Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/fs/fuse/fuse_vfsops.c
105879 views
1
/*-
2
* SPDX-License-Identifier: BSD-3-Clause
3
*
4
* Copyright (c) 2007-2009 Google Inc. and Amit Singh
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions are
9
* met:
10
*
11
* * Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
* * Redistributions in binary form must reproduce the above
14
* copyright notice, this list of conditions and the following disclaimer
15
* in the documentation and/or other materials provided with the
16
* distribution.
17
* * Neither the name of Google Inc. nor the names of its
18
* contributors may be used to endorse or promote products derived from
19
* this software without specific prior written permission.
20
*
21
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
*
33
* Copyright (C) 2005 Csaba Henk.
34
* All rights reserved.
35
*
36
* Copyright (c) 2019 The FreeBSD Foundation
37
*
38
* Portions of this software were developed by BFF Storage Systems, LLC under
39
* sponsorship from the FreeBSD Foundation.
40
*
41
* Redistribution and use in source and binary forms, with or without
42
* modification, are permitted provided that the following conditions
43
* are met:
44
* 1. Redistributions of source code must retain the above copyright
45
* notice, this list of conditions and the following disclaimer.
46
* 2. Redistributions in binary form must reproduce the above copyright
47
* notice, this list of conditions and the following disclaimer in the
48
* documentation and/or other materials provided with the distribution.
49
*
50
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
51
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
54
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
55
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
56
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
58
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
59
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60
* SUCH DAMAGE.
61
*/
62
63
#include <sys/param.h>
64
#include <sys/buf.h>
65
#include <sys/module.h>
66
#include <sys/systm.h>
67
#include <sys/errno.h>
68
#include <sys/kernel.h>
69
#include <sys/capsicum.h>
70
#include <sys/conf.h>
71
#include <sys/filedesc.h>
72
#include <sys/uio.h>
73
#include <sys/malloc.h>
74
#include <sys/queue.h>
75
#include <sys/lock.h>
76
#include <sys/sx.h>
77
#include <sys/mutex.h>
78
#include <sys/proc.h>
79
#include <sys/vnode.h>
80
#include <sys/namei.h>
81
#include <sys/mount.h>
82
#include <sys/sysctl.h>
83
#include <sys/fcntl.h>
84
#define EXTERR_CATEGORY EXTERR_CAT_FUSE_VFS
85
#include <sys/exterrvar.h>
86
87
#include "fuse.h"
88
#include "fuse_node.h"
89
#include "fuse_ipc.h"
90
#include "fuse_internal.h"
91
92
#include <sys/priv.h>
93
#include <security/mac/mac_framework.h>
94
95
SDT_PROVIDER_DECLARE(fusefs);
96
/*
97
* Fuse trace probe:
98
* arg0: verbosity. Higher numbers give more verbose messages
99
* arg1: Textual message
100
*/
101
SDT_PROBE_DEFINE2(fusefs, , vfsops, trace, "int", "char*");
102
103
/* This will do for privilege types for now */
104
#ifndef PRIV_VFS_FUSE_ALLOWOTHER
105
#define PRIV_VFS_FUSE_ALLOWOTHER PRIV_VFS_MOUNT_NONUSER
106
#endif
107
#ifndef PRIV_VFS_FUSE_MOUNT_NONUSER
108
#define PRIV_VFS_FUSE_MOUNT_NONUSER PRIV_VFS_MOUNT_NONUSER
109
#endif
110
#ifndef PRIV_VFS_FUSE_SYNC_UNMOUNT
111
#define PRIV_VFS_FUSE_SYNC_UNMOUNT PRIV_VFS_MOUNT_NONUSER
112
#endif
113
114
static vfs_fhtovp_t fuse_vfsop_fhtovp;
115
static vfs_mount_t fuse_vfsop_mount;
116
static vfs_unmount_t fuse_vfsop_unmount;
117
static vfs_root_t fuse_vfsop_root;
118
static vfs_statfs_t fuse_vfsop_statfs;
119
static vfs_vget_t fuse_vfsop_vget;
120
121
struct vfsops fuse_vfsops = {
122
.vfs_fhtovp = fuse_vfsop_fhtovp,
123
.vfs_mount = fuse_vfsop_mount,
124
.vfs_unmount = fuse_vfsop_unmount,
125
.vfs_root = fuse_vfsop_root,
126
.vfs_statfs = fuse_vfsop_statfs,
127
.vfs_vget = fuse_vfsop_vget,
128
};
129
130
static int fuse_enforce_dev_perms = 0;
131
132
SYSCTL_INT(_vfs_fusefs, OID_AUTO, enforce_dev_perms, CTLFLAG_RW,
133
&fuse_enforce_dev_perms, 0,
134
"enforce fuse device permissions for secondary mounts");
135
136
MALLOC_DEFINE(M_FUSEVFS, "fuse_filesystem", "buffer for fuse vfs layer");
137
138
static int
139
fuse_getdevice(const char *fspec, struct thread *td, struct cdev **fdevp)
140
{
141
struct nameidata nd, *ndp = &nd;
142
struct vnode *devvp;
143
struct cdev *fdev;
144
int err;
145
146
/*
147
* Not an update, or updating the name: look up the name
148
* and verify that it refers to a sensible disk device.
149
*/
150
151
NDINIT(ndp, LOOKUP, FOLLOW, UIO_SYSSPACE, fspec);
152
if ((err = namei(ndp)) != 0)
153
return err;
154
NDFREE_PNBUF(ndp);
155
devvp = ndp->ni_vp;
156
157
if (devvp->v_type != VCHR) {
158
vrele(devvp);
159
return ENXIO;
160
}
161
fdev = devvp->v_rdev;
162
dev_ref(fdev);
163
164
if (fuse_enforce_dev_perms) {
165
/*
166
* Check if mounter can open the fuse device.
167
*
168
* This has significance only if we are doing a secondary mount
169
* which doesn't involve actually opening fuse devices, but we
170
* still want to enforce the permissions of the device (in
171
* order to keep control over the circle of fuse users).
172
*
173
* (In case of primary mounts, we are either the superuser so
174
* we can do anything anyway, or we can mount only if the
175
* device is already opened by us, ie. we are permitted to open
176
* the device.)
177
*/
178
#if 0
179
#ifdef MAC
180
err = mac_check_vnode_open(td->td_ucred, devvp, VREAD | VWRITE);
181
if (!err)
182
#endif
183
#endif /* 0 */
184
err = VOP_ACCESS(devvp, VREAD | VWRITE, td->td_ucred, td);
185
if (err) {
186
vrele(devvp);
187
dev_rel(fdev);
188
return err;
189
}
190
}
191
/*
192
* according to coda code, no extra lock is needed --
193
* although in sys/vnode.h this field is marked "v"
194
*/
195
vrele(devvp);
196
197
if (!fdev->si_devsw ||
198
strcmp("fuse", fdev->si_devsw->d_name)) {
199
dev_rel(fdev);
200
return ENXIO;
201
}
202
*fdevp = fdev;
203
204
return 0;
205
}
206
207
#define FUSE_FLAGOPT(fnam, fval) do { \
208
vfs_flagopt(opts, #fnam, &mntopts, fval); \
209
vfs_flagopt(opts, "__" #fnam, &__mntopts, fval); \
210
} while (0)
211
212
SDT_PROBE_DEFINE1(fusefs, , vfsops, mntopts, "uint64_t");
213
SDT_PROBE_DEFINE4(fusefs, , vfsops, mount_err, "char*", "struct fuse_data*",
214
"struct mount*", "int");
215
216
static int
217
fuse_vfs_remount(struct mount *mp, struct thread *td, uint64_t mntopts,
218
uint32_t max_read, int daemon_timeout)
219
{
220
int err = 0;
221
struct fuse_data *data = fuse_get_mpdata(mp);
222
/* Don't allow these options to be changed */
223
const static unsigned long long cant_update_opts =
224
MNT_USER; /* Mount owner must be the user running the daemon */
225
226
FUSE_LOCK();
227
228
if ((mp->mnt_flag ^ data->mnt_flag) & cant_update_opts) {
229
err = EOPNOTSUPP;
230
SDT_PROBE4(fusefs, , vfsops, mount_err,
231
"Can't change these mount options during remount",
232
data, mp, err);
233
goto out;
234
}
235
if (((data->dataflags ^ mntopts) & FSESS_MNTOPTS_MASK) ||
236
(data->max_read != max_read) ||
237
(data->daemon_timeout != daemon_timeout)) {
238
// TODO: allow changing options where it makes sense
239
err = EOPNOTSUPP;
240
SDT_PROBE4(fusefs, , vfsops, mount_err,
241
"Can't change fuse mount options during remount",
242
data, mp, err);
243
goto out;
244
}
245
246
if (fdata_get_dead(data)) {
247
err = ENOTCONN;
248
SDT_PROBE4(fusefs, , vfsops, mount_err,
249
"device is dead during mount", data, mp, err);
250
goto out;
251
}
252
253
/* Sanity + permission checks */
254
if (!data->daemoncred)
255
panic("fuse daemon found, but identity unknown");
256
if (mntopts & FSESS_DAEMON_CAN_SPY)
257
err = priv_check(td, PRIV_VFS_FUSE_ALLOWOTHER);
258
if (err == 0 && td->td_ucred->cr_uid != data->daemoncred->cr_uid)
259
/* are we allowed to do the first mount? */
260
err = priv_check(td, PRIV_VFS_FUSE_MOUNT_NONUSER);
261
262
out:
263
FUSE_UNLOCK();
264
return err;
265
}
266
267
static int
268
fuse_vfsop_fhtovp(struct mount *mp, struct fid *fhp, int flags,
269
struct vnode **vpp)
270
{
271
struct fuse_fid *ffhp = (struct fuse_fid *)fhp;
272
struct fuse_vnode_data *fvdat;
273
struct vnode *nvp;
274
int error;
275
276
if (!(fuse_get_mpdata(mp)->dataflags & FSESS_EXPORT_SUPPORT))
277
return (EXTERROR(EOPNOTSUPP, "NFS-style lookups are not supported"));
278
279
error = VFS_VGET(mp, ffhp->nid, LK_EXCLUSIVE, &nvp);
280
if (error) {
281
*vpp = NULL;
282
return (error);
283
}
284
fvdat = VTOFUD(nvp);
285
if (fvdat->generation != ffhp->gen ) {
286
vput(nvp);
287
*vpp = NULL;
288
return (ESTALE);
289
}
290
*vpp = nvp;
291
vnode_create_vobject(*vpp, VNODE_NO_SIZE, curthread);
292
return (0);
293
}
294
295
static int
296
fuse_vfsop_mount(struct mount *mp)
297
{
298
int err;
299
300
uint64_t mntopts, __mntopts;
301
uint32_t max_read;
302
int linux_errnos;
303
int daemon_timeout;
304
int fd;
305
306
struct cdev *fdev;
307
struct fuse_data *data = NULL;
308
struct thread *td;
309
struct file *fp, *fptmp;
310
char *fspec, *subtype, *fsname = NULL;
311
int fsnamelen;
312
struct vfsoptlist *opts;
313
314
subtype = NULL;
315
max_read = ~0;
316
linux_errnos = 0;
317
err = 0;
318
mntopts = 0;
319
__mntopts = 0;
320
td = curthread;
321
322
/* Get the new options passed to mount */
323
opts = mp->mnt_optnew;
324
325
if (!opts)
326
return (EXTERROR(EINVAL, "Mount options were not supplied"));
327
328
/* `fspath' contains the mount point (eg. /mnt/fuse/sshfs); REQUIRED */
329
if (!vfs_getopts(opts, "fspath", &err))
330
return (EXTERROR(err, "Mount options are missing 'fspath'"));
331
332
/*
333
* With the help of underscored options the mount program
334
* can inform us from the flags it sets by default
335
*/
336
FUSE_FLAGOPT(allow_other, FSESS_DAEMON_CAN_SPY);
337
FUSE_FLAGOPT(push_symlinks_in, FSESS_PUSH_SYMLINKS_IN);
338
FUSE_FLAGOPT(default_permissions, FSESS_DEFAULT_PERMISSIONS);
339
FUSE_FLAGOPT(intr, FSESS_INTR);
340
FUSE_FLAGOPT(auto_unmount, FSESS_AUTO_UNMOUNT);
341
342
(void)vfs_scanopt(opts, "max_read=", "%u", &max_read);
343
(void)vfs_scanopt(opts, "linux_errnos", "%d", &linux_errnos);
344
if (vfs_scanopt(opts, "timeout=", "%u", &daemon_timeout) == 1) {
345
if (daemon_timeout < FUSE_MIN_DAEMON_TIMEOUT)
346
daemon_timeout = FUSE_MIN_DAEMON_TIMEOUT;
347
else if (daemon_timeout > FUSE_MAX_DAEMON_TIMEOUT)
348
daemon_timeout = FUSE_MAX_DAEMON_TIMEOUT;
349
} else {
350
daemon_timeout = FUSE_DEFAULT_DAEMON_TIMEOUT;
351
}
352
subtype = vfs_getopts(opts, "subtype=", &err);
353
354
SDT_PROBE1(fusefs, , vfsops, mntopts, mntopts);
355
356
if (mp->mnt_flag & MNT_UPDATE) {
357
return fuse_vfs_remount(mp, td, mntopts, max_read,
358
daemon_timeout);
359
}
360
361
/* `from' contains the device name (eg. /dev/fuse0); REQUIRED */
362
fspec = vfs_getopts(opts, "from", &err);
363
if (!fspec)
364
return (EXTERROR(err, "Mount options are missing 'from'"));
365
366
/* `fd' contains the filedescriptor for this session; REQUIRED */
367
if (vfs_scanopt(opts, "fd", "%d", &fd) != 1)
368
return (EXTERROR(EINVAL, "Mount options contain an invalid value "
369
"for 'fd'"));
370
371
err = fuse_getdevice(fspec, td, &fdev);
372
if (err != 0)
373
return err;
374
375
err = fget(td, fd, &cap_read_rights, &fp);
376
if (err != 0) {
377
SDT_PROBE2(fusefs, , vfsops, trace, 1,
378
"invalid or not opened device");
379
goto out;
380
}
381
fptmp = td->td_fpop;
382
td->td_fpop = fp;
383
err = devfs_get_cdevpriv((void **)&data);
384
td->td_fpop = fptmp;
385
fdrop(fp, td);
386
FUSE_LOCK();
387
388
if (err != 0 || data == NULL) {
389
err = ENXIO;
390
SDT_PROBE4(fusefs, , vfsops, mount_err,
391
"invalid or not opened device", data, mp, err);
392
FUSE_UNLOCK();
393
goto out;
394
}
395
if (fdata_get_dead(data)) {
396
err = ENOTCONN;
397
SDT_PROBE4(fusefs, , vfsops, mount_err,
398
"device is dead during mount", data, mp, err);
399
FUSE_UNLOCK();
400
goto out;
401
}
402
/* Sanity + permission checks */
403
if (!data->daemoncred)
404
panic("fuse daemon found, but identity unknown");
405
if (mntopts & FSESS_DAEMON_CAN_SPY) {
406
err = priv_check(td, PRIV_VFS_FUSE_ALLOWOTHER);
407
EXTERROR(err, "FUSE daemon requires privileges "
408
"due to 'allow_other' option");
409
}
410
if (err == 0 && td->td_ucred->cr_uid != data->daemoncred->cr_uid) {
411
/* are we allowed to do the first mount? */
412
err = priv_check(td, PRIV_VFS_FUSE_MOUNT_NONUSER);
413
EXTERROR(err, "Mounting as a user that is different from the FUSE "
414
"daemon's requires privileges");
415
}
416
if (err) {
417
FUSE_UNLOCK();
418
goto out;
419
}
420
data->ref++;
421
data->mp = mp;
422
data->dataflags |= mntopts;
423
data->max_read = max_read;
424
data->daemon_timeout = daemon_timeout;
425
data->linux_errnos = linux_errnos;
426
data->mnt_flag = mp->mnt_flag & MNT_UPDATEMASK;
427
FUSE_UNLOCK();
428
429
vfs_getnewfsid(mp);
430
MNT_ILOCK(mp);
431
mp->mnt_data = data;
432
/*
433
* FUSE file systems can be either local or remote, but the kernel
434
* can't tell the difference.
435
*/
436
mp->mnt_flag &= ~MNT_LOCAL;
437
mp->mnt_kern_flag |= MNTK_USES_BCACHE;
438
/*
439
* Disable nullfs cacheing because it can consume too many resources in
440
* the FUSE server.
441
*/
442
mp->mnt_kern_flag |= MNTK_NULL_NOCACHE;
443
MNT_IUNLOCK(mp);
444
/* We need this here as this slot is used by getnewvnode() */
445
mp->mnt_stat.f_iosize = maxbcachebuf;
446
if (subtype) {
447
strlcat(mp->mnt_stat.f_fstypename, ".", MFSNAMELEN);
448
strlcat(mp->mnt_stat.f_fstypename, subtype, MFSNAMELEN);
449
}
450
memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN);
451
vfs_getopt(opts, "fsname=", (void**)&fsname, &fsnamelen);
452
strlcpy(mp->mnt_stat.f_mntfromname,
453
fsname == NULL ? fspec : fsname, MNAMELEN);
454
mp->mnt_iosize_max = maxphys;
455
456
/* Now handshaking with daemon */
457
fuse_internal_send_init(data, td);
458
459
out:
460
if (err) {
461
FUSE_LOCK();
462
if (data != NULL && data->mp == mp) {
463
/*
464
* Destroy device only if we acquired reference to
465
* it
466
*/
467
SDT_PROBE4(fusefs, , vfsops, mount_err,
468
"mount failed, destroy device", data, mp, err);
469
data->mp = NULL;
470
mp->mnt_data = NULL;
471
fdata_trydestroy(data);
472
}
473
FUSE_UNLOCK();
474
dev_rel(fdev);
475
}
476
return err;
477
}
478
479
static int
480
fuse_vfsop_unmount(struct mount *mp, int mntflags)
481
{
482
int err = 0;
483
int flags = 0;
484
485
struct cdev *fdev;
486
struct fuse_data *data;
487
struct fuse_dispatcher fdi;
488
struct thread *td = curthread;
489
490
if (mntflags & MNT_FORCE) {
491
flags |= FORCECLOSE;
492
}
493
data = fuse_get_mpdata(mp);
494
if (!data) {
495
panic("no private data for mount point?");
496
}
497
/* There is 1 extra root vnode reference (mp->mnt_data). */
498
FUSE_LOCK();
499
if (data->vroot != NULL) {
500
struct vnode *vroot = data->vroot;
501
502
data->vroot = NULL;
503
FUSE_UNLOCK();
504
vrele(vroot);
505
} else
506
FUSE_UNLOCK();
507
err = vflush(mp, 0, flags, td);
508
if (err) {
509
return err;
510
}
511
if (fdata_get_dead(data)) {
512
goto alreadydead;
513
}
514
if (fsess_maybe_impl(mp, FUSE_DESTROY)) {
515
fdisp_init(&fdi, 0);
516
fdisp_make(&fdi, FUSE_DESTROY, mp, 0, td, NULL);
517
518
(void)fdisp_wait_answ(&fdi);
519
fdisp_destroy(&fdi);
520
}
521
522
fdata_set_dead(data);
523
524
alreadydead:
525
FUSE_LOCK();
526
data->mp = NULL;
527
fdev = data->fdev;
528
fdata_trydestroy(data);
529
FUSE_UNLOCK();
530
531
MNT_ILOCK(mp);
532
mp->mnt_data = NULL;
533
MNT_IUNLOCK(mp);
534
535
dev_rel(fdev);
536
537
return 0;
538
}
539
540
SDT_PROBE_DEFINE1(fusefs, , vfsops, invalidate_without_export,
541
"struct mount*");
542
static int
543
fuse_vfsop_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
544
{
545
struct fuse_data *data = fuse_get_mpdata(mp);
546
uint64_t nodeid = ino;
547
struct thread *td = curthread;
548
struct fuse_dispatcher fdi;
549
struct fuse_entry_out *feo;
550
struct fuse_vnode_data *fvdat;
551
struct timespec now;
552
const char dot[] = ".";
553
__enum_uint8(vtype) vtyp;
554
int error;
555
556
if (!(data->dataflags & FSESS_EXPORT_SUPPORT)) {
557
/*
558
* Unreachable unless you do something stupid, like export a
559
* nullfs mount of a fusefs file system.
560
*/
561
SDT_PROBE1(fusefs, , vfsops, invalidate_without_export, mp);
562
return (EXTERROR(EOPNOTSUPP, "NFS-style lookups are not supported"));
563
}
564
565
error = fuse_internal_get_cached_vnode(mp, ino, flags, vpp);
566
if (error || *vpp != NULL)
567
return error;
568
569
getnanouptime(&now);
570
571
/* Do a LOOKUP, using nodeid as the parent and "." as filename */
572
fdisp_init(&fdi, sizeof(dot));
573
fdisp_make(&fdi, FUSE_LOOKUP, mp, nodeid, td, td->td_ucred);
574
memcpy(fdi.indata, dot, sizeof(dot));
575
error = fdisp_wait_answ(&fdi);
576
577
if (error)
578
goto out;
579
580
feo = (struct fuse_entry_out *)fdi.answ;
581
582
if (feo->nodeid == 0) {
583
/* zero nodeid means ENOENT and cache it */
584
error = ENOENT;
585
goto out;
586
}
587
588
if (feo->nodeid != nodeid) {
589
/*
590
* Something is very wrong with the server if "foo/." has a
591
* different inode number than "foo".
592
*/
593
static const char exterr[] = "Inconsistent LOOKUP response: "
594
"\"FILE/.\" has a different inode number than \"FILE\".";
595
fuse_warn(data, FSESS_WARN_DOT_LOOKUP, exterr);
596
error = EXTERROR(EIO, exterr);
597
goto out;
598
}
599
600
vtyp = IFTOVT(feo->attr.mode);
601
error = fuse_vnode_get(mp, feo, nodeid, NULL, vpp, NULL, vtyp);
602
if (error)
603
goto out;
604
fvdat = VTOFUD(*vpp);
605
606
if (timespeccmp(&now, &fvdat->last_local_modify, >)) {
607
/*
608
* Attributes from the server are definitely newer than the
609
* last attributes we sent to the server, so cache them.
610
*/
611
fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
612
feo->attr_valid_nsec, NULL, true);
613
}
614
fuse_validity_2_bintime(feo->entry_valid, feo->entry_valid_nsec,
615
&fvdat->entry_cache_timeout);
616
out:
617
fdisp_destroy(&fdi);
618
return error;
619
}
620
621
static int
622
fuse_vfsop_root(struct mount *mp, int lkflags, struct vnode **vpp)
623
{
624
struct fuse_data *data = fuse_get_mpdata(mp);
625
int err = 0;
626
627
if (data->vroot != NULL) {
628
err = vget(data->vroot, lkflags);
629
if (err == 0)
630
*vpp = data->vroot;
631
} else {
632
err = fuse_vnode_get(mp, NULL, FUSE_ROOT_ID, NULL, vpp, NULL,
633
VDIR);
634
if (err == 0) {
635
FUSE_LOCK();
636
MPASS(data->vroot == NULL || data->vroot == *vpp);
637
if (data->vroot == NULL) {
638
SDT_PROBE2(fusefs, , vfsops, trace, 1,
639
"new root vnode");
640
data->vroot = *vpp;
641
FUSE_UNLOCK();
642
vref(*vpp);
643
} else if (data->vroot != *vpp) {
644
SDT_PROBE2(fusefs, , vfsops, trace, 1,
645
"root vnode race");
646
FUSE_UNLOCK();
647
vput(*vpp);
648
vrecycle(*vpp);
649
*vpp = data->vroot;
650
} else
651
FUSE_UNLOCK();
652
}
653
}
654
return err;
655
}
656
657
static int
658
fuse_vfsop_statfs(struct mount *mp, struct statfs *sbp)
659
{
660
struct fuse_dispatcher fdi;
661
int err = 0;
662
663
struct fuse_statfs_out *fsfo;
664
struct fuse_data *data;
665
666
data = fuse_get_mpdata(mp);
667
668
if (!(data->dataflags & FSESS_INITED))
669
goto fake;
670
671
fdisp_init(&fdi, 0);
672
fdisp_make(&fdi, FUSE_STATFS, mp, FUSE_ROOT_ID, NULL, NULL);
673
err = fdisp_wait_answ(&fdi);
674
if (err) {
675
fdisp_destroy(&fdi);
676
if (err == ENOTCONN) {
677
/*
678
* We want to seem a legitimate fs even if the daemon
679
* is stiff dead... (so that, eg., we can still do path
680
* based unmounting after the daemon dies).
681
*/
682
goto fake;
683
}
684
return err;
685
}
686
fsfo = fdi.answ;
687
688
sbp->f_blocks = fsfo->st.blocks;
689
sbp->f_bfree = fsfo->st.bfree;
690
sbp->f_bavail = fsfo->st.bavail;
691
sbp->f_files = fsfo->st.files;
692
sbp->f_ffree = fsfo->st.ffree; /* cast from uint64_t to int64_t */
693
sbp->f_namemax = fsfo->st.namelen;
694
sbp->f_bsize = fsfo->st.frsize; /* cast from uint32_t to uint64_t */
695
696
fdisp_destroy(&fdi);
697
return 0;
698
699
fake:
700
sbp->f_blocks = 0;
701
sbp->f_bfree = 0;
702
sbp->f_bavail = 0;
703
sbp->f_files = 0;
704
sbp->f_ffree = 0;
705
sbp->f_namemax = 0;
706
sbp->f_bsize = S_BLKSIZE;
707
708
return 0;
709
}
710
711