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; | ||||
| } | } | ||||