Changeset View
Changeset View
Standalone View
Standalone View
sys/fs/fuse/fuse_vfsops.c
Show All 27 Lines | |||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
* | * | ||||
* Copyright (C) 2005 Csaba Henk. | * Copyright (C) 2005 Csaba Henk. | ||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* Copyright (c) 2019 The FreeBSD Foundation | |||||
* | |||||
* Portions of this software were developed by BFF Storage Systems, LLC under | |||||
* sponsorship from the FreeBSD Foundation. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||||
* notice, this list of conditions and the following disclaimer in the | * notice, this list of conditions and the following disclaimer in the | ||||
* documentation and/or other materials provided with the distribution. | * documentation and/or other materials provided with the distribution. | ||||
Show All 32 Lines | |||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/vnode.h> | #include <sys/vnode.h> | ||||
#include <sys/namei.h> | #include <sys/namei.h> | ||||
#include <sys/mount.h> | #include <sys/mount.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/fcntl.h> | #include <sys/fcntl.h> | ||||
#include "fuse.h" | #include "fuse.h" | ||||
#include "fuse_param.h" | |||||
#include "fuse_node.h" | #include "fuse_node.h" | ||||
#include "fuse_ipc.h" | #include "fuse_ipc.h" | ||||
#include "fuse_internal.h" | #include "fuse_internal.h" | ||||
#include <sys/priv.h> | #include <sys/priv.h> | ||||
#include <security/mac/mac_framework.h> | #include <security/mac/mac_framework.h> | ||||
SDT_PROVIDER_DECLARE(fuse); | SDT_PROVIDER_DECLARE(fusefs); | ||||
/* | /* | ||||
* Fuse trace probe: | * Fuse trace probe: | ||||
* arg0: verbosity. Higher numbers give more verbose messages | * arg0: verbosity. Higher numbers give more verbose messages | ||||
* arg1: Textual message | * arg1: Textual message | ||||
*/ | */ | ||||
SDT_PROBE_DEFINE2(fuse, , vfsops, trace, "int", "char*"); | SDT_PROBE_DEFINE2(fusefs, , vfsops, trace, "int", "char*"); | ||||
/* This will do for privilege types for now */ | /* This will do for privilege types for now */ | ||||
#ifndef PRIV_VFS_FUSE_ALLOWOTHER | #ifndef PRIV_VFS_FUSE_ALLOWOTHER | ||||
#define PRIV_VFS_FUSE_ALLOWOTHER PRIV_VFS_MOUNT_NONUSER | #define PRIV_VFS_FUSE_ALLOWOTHER PRIV_VFS_MOUNT_NONUSER | ||||
#endif | #endif | ||||
#ifndef PRIV_VFS_FUSE_MOUNT_NONUSER | #ifndef PRIV_VFS_FUSE_MOUNT_NONUSER | ||||
#define PRIV_VFS_FUSE_MOUNT_NONUSER PRIV_VFS_MOUNT_NONUSER | #define PRIV_VFS_FUSE_MOUNT_NONUSER PRIV_VFS_MOUNT_NONUSER | ||||
#endif | #endif | ||||
#ifndef PRIV_VFS_FUSE_SYNC_UNMOUNT | #ifndef PRIV_VFS_FUSE_SYNC_UNMOUNT | ||||
#define PRIV_VFS_FUSE_SYNC_UNMOUNT PRIV_VFS_MOUNT_NONUSER | #define PRIV_VFS_FUSE_SYNC_UNMOUNT PRIV_VFS_MOUNT_NONUSER | ||||
#endif | #endif | ||||
static vfs_fhtovp_t fuse_vfsop_fhtovp; | |||||
static vfs_mount_t fuse_vfsop_mount; | static vfs_mount_t fuse_vfsop_mount; | ||||
static vfs_unmount_t fuse_vfsop_unmount; | static vfs_unmount_t fuse_vfsop_unmount; | ||||
static vfs_root_t fuse_vfsop_root; | static vfs_root_t fuse_vfsop_root; | ||||
static vfs_statfs_t fuse_vfsop_statfs; | static vfs_statfs_t fuse_vfsop_statfs; | ||||
static vfs_vget_t fuse_vfsop_vget; | |||||
struct vfsops fuse_vfsops = { | struct vfsops fuse_vfsops = { | ||||
.vfs_fhtovp = fuse_vfsop_fhtovp, | |||||
.vfs_mount = fuse_vfsop_mount, | .vfs_mount = fuse_vfsop_mount, | ||||
.vfs_unmount = fuse_vfsop_unmount, | .vfs_unmount = fuse_vfsop_unmount, | ||||
.vfs_root = fuse_vfsop_root, | .vfs_root = fuse_vfsop_root, | ||||
.vfs_statfs = fuse_vfsop_statfs, | .vfs_statfs = fuse_vfsop_statfs, | ||||
.vfs_vget = fuse_vfsop_vget, | |||||
}; | }; | ||||
SYSCTL_INT(_vfs_fusefs, OID_AUTO, init_backgrounded, CTLFLAG_RD, | |||||
SYSCTL_NULL_INT_PTR, 1, "indicate async handshake"); | |||||
static int fuse_enforce_dev_perms = 0; | static int fuse_enforce_dev_perms = 0; | ||||
SYSCTL_INT(_vfs_fusefs, OID_AUTO, enforce_dev_perms, CTLFLAG_RW, | SYSCTL_INT(_vfs_fusefs, OID_AUTO, enforce_dev_perms, CTLFLAG_RW, | ||||
&fuse_enforce_dev_perms, 0, | &fuse_enforce_dev_perms, 0, | ||||
"enforce fuse device permissions for secondary mounts"); | "enforce fuse device permissions for secondary mounts"); | ||||
static unsigned sync_unmount = 1; | |||||
SYSCTL_UINT(_vfs_fusefs, OID_AUTO, sync_unmount, CTLFLAG_RW, | |||||
&sync_unmount, 0, "specify when to use synchronous unmount"); | |||||
MALLOC_DEFINE(M_FUSEVFS, "fuse_filesystem", "buffer for fuse vfs layer"); | MALLOC_DEFINE(M_FUSEVFS, "fuse_filesystem", "buffer for fuse vfs layer"); | ||||
static int | static int | ||||
fuse_getdevice(const char *fspec, struct thread *td, struct cdev **fdevp) | fuse_getdevice(const char *fspec, struct thread *td, struct cdev **fdevp) | ||||
{ | { | ||||
struct nameidata nd, *ndp = &nd; | struct nameidata nd, *ndp = &nd; | ||||
struct vnode *devvp; | struct vnode *devvp; | ||||
struct cdev *fdev; | struct cdev *fdev; | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | #endif /* 0 */ | ||||
return 0; | return 0; | ||||
} | } | ||||
#define FUSE_FLAGOPT(fnam, fval) do { \ | #define FUSE_FLAGOPT(fnam, fval) do { \ | ||||
vfs_flagopt(opts, #fnam, &mntopts, fval); \ | vfs_flagopt(opts, #fnam, &mntopts, fval); \ | ||||
vfs_flagopt(opts, "__" #fnam, &__mntopts, fval); \ | vfs_flagopt(opts, "__" #fnam, &__mntopts, fval); \ | ||||
} while (0) | } while (0) | ||||
SDT_PROBE_DEFINE1(fuse, , vfsops, mntopts, "uint64_t"); | SDT_PROBE_DEFINE1(fusefs, , vfsops, mntopts, "uint64_t"); | ||||
SDT_PROBE_DEFINE4(fuse, , vfsops, mount_err, "char*", "struct fuse_data*", | SDT_PROBE_DEFINE4(fusefs, , vfsops, mount_err, "char*", "struct fuse_data*", | ||||
"struct mount*", "int"); | "struct mount*", "int"); | ||||
static int | static int | ||||
fuse_vfs_remount(struct mount *mp, struct thread *td, uint64_t mntopts, | |||||
uint32_t max_read, int daemon_timeout) | |||||
{ | |||||
int err = 0; | |||||
struct fuse_data *data = fuse_get_mpdata(mp); | |||||
/* Don't allow these options to be changed */ | |||||
const static unsigned long long cant_update_opts = | |||||
MNT_USER; /* Mount owner must be the user running the daemon */ | |||||
FUSE_LOCK(); | |||||
if ((mp->mnt_flag ^ data->mnt_flag) & cant_update_opts) { | |||||
err = EOPNOTSUPP; | |||||
SDT_PROBE4(fusefs, , vfsops, mount_err, | |||||
"Can't change these mount options during remount", | |||||
data, mp, err); | |||||
goto out; | |||||
} | |||||
if (((data->dataflags ^ mntopts) & FSESS_MNTOPTS_MASK) || | |||||
(data->max_read != max_read) || | |||||
(data->daemon_timeout != daemon_timeout)) { | |||||
// TODO: allow changing options where it makes sense | |||||
err = EOPNOTSUPP; | |||||
SDT_PROBE4(fusefs, , vfsops, mount_err, | |||||
"Can't change fuse mount options during remount", | |||||
data, mp, err); | |||||
goto out; | |||||
} | |||||
if (fdata_get_dead(data)) { | |||||
err = ENOTCONN; | |||||
SDT_PROBE4(fusefs, , vfsops, mount_err, | |||||
"device is dead during mount", data, mp, err); | |||||
goto out; | |||||
} | |||||
/* Sanity + permission checks */ | |||||
if (!data->daemoncred) | |||||
panic("fuse daemon found, but identity unknown"); | |||||
if (mntopts & FSESS_DAEMON_CAN_SPY) | |||||
err = priv_check(td, PRIV_VFS_FUSE_ALLOWOTHER); | |||||
if (err == 0 && td->td_ucred->cr_uid != data->daemoncred->cr_uid) | |||||
/* are we allowed to do the first mount? */ | |||||
err = priv_check(td, PRIV_VFS_FUSE_MOUNT_NONUSER); | |||||
out: | |||||
FUSE_UNLOCK(); | |||||
return err; | |||||
} | |||||
static int | |||||
fuse_vfsop_fhtovp(struct mount *mp, struct fid *fhp, int flags, | |||||
struct vnode **vpp) | |||||
{ | |||||
struct fuse_fid *ffhp = (struct fuse_fid *)fhp; | |||||
struct fuse_vnode_data *fvdat; | |||||
struct vnode *nvp; | |||||
int error; | |||||
if (!(fuse_get_mpdata(mp)->dataflags & FSESS_EXPORT_SUPPORT)) | |||||
return EOPNOTSUPP; | |||||
error = VFS_VGET(mp, ffhp->nid, LK_EXCLUSIVE, &nvp); | |||||
if (error) { | |||||
*vpp = NULLVP; | |||||
return (error); | |||||
} | |||||
fvdat = VTOFUD(nvp); | |||||
if (fvdat->generation != ffhp->gen ) { | |||||
vput(nvp); | |||||
*vpp = NULLVP; | |||||
return (ESTALE); | |||||
} | |||||
*vpp = nvp; | |||||
vnode_create_vobject(*vpp, 0, curthread); | |||||
return (0); | |||||
} | |||||
static int | |||||
fuse_vfsop_mount(struct mount *mp) | fuse_vfsop_mount(struct mount *mp) | ||||
{ | { | ||||
int err; | int err; | ||||
uint64_t mntopts, __mntopts; | uint64_t mntopts, __mntopts; | ||||
uint32_t max_read; | uint32_t max_read; | ||||
int daemon_timeout; | int daemon_timeout; | ||||
int fd; | int fd; | ||||
Show All 9 Lines | fuse_vfsop_mount(struct mount *mp) | ||||
subtype = NULL; | subtype = NULL; | ||||
max_read = ~0; | max_read = ~0; | ||||
err = 0; | err = 0; | ||||
mntopts = 0; | mntopts = 0; | ||||
__mntopts = 0; | __mntopts = 0; | ||||
td = curthread; | td = curthread; | ||||
if (mp->mnt_flag & MNT_UPDATE) | |||||
return EOPNOTSUPP; | |||||
MNT_ILOCK(mp); | |||||
mp->mnt_flag |= MNT_SYNCHRONOUS; | |||||
mp->mnt_data = NULL; | |||||
MNT_IUNLOCK(mp); | |||||
/* Get the new options passed to mount */ | /* Get the new options passed to mount */ | ||||
opts = mp->mnt_optnew; | opts = mp->mnt_optnew; | ||||
if (!opts) | if (!opts) | ||||
return EINVAL; | return EINVAL; | ||||
/* `fspath' contains the mount point (eg. /mnt/fuse/sshfs); REQUIRED */ | /* `fspath' contains the mount point (eg. /mnt/fuse/sshfs); REQUIRED */ | ||||
if (!vfs_getopts(opts, "fspath", &err)) | if (!vfs_getopts(opts, "fspath", &err)) | ||||
return err; | return err; | ||||
/* `from' contains the device name (eg. /dev/fuse0); REQUIRED */ | |||||
fspec = vfs_getopts(opts, "from", &err); | |||||
if (!fspec) | |||||
return err; | |||||
/* `fd' contains the filedescriptor for this session; REQUIRED */ | |||||
if (vfs_scanopt(opts, "fd", "%d", &fd) != 1) | |||||
return EINVAL; | |||||
err = fuse_getdevice(fspec, td, &fdev); | |||||
if (err != 0) | |||||
return err; | |||||
/* | /* | ||||
* With the help of underscored options the mount program | * With the help of underscored options the mount program | ||||
* can inform us from the flags it sets by default | * can inform us from the flags it sets by default | ||||
*/ | */ | ||||
FUSE_FLAGOPT(allow_other, FSESS_DAEMON_CAN_SPY); | FUSE_FLAGOPT(allow_other, FSESS_DAEMON_CAN_SPY); | ||||
FUSE_FLAGOPT(push_symlinks_in, FSESS_PUSH_SYMLINKS_IN); | FUSE_FLAGOPT(push_symlinks_in, FSESS_PUSH_SYMLINKS_IN); | ||||
FUSE_FLAGOPT(default_permissions, FSESS_DEFAULT_PERMISSIONS); | FUSE_FLAGOPT(default_permissions, FSESS_DEFAULT_PERMISSIONS); | ||||
FUSE_FLAGOPT(no_attrcache, FSESS_NO_ATTRCACHE); | FUSE_FLAGOPT(intr, FSESS_INTR); | ||||
FUSE_FLAGOPT(no_readahed, FSESS_NO_READAHEAD); | |||||
FUSE_FLAGOPT(no_datacache, FSESS_NO_DATACACHE); | |||||
FUSE_FLAGOPT(no_namecache, FSESS_NO_NAMECACHE); | |||||
FUSE_FLAGOPT(no_mmap, FSESS_NO_MMAP); | |||||
FUSE_FLAGOPT(brokenio, FSESS_BROKENIO); | |||||
(void)vfs_scanopt(opts, "max_read=", "%u", &max_read); | (void)vfs_scanopt(opts, "max_read=", "%u", &max_read); | ||||
if (vfs_scanopt(opts, "timeout=", "%u", &daemon_timeout) == 1) { | if (vfs_scanopt(opts, "timeout=", "%u", &daemon_timeout) == 1) { | ||||
if (daemon_timeout < FUSE_MIN_DAEMON_TIMEOUT) | if (daemon_timeout < FUSE_MIN_DAEMON_TIMEOUT) | ||||
daemon_timeout = FUSE_MIN_DAEMON_TIMEOUT; | daemon_timeout = FUSE_MIN_DAEMON_TIMEOUT; | ||||
else if (daemon_timeout > FUSE_MAX_DAEMON_TIMEOUT) | else if (daemon_timeout > FUSE_MAX_DAEMON_TIMEOUT) | ||||
daemon_timeout = FUSE_MAX_DAEMON_TIMEOUT; | daemon_timeout = FUSE_MAX_DAEMON_TIMEOUT; | ||||
} else { | } else { | ||||
daemon_timeout = FUSE_DEFAULT_DAEMON_TIMEOUT; | daemon_timeout = FUSE_DEFAULT_DAEMON_TIMEOUT; | ||||
} | } | ||||
subtype = vfs_getopts(opts, "subtype=", &err); | subtype = vfs_getopts(opts, "subtype=", &err); | ||||
SDT_PROBE1(fuse, , vfsops, mntopts, mntopts); | SDT_PROBE1(fusefs, , vfsops, mntopts, mntopts); | ||||
if (mp->mnt_flag & MNT_UPDATE) { | |||||
return fuse_vfs_remount(mp, td, mntopts, max_read, | |||||
daemon_timeout); | |||||
} | |||||
/* `from' contains the device name (eg. /dev/fuse0); REQUIRED */ | |||||
fspec = vfs_getopts(opts, "from", &err); | |||||
if (!fspec) | |||||
return err; | |||||
/* `fd' contains the filedescriptor for this session; REQUIRED */ | |||||
if (vfs_scanopt(opts, "fd", "%d", &fd) != 1) | |||||
return EINVAL; | |||||
err = fuse_getdevice(fspec, td, &fdev); | |||||
if (err != 0) | |||||
return err; | |||||
err = fget(td, fd, &cap_read_rights, &fp); | err = fget(td, fd, &cap_read_rights, &fp); | ||||
if (err != 0) { | if (err != 0) { | ||||
SDT_PROBE2(fuse, , vfsops, trace, 1, | SDT_PROBE2(fusefs, , vfsops, trace, 1, | ||||
"invalid or not opened device"); | "invalid or not opened device"); | ||||
goto out; | goto out; | ||||
} | } | ||||
fptmp = td->td_fpop; | fptmp = td->td_fpop; | ||||
td->td_fpop = fp; | td->td_fpop = fp; | ||||
err = devfs_get_cdevpriv((void **)&data); | err = devfs_get_cdevpriv((void **)&data); | ||||
td->td_fpop = fptmp; | td->td_fpop = fptmp; | ||||
fdrop(fp, td); | fdrop(fp, td); | ||||
FUSE_LOCK(); | FUSE_LOCK(); | ||||
if (err != 0 || data == NULL || data->mp != NULL) { | |||||
if (err != 0 || data == NULL) { | |||||
err = ENXIO; | err = ENXIO; | ||||
SDT_PROBE4(fuse, , vfsops, mount_err, | SDT_PROBE4(fusefs, , vfsops, mount_err, | ||||
"invalid or not opened device", data, mp, err); | "invalid or not opened device", data, mp, err); | ||||
FUSE_UNLOCK(); | FUSE_UNLOCK(); | ||||
goto out; | goto out; | ||||
} | } | ||||
if (fdata_get_dead(data)) { | if (fdata_get_dead(data)) { | ||||
err = ENOTCONN; | err = ENOTCONN; | ||||
SDT_PROBE4(fuse, , vfsops, mount_err, | SDT_PROBE4(fusefs, , vfsops, mount_err, | ||||
"device is dead during mount", data, mp, err); | "device is dead during mount", data, mp, err); | ||||
FUSE_UNLOCK(); | FUSE_UNLOCK(); | ||||
goto out; | goto out; | ||||
} | } | ||||
/* Sanity + permission checks */ | /* Sanity + permission checks */ | ||||
if (!data->daemoncred) | if (!data->daemoncred) | ||||
panic("fuse daemon found, but identity unknown"); | panic("fuse daemon found, but identity unknown"); | ||||
if (mntopts & FSESS_DAEMON_CAN_SPY) | if (mntopts & FSESS_DAEMON_CAN_SPY) | ||||
err = priv_check(td, PRIV_VFS_FUSE_ALLOWOTHER); | err = priv_check(td, PRIV_VFS_FUSE_ALLOWOTHER); | ||||
if (err == 0 && td->td_ucred->cr_uid != data->daemoncred->cr_uid) | if (err == 0 && td->td_ucred->cr_uid != data->daemoncred->cr_uid) | ||||
/* are we allowed to do the first mount? */ | /* are we allowed to do the first mount? */ | ||||
err = priv_check(td, PRIV_VFS_FUSE_MOUNT_NONUSER); | err = priv_check(td, PRIV_VFS_FUSE_MOUNT_NONUSER); | ||||
if (err) { | if (err) { | ||||
FUSE_UNLOCK(); | FUSE_UNLOCK(); | ||||
goto out; | goto out; | ||||
} | } | ||||
data->ref++; | data->ref++; | ||||
data->mp = mp; | data->mp = mp; | ||||
data->dataflags |= mntopts; | data->dataflags |= mntopts; | ||||
data->max_read = max_read; | data->max_read = max_read; | ||||
data->daemon_timeout = daemon_timeout; | data->daemon_timeout = daemon_timeout; | ||||
data->mnt_flag = mp->mnt_flag & MNT_UPDATEMASK; | |||||
FUSE_UNLOCK(); | FUSE_UNLOCK(); | ||||
vfs_getnewfsid(mp); | vfs_getnewfsid(mp); | ||||
MNT_ILOCK(mp); | MNT_ILOCK(mp); | ||||
mp->mnt_data = data; | mp->mnt_data = data; | ||||
mp->mnt_flag |= MNT_LOCAL; | /* | ||||
* FUSE file systems can be either local or remote, but the kernel | |||||
* can't tell the difference. | |||||
*/ | |||||
mp->mnt_flag &= ~MNT_LOCAL; | |||||
mp->mnt_kern_flag |= MNTK_USES_BCACHE; | mp->mnt_kern_flag |= MNTK_USES_BCACHE; | ||||
MNT_IUNLOCK(mp); | MNT_IUNLOCK(mp); | ||||
/* We need this here as this slot is used by getnewvnode() */ | /* We need this here as this slot is used by getnewvnode() */ | ||||
mp->mnt_stat.f_iosize = maxbcachebuf; | mp->mnt_stat.f_iosize = maxbcachebuf; | ||||
if (subtype) { | if (subtype) { | ||||
strlcat(mp->mnt_stat.f_fstypename, ".", MFSNAMELEN); | strlcat(mp->mnt_stat.f_fstypename, ".", MFSNAMELEN); | ||||
strlcat(mp->mnt_stat.f_fstypename, subtype, MFSNAMELEN); | strlcat(mp->mnt_stat.f_fstypename, subtype, MFSNAMELEN); | ||||
} | } | ||||
copystr(fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &len); | copystr(fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &len); | ||||
bzero(mp->mnt_stat.f_mntfromname + len, MNAMELEN - len); | bzero(mp->mnt_stat.f_mntfromname + len, MNAMELEN - len); | ||||
mp->mnt_iosize_max = MAXPHYS; | |||||
/* Now handshaking with daemon */ | /* Now handshaking with daemon */ | ||||
fuse_internal_send_init(data, td); | fuse_internal_send_init(data, td); | ||||
out: | out: | ||||
if (err) { | if (err) { | ||||
FUSE_LOCK(); | FUSE_LOCK(); | ||||
if (data != NULL && data->mp == mp) { | if (data != NULL && data->mp == mp) { | ||||
/* | /* | ||||
* Destroy device only if we acquired reference to | * Destroy device only if we acquired reference to | ||||
* it | * it | ||||
*/ | */ | ||||
SDT_PROBE4(fuse, , vfsops, mount_err, | SDT_PROBE4(fusefs, , vfsops, mount_err, | ||||
"mount failed, destroy device", data, mp, err); | "mount failed, destroy device", data, mp, err); | ||||
data->mp = NULL; | data->mp = NULL; | ||||
mp->mnt_data = NULL; | |||||
fdata_trydestroy(data); | fdata_trydestroy(data); | ||||
} | } | ||||
FUSE_UNLOCK(); | FUSE_UNLOCK(); | ||||
dev_rel(fdev); | dev_rel(fdev); | ||||
} | } | ||||
return err; | return err; | ||||
} | } | ||||
Show All 27 Lines | if (data->vroot != NULL) { | ||||
FUSE_UNLOCK(); | FUSE_UNLOCK(); | ||||
err = vflush(mp, 0, flags, td); | err = vflush(mp, 0, flags, td); | ||||
if (err) { | if (err) { | ||||
return err; | return err; | ||||
} | } | ||||
if (fdata_get_dead(data)) { | if (fdata_get_dead(data)) { | ||||
goto alreadydead; | goto alreadydead; | ||||
} | } | ||||
if (fsess_isimpl(mp, FUSE_DESTROY)) { | |||||
fdisp_init(&fdi, 0); | fdisp_init(&fdi, 0); | ||||
fdisp_make(&fdi, FUSE_DESTROY, mp, 0, td, NULL); | fdisp_make(&fdi, FUSE_DESTROY, mp, 0, td, NULL); | ||||
err = fdisp_wait_answ(&fdi); | (void)fdisp_wait_answ(&fdi); | ||||
fdisp_destroy(&fdi); | fdisp_destroy(&fdi); | ||||
} | |||||
fdata_set_dead(data); | fdata_set_dead(data); | ||||
alreadydead: | alreadydead: | ||||
FUSE_LOCK(); | FUSE_LOCK(); | ||||
data->mp = NULL; | data->mp = NULL; | ||||
fdev = data->fdev; | fdev = data->fdev; | ||||
fdata_trydestroy(data); | fdata_trydestroy(data); | ||||
FUSE_UNLOCK(); | FUSE_UNLOCK(); | ||||
MNT_ILOCK(mp); | MNT_ILOCK(mp); | ||||
mp->mnt_data = NULL; | mp->mnt_data = NULL; | ||||
mp->mnt_flag &= ~MNT_LOCAL; | |||||
MNT_IUNLOCK(mp); | MNT_IUNLOCK(mp); | ||||
dev_rel(fdev); | dev_rel(fdev); | ||||
return 0; | return 0; | ||||
} | } | ||||
SDT_PROBE_DEFINE1(fusefs, , vfsops, invalidate_without_export, | |||||
"struct mount*"); | |||||
static int | static int | ||||
fuse_vfsop_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) | |||||
{ | |||||
struct fuse_data *data = fuse_get_mpdata(mp); | |||||
uint64_t nodeid = ino; | |||||
struct thread *td = curthread; | |||||
struct fuse_dispatcher fdi; | |||||
struct fuse_entry_out *feo; | |||||
struct fuse_vnode_data *fvdat; | |||||
const char dot[] = "."; | |||||
off_t filesize; | |||||
enum vtype vtyp; | |||||
int error; | |||||
if (!(data->dataflags & FSESS_EXPORT_SUPPORT)) { | |||||
/* | |||||
* Unreachable unless you do something stupid, like export a | |||||
* nullfs mount of a fusefs file system. | |||||
*/ | |||||
SDT_PROBE1(fusefs, , vfsops, invalidate_without_export, mp); | |||||
return (EOPNOTSUPP); | |||||
} | |||||
error = fuse_internal_get_cached_vnode(mp, ino, flags, vpp); | |||||
if (error || *vpp != NULL) | |||||
return error; | |||||
/* Do a LOOKUP, using nodeid as the parent and "." as filename */ | |||||
fdisp_init(&fdi, sizeof(dot)); | |||||
fdisp_make(&fdi, FUSE_LOOKUP, mp, nodeid, td, td->td_ucred); | |||||
memcpy(fdi.indata, dot, sizeof(dot)); | |||||
error = fdisp_wait_answ(&fdi); | |||||
if (error) | |||||
return error; | |||||
feo = (struct fuse_entry_out *)fdi.answ; | |||||
if (feo->nodeid == 0) { | |||||
/* zero nodeid means ENOENT and cache it */ | |||||
error = ENOENT; | |||||
goto out; | |||||
} | |||||
vtyp = IFTOVT(feo->attr.mode); | |||||
error = fuse_vnode_get(mp, feo, nodeid, NULL, vpp, NULL, vtyp); | |||||
if (error) | |||||
goto out; | |||||
filesize = feo->attr.size; | |||||
/* | |||||
* In the case where we are looking up a FUSE node represented by an | |||||
* existing cached vnode, and the true size reported by FUSE_LOOKUP | |||||
* doesn't match the vnode's cached size, then any cached writes beyond | |||||
* the file's current size are lost. | |||||
* | |||||
* We can get here: | |||||
* * following attribute cache expiration, or | |||||
* * due a bug in the daemon, or | |||||
*/ | |||||
fvdat = VTOFUD(*vpp); | |||||
if (vnode_isreg(*vpp) && | |||||
filesize != fvdat->cached_attrs.va_size && | |||||
fvdat->flag & FN_SIZECHANGE) { | |||||
printf("%s: WB cache incoherent on %s!\n", __func__, | |||||
vnode_mount(*vpp)->mnt_stat.f_mntonname); | |||||
fvdat->flag &= ~FN_SIZECHANGE; | |||||
} | |||||
fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid, | |||||
feo->attr_valid_nsec, NULL); | |||||
fuse_validity_2_bintime(feo->entry_valid, feo->entry_valid_nsec, | |||||
&fvdat->entry_cache_timeout); | |||||
out: | |||||
fdisp_destroy(&fdi); | |||||
return error; | |||||
} | |||||
static int | |||||
fuse_vfsop_root(struct mount *mp, int lkflags, struct vnode **vpp) | fuse_vfsop_root(struct mount *mp, int lkflags, struct vnode **vpp) | ||||
{ | { | ||||
struct fuse_data *data = fuse_get_mpdata(mp); | struct fuse_data *data = fuse_get_mpdata(mp); | ||||
int err = 0; | int err = 0; | ||||
if (data->vroot != NULL) { | if (data->vroot != NULL) { | ||||
err = vget(data->vroot, lkflags, curthread); | err = vget(data->vroot, lkflags, curthread); | ||||
if (err == 0) | if (err == 0) | ||||
*vpp = data->vroot; | *vpp = data->vroot; | ||||
} else { | } else { | ||||
err = fuse_vnode_get(mp, NULL, FUSE_ROOT_ID, NULL, vpp, NULL, | err = fuse_vnode_get(mp, NULL, FUSE_ROOT_ID, NULL, vpp, NULL, | ||||
VDIR); | VDIR); | ||||
if (err == 0) { | if (err == 0) { | ||||
FUSE_LOCK(); | FUSE_LOCK(); | ||||
MPASS(data->vroot == NULL || data->vroot == *vpp); | MPASS(data->vroot == NULL || data->vroot == *vpp); | ||||
if (data->vroot == NULL) { | if (data->vroot == NULL) { | ||||
SDT_PROBE2(fuse, , vfsops, trace, 1, | SDT_PROBE2(fusefs, , vfsops, trace, 1, | ||||
"new root vnode"); | "new root vnode"); | ||||
data->vroot = *vpp; | data->vroot = *vpp; | ||||
FUSE_UNLOCK(); | FUSE_UNLOCK(); | ||||
vref(*vpp); | vref(*vpp); | ||||
} else if (data->vroot != *vpp) { | } else if (data->vroot != *vpp) { | ||||
SDT_PROBE2(fuse, , vfsops, trace, 1, | SDT_PROBE2(fusefs, , vfsops, trace, 1, | ||||
"root vnode race"); | "root vnode race"); | ||||
FUSE_UNLOCK(); | FUSE_UNLOCK(); | ||||
VOP_UNLOCK(*vpp, 0); | VOP_UNLOCK(*vpp, 0); | ||||
vrele(*vpp); | vrele(*vpp); | ||||
vrecycle(*vpp); | vrecycle(*vpp); | ||||
*vpp = data->vroot; | *vpp = data->vroot; | ||||
} else | } else | ||||
FUSE_UNLOCK(); | FUSE_UNLOCK(); | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | |||||
fake: | fake: | ||||
sbp->f_blocks = 0; | sbp->f_blocks = 0; | ||||
sbp->f_bfree = 0; | sbp->f_bfree = 0; | ||||
sbp->f_bavail = 0; | sbp->f_bavail = 0; | ||||
sbp->f_files = 0; | sbp->f_files = 0; | ||||
sbp->f_ffree = 0; | sbp->f_ffree = 0; | ||||
sbp->f_namemax = 0; | sbp->f_namemax = 0; | ||||
sbp->f_bsize = FUSE_DEFAULT_BLOCKSIZE; | sbp->f_bsize = S_BLKSIZE; | ||||
return 0; | return 0; | ||||
} | } |