Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/fs/fuse/fuse_vfsops.c
39586 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
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
341
(void)vfs_scanopt(opts, "max_read=", "%u", &max_read);
342
(void)vfs_scanopt(opts, "linux_errnos", "%d", &linux_errnos);
343
if (vfs_scanopt(opts, "timeout=", "%u", &daemon_timeout) == 1) {
344
if (daemon_timeout < FUSE_MIN_DAEMON_TIMEOUT)
345
daemon_timeout = FUSE_MIN_DAEMON_TIMEOUT;
346
else if (daemon_timeout > FUSE_MAX_DAEMON_TIMEOUT)
347
daemon_timeout = FUSE_MAX_DAEMON_TIMEOUT;
348
} else {
349
daemon_timeout = FUSE_DEFAULT_DAEMON_TIMEOUT;
350
}
351
subtype = vfs_getopts(opts, "subtype=", &err);
352
353
SDT_PROBE1(fusefs, , vfsops, mntopts, mntopts);
354
355
if (mp->mnt_flag & MNT_UPDATE) {
356
return fuse_vfs_remount(mp, td, mntopts, max_read,
357
daemon_timeout);
358
}
359
360
/* `from' contains the device name (eg. /dev/fuse0); REQUIRED */
361
fspec = vfs_getopts(opts, "from", &err);
362
if (!fspec)
363
return (EXTERROR(err, "Mount options are missing 'from'"));
364
365
/* `fd' contains the filedescriptor for this session; REQUIRED */
366
if (vfs_scanopt(opts, "fd", "%d", &fd) != 1)
367
return (EXTERROR(EINVAL, "Mount options contain an invalid value "
368
"for 'fd'"));
369
370
err = fuse_getdevice(fspec, td, &fdev);
371
if (err != 0)
372
return err;
373
374
err = fget(td, fd, &cap_read_rights, &fp);
375
if (err != 0) {
376
SDT_PROBE2(fusefs, , vfsops, trace, 1,
377
"invalid or not opened device");
378
goto out;
379
}
380
fptmp = td->td_fpop;
381
td->td_fpop = fp;
382
err = devfs_get_cdevpriv((void **)&data);
383
td->td_fpop = fptmp;
384
fdrop(fp, td);
385
FUSE_LOCK();
386
387
if (err != 0 || data == NULL) {
388
err = ENXIO;
389
SDT_PROBE4(fusefs, , vfsops, mount_err,
390
"invalid or not opened device", data, mp, err);
391
FUSE_UNLOCK();
392
goto out;
393
}
394
if (fdata_get_dead(data)) {
395
err = ENOTCONN;
396
SDT_PROBE4(fusefs, , vfsops, mount_err,
397
"device is dead during mount", data, mp, err);
398
FUSE_UNLOCK();
399
goto out;
400
}
401
/* Sanity + permission checks */
402
if (!data->daemoncred)
403
panic("fuse daemon found, but identity unknown");
404
if (mntopts & FSESS_DAEMON_CAN_SPY) {
405
err = priv_check(td, PRIV_VFS_FUSE_ALLOWOTHER);
406
EXTERROR(err, "FUSE daemon requires privileges "
407
"due to 'allow_other' option");
408
}
409
if (err == 0 && td->td_ucred->cr_uid != data->daemoncred->cr_uid) {
410
/* are we allowed to do the first mount? */
411
err = priv_check(td, PRIV_VFS_FUSE_MOUNT_NONUSER);
412
EXTERROR(err, "Mounting as a user that is different from the FUSE "
413
"daemon's requires privileges");
414
}
415
if (err) {
416
FUSE_UNLOCK();
417
goto out;
418
}
419
data->ref++;
420
data->mp = mp;
421
data->dataflags |= mntopts;
422
data->max_read = max_read;
423
data->daemon_timeout = daemon_timeout;
424
data->linux_errnos = linux_errnos;
425
data->mnt_flag = mp->mnt_flag & MNT_UPDATEMASK;
426
FUSE_UNLOCK();
427
428
vfs_getnewfsid(mp);
429
MNT_ILOCK(mp);
430
mp->mnt_data = data;
431
/*
432
* FUSE file systems can be either local or remote, but the kernel
433
* can't tell the difference.
434
*/
435
mp->mnt_flag &= ~MNT_LOCAL;
436
mp->mnt_kern_flag |= MNTK_USES_BCACHE;
437
/*
438
* Disable nullfs cacheing because it can consume too many resources in
439
* the FUSE server.
440
*/
441
mp->mnt_kern_flag |= MNTK_NULL_NOCACHE;
442
MNT_IUNLOCK(mp);
443
/* We need this here as this slot is used by getnewvnode() */
444
mp->mnt_stat.f_iosize = maxbcachebuf;
445
if (subtype) {
446
strlcat(mp->mnt_stat.f_fstypename, ".", MFSNAMELEN);
447
strlcat(mp->mnt_stat.f_fstypename, subtype, MFSNAMELEN);
448
}
449
memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN);
450
vfs_getopt(opts, "fsname=", (void**)&fsname, &fsnamelen);
451
strlcpy(mp->mnt_stat.f_mntfromname,
452
fsname == NULL ? fspec : fsname, MNAMELEN);
453
mp->mnt_iosize_max = maxphys;
454
455
/* Now handshaking with daemon */
456
fuse_internal_send_init(data, td);
457
458
out:
459
if (err) {
460
FUSE_LOCK();
461
if (data != NULL && data->mp == mp) {
462
/*
463
* Destroy device only if we acquired reference to
464
* it
465
*/
466
SDT_PROBE4(fusefs, , vfsops, mount_err,
467
"mount failed, destroy device", data, mp, err);
468
data->mp = NULL;
469
mp->mnt_data = NULL;
470
fdata_trydestroy(data);
471
}
472
FUSE_UNLOCK();
473
dev_rel(fdev);
474
}
475
return err;
476
}
477
478
static int
479
fuse_vfsop_unmount(struct mount *mp, int mntflags)
480
{
481
int err = 0;
482
int flags = 0;
483
484
struct cdev *fdev;
485
struct fuse_data *data;
486
struct fuse_dispatcher fdi;
487
struct thread *td = curthread;
488
489
if (mntflags & MNT_FORCE) {
490
flags |= FORCECLOSE;
491
}
492
data = fuse_get_mpdata(mp);
493
if (!data) {
494
panic("no private data for mount point?");
495
}
496
/* There is 1 extra root vnode reference (mp->mnt_data). */
497
FUSE_LOCK();
498
if (data->vroot != NULL) {
499
struct vnode *vroot = data->vroot;
500
501
data->vroot = NULL;
502
FUSE_UNLOCK();
503
vrele(vroot);
504
} else
505
FUSE_UNLOCK();
506
err = vflush(mp, 0, flags, td);
507
if (err) {
508
return err;
509
}
510
if (fdata_get_dead(data)) {
511
goto alreadydead;
512
}
513
if (fsess_maybe_impl(mp, FUSE_DESTROY)) {
514
fdisp_init(&fdi, 0);
515
fdisp_make(&fdi, FUSE_DESTROY, mp, 0, td, NULL);
516
517
(void)fdisp_wait_answ(&fdi);
518
fdisp_destroy(&fdi);
519
}
520
521
fdata_set_dead(data);
522
523
alreadydead:
524
FUSE_LOCK();
525
data->mp = NULL;
526
fdev = data->fdev;
527
fdata_trydestroy(data);
528
FUSE_UNLOCK();
529
530
MNT_ILOCK(mp);
531
mp->mnt_data = NULL;
532
MNT_IUNLOCK(mp);
533
534
dev_rel(fdev);
535
536
return 0;
537
}
538
539
SDT_PROBE_DEFINE1(fusefs, , vfsops, invalidate_without_export,
540
"struct mount*");
541
static int
542
fuse_vfsop_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
543
{
544
struct fuse_data *data = fuse_get_mpdata(mp);
545
uint64_t nodeid = ino;
546
struct thread *td = curthread;
547
struct fuse_dispatcher fdi;
548
struct fuse_entry_out *feo;
549
struct fuse_vnode_data *fvdat;
550
struct timespec now;
551
const char dot[] = ".";
552
__enum_uint8(vtype) vtyp;
553
int error;
554
555
if (!(data->dataflags & FSESS_EXPORT_SUPPORT)) {
556
/*
557
* Unreachable unless you do something stupid, like export a
558
* nullfs mount of a fusefs file system.
559
*/
560
SDT_PROBE1(fusefs, , vfsops, invalidate_without_export, mp);
561
return (EXTERROR(EOPNOTSUPP, "NFS-style lookups are not supported"));
562
}
563
564
error = fuse_internal_get_cached_vnode(mp, ino, flags, vpp);
565
if (error || *vpp != NULL)
566
return error;
567
568
getnanouptime(&now);
569
570
/* Do a LOOKUP, using nodeid as the parent and "." as filename */
571
fdisp_init(&fdi, sizeof(dot));
572
fdisp_make(&fdi, FUSE_LOOKUP, mp, nodeid, td, td->td_ucred);
573
memcpy(fdi.indata, dot, sizeof(dot));
574
error = fdisp_wait_answ(&fdi);
575
576
if (error)
577
goto out;
578
579
feo = (struct fuse_entry_out *)fdi.answ;
580
581
if (feo->nodeid == 0) {
582
/* zero nodeid means ENOENT and cache it */
583
error = ENOENT;
584
goto out;
585
}
586
587
if (feo->nodeid != nodeid) {
588
/*
589
* Something is very wrong with the server if "foo/." has a
590
* different inode number than "foo".
591
*/
592
static const char exterr[] = "Inconsistent LOOKUP response: "
593
"\"FILE/.\" has a different inode number than \"FILE\".";
594
fuse_warn(data, FSESS_WARN_DOT_LOOKUP, exterr);
595
error = EXTERROR(EIO, exterr);
596
goto out;
597
}
598
599
vtyp = IFTOVT(feo->attr.mode);
600
error = fuse_vnode_get(mp, feo, nodeid, NULL, vpp, NULL, vtyp);
601
if (error)
602
goto out;
603
fvdat = VTOFUD(*vpp);
604
605
if (timespeccmp(&now, &fvdat->last_local_modify, >)) {
606
/*
607
* Attributes from the server are definitely newer than the
608
* last attributes we sent to the server, so cache them.
609
*/
610
fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
611
feo->attr_valid_nsec, NULL, true);
612
}
613
fuse_validity_2_bintime(feo->entry_valid, feo->entry_valid_nsec,
614
&fvdat->entry_cache_timeout);
615
out:
616
fdisp_destroy(&fdi);
617
return error;
618
}
619
620
static int
621
fuse_vfsop_root(struct mount *mp, int lkflags, struct vnode **vpp)
622
{
623
struct fuse_data *data = fuse_get_mpdata(mp);
624
int err = 0;
625
626
if (data->vroot != NULL) {
627
err = vget(data->vroot, lkflags);
628
if (err == 0)
629
*vpp = data->vroot;
630
} else {
631
err = fuse_vnode_get(mp, NULL, FUSE_ROOT_ID, NULL, vpp, NULL,
632
VDIR);
633
if (err == 0) {
634
FUSE_LOCK();
635
MPASS(data->vroot == NULL || data->vroot == *vpp);
636
if (data->vroot == NULL) {
637
SDT_PROBE2(fusefs, , vfsops, trace, 1,
638
"new root vnode");
639
data->vroot = *vpp;
640
FUSE_UNLOCK();
641
vref(*vpp);
642
} else if (data->vroot != *vpp) {
643
SDT_PROBE2(fusefs, , vfsops, trace, 1,
644
"root vnode race");
645
FUSE_UNLOCK();
646
vput(*vpp);
647
vrecycle(*vpp);
648
*vpp = data->vroot;
649
} else
650
FUSE_UNLOCK();
651
}
652
}
653
return err;
654
}
655
656
static int
657
fuse_vfsop_statfs(struct mount *mp, struct statfs *sbp)
658
{
659
struct fuse_dispatcher fdi;
660
int err = 0;
661
662
struct fuse_statfs_out *fsfo;
663
struct fuse_data *data;
664
665
data = fuse_get_mpdata(mp);
666
667
if (!(data->dataflags & FSESS_INITED))
668
goto fake;
669
670
fdisp_init(&fdi, 0);
671
fdisp_make(&fdi, FUSE_STATFS, mp, FUSE_ROOT_ID, NULL, NULL);
672
err = fdisp_wait_answ(&fdi);
673
if (err) {
674
fdisp_destroy(&fdi);
675
if (err == ENOTCONN) {
676
/*
677
* We want to seem a legitimate fs even if the daemon
678
* is stiff dead... (so that, eg., we can still do path
679
* based unmounting after the daemon dies).
680
*/
681
goto fake;
682
}
683
return err;
684
}
685
fsfo = fdi.answ;
686
687
sbp->f_blocks = fsfo->st.blocks;
688
sbp->f_bfree = fsfo->st.bfree;
689
sbp->f_bavail = fsfo->st.bavail;
690
sbp->f_files = fsfo->st.files;
691
sbp->f_ffree = fsfo->st.ffree; /* cast from uint64_t to int64_t */
692
sbp->f_namemax = fsfo->st.namelen;
693
sbp->f_bsize = fsfo->st.frsize; /* cast from uint32_t to uint64_t */
694
695
fdisp_destroy(&fdi);
696
return 0;
697
698
fake:
699
sbp->f_blocks = 0;
700
sbp->f_bfree = 0;
701
sbp->f_bavail = 0;
702
sbp->f_files = 0;
703
sbp->f_ffree = 0;
704
sbp->f_namemax = 0;
705
sbp->f_bsize = S_BLKSIZE;
706
707
return 0;
708
}
709
710