151369649SPedro F. Giffuni /*-
251369649SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
351369649SPedro F. Giffuni *
45fe58019SAttilio Rao * Copyright (c) 2007-2009 Google Inc. and Amit Singh
55fe58019SAttilio Rao * All rights reserved.
65fe58019SAttilio Rao *
75fe58019SAttilio Rao * Redistribution and use in source and binary forms, with or without
85fe58019SAttilio Rao * modification, are permitted provided that the following conditions are
95fe58019SAttilio Rao * met:
105fe58019SAttilio Rao *
115fe58019SAttilio Rao * * Redistributions of source code must retain the above copyright
125fe58019SAttilio Rao * notice, this list of conditions and the following disclaimer.
135fe58019SAttilio Rao * * Redistributions in binary form must reproduce the above
145fe58019SAttilio Rao * copyright notice, this list of conditions and the following disclaimer
155fe58019SAttilio Rao * in the documentation and/or other materials provided with the
165fe58019SAttilio Rao * distribution.
175fe58019SAttilio Rao * * Neither the name of Google Inc. nor the names of its
185fe58019SAttilio Rao * contributors may be used to endorse or promote products derived from
195fe58019SAttilio Rao * this software without specific prior written permission.
205fe58019SAttilio Rao *
215fe58019SAttilio Rao * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
225fe58019SAttilio Rao * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
235fe58019SAttilio Rao * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
245fe58019SAttilio Rao * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
255fe58019SAttilio Rao * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
265fe58019SAttilio Rao * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
275fe58019SAttilio Rao * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
285fe58019SAttilio Rao * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
295fe58019SAttilio Rao * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
305fe58019SAttilio Rao * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
315fe58019SAttilio Rao * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
325fe58019SAttilio Rao *
335fe58019SAttilio Rao * Copyright (C) 2005 Csaba Henk.
345fe58019SAttilio Rao * All rights reserved.
355fe58019SAttilio Rao *
368aafc8c3SAlan Somers * Copyright (c) 2019 The FreeBSD Foundation
378aafc8c3SAlan Somers *
388aafc8c3SAlan Somers * Portions of this software were developed by BFF Storage Systems, LLC under
398aafc8c3SAlan Somers * sponsorship from the FreeBSD Foundation.
408aafc8c3SAlan Somers *
415fe58019SAttilio Rao * Redistribution and use in source and binary forms, with or without
425fe58019SAttilio Rao * modification, are permitted provided that the following conditions
435fe58019SAttilio Rao * are met:
445fe58019SAttilio Rao * 1. Redistributions of source code must retain the above copyright
455fe58019SAttilio Rao * notice, this list of conditions and the following disclaimer.
465fe58019SAttilio Rao * 2. Redistributions in binary form must reproduce the above copyright
475fe58019SAttilio Rao * notice, this list of conditions and the following disclaimer in the
485fe58019SAttilio Rao * documentation and/or other materials provided with the distribution.
495fe58019SAttilio Rao *
505fe58019SAttilio Rao * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
515fe58019SAttilio Rao * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
525fe58019SAttilio Rao * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
535fe58019SAttilio Rao * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
545fe58019SAttilio Rao * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
555fe58019SAttilio Rao * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
565fe58019SAttilio Rao * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
575fe58019SAttilio Rao * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
585fe58019SAttilio Rao * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
595fe58019SAttilio Rao * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
605fe58019SAttilio Rao * SUCH DAMAGE.
615fe58019SAttilio Rao */
625fe58019SAttilio Rao
63cf169498SAlan Somers #include <sys/param.h>
649a6a45d8SConrad Meyer #include <sys/buf.h>
655fe58019SAttilio Rao #include <sys/module.h>
665fe58019SAttilio Rao #include <sys/systm.h>
675fe58019SAttilio Rao #include <sys/errno.h>
685fe58019SAttilio Rao #include <sys/kernel.h>
694a144410SRobert Watson #include <sys/capsicum.h>
705fe58019SAttilio Rao #include <sys/conf.h>
715fe58019SAttilio Rao #include <sys/filedesc.h>
725fe58019SAttilio Rao #include <sys/uio.h>
735fe58019SAttilio Rao #include <sys/malloc.h>
745fe58019SAttilio Rao #include <sys/queue.h>
755fe58019SAttilio Rao #include <sys/lock.h>
765fe58019SAttilio Rao #include <sys/sx.h>
775fe58019SAttilio Rao #include <sys/mutex.h>
785fe58019SAttilio Rao #include <sys/proc.h>
795fe58019SAttilio Rao #include <sys/vnode.h>
805fe58019SAttilio Rao #include <sys/namei.h>
815fe58019SAttilio Rao #include <sys/mount.h>
825fe58019SAttilio Rao #include <sys/sysctl.h>
835fe58019SAttilio Rao #include <sys/fcntl.h>
845fe58019SAttilio Rao
855fe58019SAttilio Rao #include "fuse.h"
865fe58019SAttilio Rao #include "fuse_node.h"
875fe58019SAttilio Rao #include "fuse_ipc.h"
885fe58019SAttilio Rao #include "fuse_internal.h"
895fe58019SAttilio Rao
905fe58019SAttilio Rao #include <sys/priv.h>
915fe58019SAttilio Rao #include <security/mac/mac_framework.h>
925fe58019SAttilio Rao
93419e7ff6SAlan Somers SDT_PROVIDER_DECLARE(fusefs);
94cf169498SAlan Somers /*
95cf169498SAlan Somers * Fuse trace probe:
96cf169498SAlan Somers * arg0: verbosity. Higher numbers give more verbose messages
97cf169498SAlan Somers * arg1: Textual message
98cf169498SAlan Somers */
99419e7ff6SAlan Somers SDT_PROBE_DEFINE2(fusefs, , vfsops, trace, "int", "char*");
1005fe58019SAttilio Rao
1015fe58019SAttilio Rao /* This will do for privilege types for now */
1025fe58019SAttilio Rao #ifndef PRIV_VFS_FUSE_ALLOWOTHER
1035fe58019SAttilio Rao #define PRIV_VFS_FUSE_ALLOWOTHER PRIV_VFS_MOUNT_NONUSER
1045fe58019SAttilio Rao #endif
1055fe58019SAttilio Rao #ifndef PRIV_VFS_FUSE_MOUNT_NONUSER
1065fe58019SAttilio Rao #define PRIV_VFS_FUSE_MOUNT_NONUSER PRIV_VFS_MOUNT_NONUSER
1075fe58019SAttilio Rao #endif
1085fe58019SAttilio Rao #ifndef PRIV_VFS_FUSE_SYNC_UNMOUNT
1095fe58019SAttilio Rao #define PRIV_VFS_FUSE_SYNC_UNMOUNT PRIV_VFS_MOUNT_NONUSER
1105fe58019SAttilio Rao #endif
1115fe58019SAttilio Rao
112e5b50fe7SAlan Somers static vfs_fhtovp_t fuse_vfsop_fhtovp;
1135fe58019SAttilio Rao static vfs_mount_t fuse_vfsop_mount;
1145fe58019SAttilio Rao static vfs_unmount_t fuse_vfsop_unmount;
1155fe58019SAttilio Rao static vfs_root_t fuse_vfsop_root;
1165fe58019SAttilio Rao static vfs_statfs_t fuse_vfsop_statfs;
117e5b50fe7SAlan Somers static vfs_vget_t fuse_vfsop_vget;
1185fe58019SAttilio Rao
1195fe58019SAttilio Rao struct vfsops fuse_vfsops = {
120e5b50fe7SAlan Somers .vfs_fhtovp = fuse_vfsop_fhtovp,
1215fe58019SAttilio Rao .vfs_mount = fuse_vfsop_mount,
1225fe58019SAttilio Rao .vfs_unmount = fuse_vfsop_unmount,
1235fe58019SAttilio Rao .vfs_root = fuse_vfsop_root,
1245fe58019SAttilio Rao .vfs_statfs = fuse_vfsop_statfs,
125e5b50fe7SAlan Somers .vfs_vget = fuse_vfsop_vget,
1265fe58019SAttilio Rao };
1275fe58019SAttilio Rao
1285fe58019SAttilio Rao static int fuse_enforce_dev_perms = 0;
1295fe58019SAttilio Rao
130123af6ecSAlan Somers SYSCTL_INT(_vfs_fusefs, OID_AUTO, enforce_dev_perms, CTLFLAG_RW,
1315fe58019SAttilio Rao &fuse_enforce_dev_perms, 0,
1325fe58019SAttilio Rao "enforce fuse device permissions for secondary mounts");
1335fe58019SAttilio Rao
1345fe58019SAttilio Rao MALLOC_DEFINE(M_FUSEVFS, "fuse_filesystem", "buffer for fuse vfs layer");
1355fe58019SAttilio Rao
1365fe58019SAttilio Rao static int
fuse_getdevice(const char * fspec,struct thread * td,struct cdev ** fdevp)1375fe58019SAttilio Rao fuse_getdevice(const char *fspec, struct thread *td, struct cdev **fdevp)
1385fe58019SAttilio Rao {
1395fe58019SAttilio Rao struct nameidata nd, *ndp = &nd;
1405fe58019SAttilio Rao struct vnode *devvp;
1415fe58019SAttilio Rao struct cdev *fdev;
1425fe58019SAttilio Rao int err;
1435fe58019SAttilio Rao
1445fe58019SAttilio Rao /*
1455fe58019SAttilio Rao * Not an update, or updating the name: look up the name
1465fe58019SAttilio Rao * and verify that it refers to a sensible disk device.
1475fe58019SAttilio Rao */
1485fe58019SAttilio Rao
1497e1d3eefSMateusz Guzik NDINIT(ndp, LOOKUP, FOLLOW, UIO_SYSSPACE, fspec);
1505fe58019SAttilio Rao if ((err = namei(ndp)) != 0)
1515fe58019SAttilio Rao return err;
152bb92cd7bSMateusz Guzik NDFREE_PNBUF(ndp);
1535fe58019SAttilio Rao devvp = ndp->ni_vp;
1545fe58019SAttilio Rao
1555fe58019SAttilio Rao if (devvp->v_type != VCHR) {
1565fe58019SAttilio Rao vrele(devvp);
1575fe58019SAttilio Rao return ENXIO;
1585fe58019SAttilio Rao }
1595fe58019SAttilio Rao fdev = devvp->v_rdev;
1605fe58019SAttilio Rao dev_ref(fdev);
1615fe58019SAttilio Rao
1625fe58019SAttilio Rao if (fuse_enforce_dev_perms) {
1635fe58019SAttilio Rao /*
1645fe58019SAttilio Rao * Check if mounter can open the fuse device.
1655fe58019SAttilio Rao *
1665fe58019SAttilio Rao * This has significance only if we are doing a secondary mount
1675fe58019SAttilio Rao * which doesn't involve actually opening fuse devices, but we
1685fe58019SAttilio Rao * still want to enforce the permissions of the device (in
1695fe58019SAttilio Rao * order to keep control over the circle of fuse users).
1705fe58019SAttilio Rao *
1715fe58019SAttilio Rao * (In case of primary mounts, we are either the superuser so
1725fe58019SAttilio Rao * we can do anything anyway, or we can mount only if the
1735fe58019SAttilio Rao * device is already opened by us, ie. we are permitted to open
1745fe58019SAttilio Rao * the device.)
1755fe58019SAttilio Rao */
1765fe58019SAttilio Rao #if 0
1775fe58019SAttilio Rao #ifdef MAC
1785fe58019SAttilio Rao err = mac_check_vnode_open(td->td_ucred, devvp, VREAD | VWRITE);
1795fe58019SAttilio Rao if (!err)
1805fe58019SAttilio Rao #endif
1815fe58019SAttilio Rao #endif /* 0 */
1825fe58019SAttilio Rao err = VOP_ACCESS(devvp, VREAD | VWRITE, td->td_ucred, td);
1835fe58019SAttilio Rao if (err) {
1845fe58019SAttilio Rao vrele(devvp);
1855fe58019SAttilio Rao dev_rel(fdev);
1865fe58019SAttilio Rao return err;
1875fe58019SAttilio Rao }
1885fe58019SAttilio Rao }
1895fe58019SAttilio Rao /*
1905fe58019SAttilio Rao * according to coda code, no extra lock is needed --
1915fe58019SAttilio Rao * although in sys/vnode.h this field is marked "v"
1925fe58019SAttilio Rao */
1935fe58019SAttilio Rao vrele(devvp);
1945fe58019SAttilio Rao
1955fe58019SAttilio Rao if (!fdev->si_devsw ||
1965fe58019SAttilio Rao strcmp("fuse", fdev->si_devsw->d_name)) {
1975fe58019SAttilio Rao dev_rel(fdev);
1985fe58019SAttilio Rao return ENXIO;
1995fe58019SAttilio Rao }
2005fe58019SAttilio Rao *fdevp = fdev;
2015fe58019SAttilio Rao
2025fe58019SAttilio Rao return 0;
2035fe58019SAttilio Rao }
2045fe58019SAttilio Rao
2055fe58019SAttilio Rao #define FUSE_FLAGOPT(fnam, fval) do { \
2065fe58019SAttilio Rao vfs_flagopt(opts, #fnam, &mntopts, fval); \
2075fe58019SAttilio Rao vfs_flagopt(opts, "__" #fnam, &__mntopts, fval); \
2085fe58019SAttilio Rao } while (0)
2095fe58019SAttilio Rao
210419e7ff6SAlan Somers SDT_PROBE_DEFINE1(fusefs, , vfsops, mntopts, "uint64_t");
211419e7ff6SAlan Somers SDT_PROBE_DEFINE4(fusefs, , vfsops, mount_err, "char*", "struct fuse_data*",
212cf169498SAlan Somers "struct mount*", "int");
213cf169498SAlan Somers
2145fe58019SAttilio Rao static int
fuse_vfs_remount(struct mount * mp,struct thread * td,uint64_t mntopts,uint32_t max_read,int daemon_timeout)215a6fac00cSAlan Somers fuse_vfs_remount(struct mount *mp, struct thread *td, uint64_t mntopts,
216a6fac00cSAlan Somers uint32_t max_read, int daemon_timeout)
217a6fac00cSAlan Somers {
218a6fac00cSAlan Somers int err = 0;
219a6fac00cSAlan Somers struct fuse_data *data = fuse_get_mpdata(mp);
220a6fac00cSAlan Somers /* Don't allow these options to be changed */
221a6fac00cSAlan Somers const static unsigned long long cant_update_opts =
222a6fac00cSAlan Somers MNT_USER; /* Mount owner must be the user running the daemon */
223a6fac00cSAlan Somers
224a6fac00cSAlan Somers FUSE_LOCK();
225a6fac00cSAlan Somers
226a6fac00cSAlan Somers if ((mp->mnt_flag ^ data->mnt_flag) & cant_update_opts) {
227a6fac00cSAlan Somers err = EOPNOTSUPP;
228a6fac00cSAlan Somers SDT_PROBE4(fusefs, , vfsops, mount_err,
229a6fac00cSAlan Somers "Can't change these mount options during remount",
230a6fac00cSAlan Somers data, mp, err);
231a6fac00cSAlan Somers goto out;
232a6fac00cSAlan Somers }
233a6fac00cSAlan Somers if (((data->dataflags ^ mntopts) & FSESS_MNTOPTS_MASK) ||
234a6fac00cSAlan Somers (data->max_read != max_read) ||
235a6fac00cSAlan Somers (data->daemon_timeout != daemon_timeout)) {
236a6fac00cSAlan Somers // TODO: allow changing options where it makes sense
237a6fac00cSAlan Somers err = EOPNOTSUPP;
238a6fac00cSAlan Somers SDT_PROBE4(fusefs, , vfsops, mount_err,
239a6fac00cSAlan Somers "Can't change fuse mount options during remount",
240a6fac00cSAlan Somers data, mp, err);
241a6fac00cSAlan Somers goto out;
242a6fac00cSAlan Somers }
243a6fac00cSAlan Somers
244a6fac00cSAlan Somers if (fdata_get_dead(data)) {
245a6fac00cSAlan Somers err = ENOTCONN;
246a6fac00cSAlan Somers SDT_PROBE4(fusefs, , vfsops, mount_err,
247a6fac00cSAlan Somers "device is dead during mount", data, mp, err);
248a6fac00cSAlan Somers goto out;
249a6fac00cSAlan Somers }
250a6fac00cSAlan Somers
251a6fac00cSAlan Somers /* Sanity + permission checks */
252a6fac00cSAlan Somers if (!data->daemoncred)
253a6fac00cSAlan Somers panic("fuse daemon found, but identity unknown");
254a6fac00cSAlan Somers if (mntopts & FSESS_DAEMON_CAN_SPY)
255a6fac00cSAlan Somers err = priv_check(td, PRIV_VFS_FUSE_ALLOWOTHER);
256a6fac00cSAlan Somers if (err == 0 && td->td_ucred->cr_uid != data->daemoncred->cr_uid)
257a6fac00cSAlan Somers /* are we allowed to do the first mount? */
258a6fac00cSAlan Somers err = priv_check(td, PRIV_VFS_FUSE_MOUNT_NONUSER);
259a6fac00cSAlan Somers
260a6fac00cSAlan Somers out:
261a6fac00cSAlan Somers FUSE_UNLOCK();
262a6fac00cSAlan Somers return err;
263a6fac00cSAlan Somers }
264a6fac00cSAlan Somers
265a6fac00cSAlan Somers static int
fuse_vfsop_fhtovp(struct mount * mp,struct fid * fhp,int flags,struct vnode ** vpp)266e5b50fe7SAlan Somers fuse_vfsop_fhtovp(struct mount *mp, struct fid *fhp, int flags,
267e5b50fe7SAlan Somers struct vnode **vpp)
268e5b50fe7SAlan Somers {
269e5b50fe7SAlan Somers struct fuse_fid *ffhp = (struct fuse_fid *)fhp;
270e5b50fe7SAlan Somers struct fuse_vnode_data *fvdat;
271e5b50fe7SAlan Somers struct vnode *nvp;
272e5b50fe7SAlan Somers int error;
273e5b50fe7SAlan Somers
274e5b50fe7SAlan Somers if (!(fuse_get_mpdata(mp)->dataflags & FSESS_EXPORT_SUPPORT))
275e5b50fe7SAlan Somers return EOPNOTSUPP;
276e5b50fe7SAlan Somers
277e5b50fe7SAlan Somers error = VFS_VGET(mp, ffhp->nid, LK_EXCLUSIVE, &nvp);
278e5b50fe7SAlan Somers if (error) {
279e5b50fe7SAlan Somers *vpp = NULLVP;
280e5b50fe7SAlan Somers return (error);
281e5b50fe7SAlan Somers }
282e5b50fe7SAlan Somers fvdat = VTOFUD(nvp);
283e5b50fe7SAlan Somers if (fvdat->generation != ffhp->gen ) {
284e5b50fe7SAlan Somers vput(nvp);
285e5b50fe7SAlan Somers *vpp = NULLVP;
286e5b50fe7SAlan Somers return (ESTALE);
287e5b50fe7SAlan Somers }
288e5b50fe7SAlan Somers *vpp = nvp;
28956a8aca8SPawel Jakub Dawidek vnode_create_vobject(*vpp, VNODE_NO_SIZE, curthread);
290e5b50fe7SAlan Somers return (0);
291e5b50fe7SAlan Somers }
292e5b50fe7SAlan Somers
293e5b50fe7SAlan Somers static int
fuse_vfsop_mount(struct mount * mp)2945fe58019SAttilio Rao fuse_vfsop_mount(struct mount *mp)
2955fe58019SAttilio Rao {
2965fe58019SAttilio Rao int err;
2975fe58019SAttilio Rao
2985fe58019SAttilio Rao uint64_t mntopts, __mntopts;
2995fe58019SAttilio Rao uint32_t max_read;
300e3b1c847SEdward Tomasz Napierala int linux_errnos;
3015fe58019SAttilio Rao int daemon_timeout;
3025fe58019SAttilio Rao int fd;
3035fe58019SAttilio Rao
3045fe58019SAttilio Rao struct cdev *fdev;
3058ba190efSAlan Somers struct fuse_data *data = NULL;
3065fe58019SAttilio Rao struct thread *td;
3075fe58019SAttilio Rao struct file *fp, *fptmp;
3082f636248SAlan Somers char *fspec, *subtype, *fsname = NULL;
3092f636248SAlan Somers int fsnamelen;
3105fe58019SAttilio Rao struct vfsoptlist *opts;
3115fe58019SAttilio Rao
3125fe58019SAttilio Rao subtype = NULL;
3135fe58019SAttilio Rao max_read = ~0;
314e3b1c847SEdward Tomasz Napierala linux_errnos = 0;
3155fe58019SAttilio Rao err = 0;
3165fe58019SAttilio Rao mntopts = 0;
3175fe58019SAttilio Rao __mntopts = 0;
3185fe58019SAttilio Rao td = curthread;
3195fe58019SAttilio Rao
3205fe58019SAttilio Rao /* Get the new options passed to mount */
3215fe58019SAttilio Rao opts = mp->mnt_optnew;
3225fe58019SAttilio Rao
3235fe58019SAttilio Rao if (!opts)
3245fe58019SAttilio Rao return EINVAL;
3255fe58019SAttilio Rao
3265fe58019SAttilio Rao /* `fspath' contains the mount point (eg. /mnt/fuse/sshfs); REQUIRED */
3275fe58019SAttilio Rao if (!vfs_getopts(opts, "fspath", &err))
3285fe58019SAttilio Rao return err;
3295fe58019SAttilio Rao
3305fe58019SAttilio Rao /*
3315fe58019SAttilio Rao * With the help of underscored options the mount program
3325fe58019SAttilio Rao * can inform us from the flags it sets by default
3335fe58019SAttilio Rao */
3345fe58019SAttilio Rao FUSE_FLAGOPT(allow_other, FSESS_DAEMON_CAN_SPY);
3355fe58019SAttilio Rao FUSE_FLAGOPT(push_symlinks_in, FSESS_PUSH_SYMLINKS_IN);
3365fe58019SAttilio Rao FUSE_FLAGOPT(default_permissions, FSESS_DEFAULT_PERMISSIONS);
337ed74f781SAlan Somers FUSE_FLAGOPT(intr, FSESS_INTR);
3385fe58019SAttilio Rao
3393dc1c7d6SConrad Meyer (void)vfs_scanopt(opts, "max_read=", "%u", &max_read);
340e3b1c847SEdward Tomasz Napierala (void)vfs_scanopt(opts, "linux_errnos", "%d", &linux_errnos);
3415fe58019SAttilio Rao if (vfs_scanopt(opts, "timeout=", "%u", &daemon_timeout) == 1) {
3425fe58019SAttilio Rao if (daemon_timeout < FUSE_MIN_DAEMON_TIMEOUT)
3435fe58019SAttilio Rao daemon_timeout = FUSE_MIN_DAEMON_TIMEOUT;
3445fe58019SAttilio Rao else if (daemon_timeout > FUSE_MAX_DAEMON_TIMEOUT)
3455fe58019SAttilio Rao daemon_timeout = FUSE_MAX_DAEMON_TIMEOUT;
3465fe58019SAttilio Rao } else {
3475fe58019SAttilio Rao daemon_timeout = FUSE_DEFAULT_DAEMON_TIMEOUT;
3485fe58019SAttilio Rao }
3495fe58019SAttilio Rao subtype = vfs_getopts(opts, "subtype=", &err);
3505fe58019SAttilio Rao
351419e7ff6SAlan Somers SDT_PROBE1(fusefs, , vfsops, mntopts, mntopts);
3525fe58019SAttilio Rao
353a6fac00cSAlan Somers if (mp->mnt_flag & MNT_UPDATE) {
354a6fac00cSAlan Somers return fuse_vfs_remount(mp, td, mntopts, max_read,
355a6fac00cSAlan Somers daemon_timeout);
356a6fac00cSAlan Somers }
357a6fac00cSAlan Somers
358a6fac00cSAlan Somers /* `from' contains the device name (eg. /dev/fuse0); REQUIRED */
359a6fac00cSAlan Somers fspec = vfs_getopts(opts, "from", &err);
360a6fac00cSAlan Somers if (!fspec)
361a6fac00cSAlan Somers return err;
362a6fac00cSAlan Somers
363a6fac00cSAlan Somers /* `fd' contains the filedescriptor for this session; REQUIRED */
364a6fac00cSAlan Somers if (vfs_scanopt(opts, "fd", "%d", &fd) != 1)
365a6fac00cSAlan Somers return EINVAL;
366a6fac00cSAlan Somers
367a6fac00cSAlan Somers err = fuse_getdevice(fspec, td, &fdev);
368a6fac00cSAlan Somers if (err != 0)
369a6fac00cSAlan Somers return err;
370a6fac00cSAlan Somers
371cbd92ce6SMatt Macy err = fget(td, fd, &cap_read_rights, &fp);
3725fe58019SAttilio Rao if (err != 0) {
373419e7ff6SAlan Somers SDT_PROBE2(fusefs, , vfsops, trace, 1,
374cf169498SAlan Somers "invalid or not opened device");
3755fe58019SAttilio Rao goto out;
3765fe58019SAttilio Rao }
3775fe58019SAttilio Rao fptmp = td->td_fpop;
3785fe58019SAttilio Rao td->td_fpop = fp;
3795fe58019SAttilio Rao err = devfs_get_cdevpriv((void **)&data);
3805fe58019SAttilio Rao td->td_fpop = fptmp;
3815fe58019SAttilio Rao fdrop(fp, td);
3825fe58019SAttilio Rao FUSE_LOCK();
383e5b50fe7SAlan Somers
384e5b50fe7SAlan Somers if (err != 0 || data == NULL) {
3855fe58019SAttilio Rao err = ENXIO;
386419e7ff6SAlan Somers SDT_PROBE4(fusefs, , vfsops, mount_err,
387cf169498SAlan Somers "invalid or not opened device", data, mp, err);
3885fe58019SAttilio Rao FUSE_UNLOCK();
3895fe58019SAttilio Rao goto out;
3905fe58019SAttilio Rao }
3915fe58019SAttilio Rao if (fdata_get_dead(data)) {
3925fe58019SAttilio Rao err = ENOTCONN;
393419e7ff6SAlan Somers SDT_PROBE4(fusefs, , vfsops, mount_err,
394cf169498SAlan Somers "device is dead during mount", data, mp, err);
3955fe58019SAttilio Rao FUSE_UNLOCK();
3965fe58019SAttilio Rao goto out;
3975fe58019SAttilio Rao }
3985fe58019SAttilio Rao /* Sanity + permission checks */
3995fe58019SAttilio Rao if (!data->daemoncred)
4005fe58019SAttilio Rao panic("fuse daemon found, but identity unknown");
4015fe58019SAttilio Rao if (mntopts & FSESS_DAEMON_CAN_SPY)
4025fe58019SAttilio Rao err = priv_check(td, PRIV_VFS_FUSE_ALLOWOTHER);
4035fe58019SAttilio Rao if (err == 0 && td->td_ucred->cr_uid != data->daemoncred->cr_uid)
4045fe58019SAttilio Rao /* are we allowed to do the first mount? */
4055fe58019SAttilio Rao err = priv_check(td, PRIV_VFS_FUSE_MOUNT_NONUSER);
4065fe58019SAttilio Rao if (err) {
4075fe58019SAttilio Rao FUSE_UNLOCK();
4085fe58019SAttilio Rao goto out;
4095fe58019SAttilio Rao }
4105fe58019SAttilio Rao data->ref++;
4115fe58019SAttilio Rao data->mp = mp;
4125fe58019SAttilio Rao data->dataflags |= mntopts;
4135fe58019SAttilio Rao data->max_read = max_read;
4145fe58019SAttilio Rao data->daemon_timeout = daemon_timeout;
415e3b1c847SEdward Tomasz Napierala data->linux_errnos = linux_errnos;
416a6fac00cSAlan Somers data->mnt_flag = mp->mnt_flag & MNT_UPDATEMASK;
4175fe58019SAttilio Rao FUSE_UNLOCK();
4185fe58019SAttilio Rao
4195fe58019SAttilio Rao vfs_getnewfsid(mp);
4201750b7b9SAttilio Rao MNT_ILOCK(mp);
4211750b7b9SAttilio Rao mp->mnt_data = data;
4226f8114adSAlan Somers /*
4236f8114adSAlan Somers * FUSE file systems can be either local or remote, but the kernel
4246f8114adSAlan Somers * can't tell the difference.
4256f8114adSAlan Somers */
4266f8114adSAlan Somers mp->mnt_flag &= ~MNT_LOCAL;
427dda11d4aSRick Macklem mp->mnt_kern_flag |= MNTK_USES_BCACHE;
4287096c29eSAlan Somers /*
4297096c29eSAlan Somers * Disable nullfs cacheing because it can consume too many resources in
4307096c29eSAlan Somers * the FUSE server.
4317096c29eSAlan Somers */
4327096c29eSAlan Somers mp->mnt_kern_flag |= MNTK_NULL_NOCACHE;
4331750b7b9SAttilio Rao MNT_IUNLOCK(mp);
4341750b7b9SAttilio Rao /* We need this here as this slot is used by getnewvnode() */
4359a6a45d8SConrad Meyer mp->mnt_stat.f_iosize = maxbcachebuf;
4365fe58019SAttilio Rao if (subtype) {
4375fe58019SAttilio Rao strlcat(mp->mnt_stat.f_fstypename, ".", MFSNAMELEN);
4385fe58019SAttilio Rao strlcat(mp->mnt_stat.f_fstypename, subtype, MFSNAMELEN);
4395fe58019SAttilio Rao }
440852c303bSConrad Meyer memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN);
4412f636248SAlan Somers vfs_getopt(opts, "fsname=", (void**)&fsname, &fsnamelen);
4422f636248SAlan Somers strlcpy(mp->mnt_stat.f_mntfromname,
4432f636248SAlan Somers fsname == NULL ? fspec : fsname, MNAMELEN);
444cd853791SKonstantin Belousov mp->mnt_iosize_max = maxphys;
4455fe58019SAttilio Rao
4465fe58019SAttilio Rao /* Now handshaking with daemon */
4475fe58019SAttilio Rao fuse_internal_send_init(data, td);
4485fe58019SAttilio Rao
4495fe58019SAttilio Rao out:
4505fe58019SAttilio Rao if (err) {
4515fe58019SAttilio Rao FUSE_LOCK();
4522c338af1SAlan Somers if (data != NULL && data->mp == mp) {
4535fe58019SAttilio Rao /*
4545fe58019SAttilio Rao * Destroy device only if we acquired reference to
4555fe58019SAttilio Rao * it
4565fe58019SAttilio Rao */
457419e7ff6SAlan Somers SDT_PROBE4(fusefs, , vfsops, mount_err,
458cf169498SAlan Somers "mount failed, destroy device", data, mp, err);
4595fe58019SAttilio Rao data->mp = NULL;
460a6fac00cSAlan Somers mp->mnt_data = NULL;
4615fe58019SAttilio Rao fdata_trydestroy(data);
4625fe58019SAttilio Rao }
4635fe58019SAttilio Rao FUSE_UNLOCK();
4645fe58019SAttilio Rao dev_rel(fdev);
4655fe58019SAttilio Rao }
4665fe58019SAttilio Rao return err;
4675fe58019SAttilio Rao }
4685fe58019SAttilio Rao
4695fe58019SAttilio Rao static int
fuse_vfsop_unmount(struct mount * mp,int mntflags)4705fe58019SAttilio Rao fuse_vfsop_unmount(struct mount *mp, int mntflags)
4715fe58019SAttilio Rao {
4725fe58019SAttilio Rao int err = 0;
4735fe58019SAttilio Rao int flags = 0;
4745fe58019SAttilio Rao
4755fe58019SAttilio Rao struct cdev *fdev;
4765fe58019SAttilio Rao struct fuse_data *data;
4775fe58019SAttilio Rao struct fuse_dispatcher fdi;
4785fe58019SAttilio Rao struct thread *td = curthread;
4795fe58019SAttilio Rao
4805fe58019SAttilio Rao if (mntflags & MNT_FORCE) {
4815fe58019SAttilio Rao flags |= FORCECLOSE;
4825fe58019SAttilio Rao }
4835fe58019SAttilio Rao data = fuse_get_mpdata(mp);
4845fe58019SAttilio Rao if (!data) {
4855fe58019SAttilio Rao panic("no private data for mount point?");
4865fe58019SAttilio Rao }
4875fe58019SAttilio Rao /* There is 1 extra root vnode reference (mp->mnt_data). */
4885fe58019SAttilio Rao FUSE_LOCK();
4895fe58019SAttilio Rao if (data->vroot != NULL) {
4905fe58019SAttilio Rao struct vnode *vroot = data->vroot;
4915fe58019SAttilio Rao
4925fe58019SAttilio Rao data->vroot = NULL;
4935fe58019SAttilio Rao FUSE_UNLOCK();
4945fe58019SAttilio Rao vrele(vroot);
4955fe58019SAttilio Rao } else
4965fe58019SAttilio Rao FUSE_UNLOCK();
4975fe58019SAttilio Rao err = vflush(mp, 0, flags, td);
4985fe58019SAttilio Rao if (err) {
4995fe58019SAttilio Rao return err;
5005fe58019SAttilio Rao }
5015fe58019SAttilio Rao if (fdata_get_dead(data)) {
5025fe58019SAttilio Rao goto alreadydead;
5035fe58019SAttilio Rao }
50437df9d3bSAlan Somers if (fsess_maybe_impl(mp, FUSE_DESTROY)) {
5055fe58019SAttilio Rao fdisp_init(&fdi, 0);
5065fe58019SAttilio Rao fdisp_make(&fdi, FUSE_DESTROY, mp, 0, td, NULL);
5075fe58019SAttilio Rao
508192a9181SAlan Somers (void)fdisp_wait_answ(&fdi);
5095fe58019SAttilio Rao fdisp_destroy(&fdi);
510192a9181SAlan Somers }
5115fe58019SAttilio Rao
5125fe58019SAttilio Rao fdata_set_dead(data);
5135fe58019SAttilio Rao
5145fe58019SAttilio Rao alreadydead:
5155fe58019SAttilio Rao FUSE_LOCK();
5165fe58019SAttilio Rao data->mp = NULL;
5175fe58019SAttilio Rao fdev = data->fdev;
5185fe58019SAttilio Rao fdata_trydestroy(data);
5195fe58019SAttilio Rao FUSE_UNLOCK();
5205fe58019SAttilio Rao
5215fe58019SAttilio Rao MNT_ILOCK(mp);
5225fe58019SAttilio Rao mp->mnt_data = NULL;
5235fe58019SAttilio Rao MNT_IUNLOCK(mp);
5245fe58019SAttilio Rao
5255fe58019SAttilio Rao dev_rel(fdev);
5265fe58019SAttilio Rao
5275fe58019SAttilio Rao return 0;
5285fe58019SAttilio Rao }
5295fe58019SAttilio Rao
5306ff7f297SAlan Somers SDT_PROBE_DEFINE1(fusefs, , vfsops, invalidate_without_export,
5316ff7f297SAlan Somers "struct mount*");
5325fe58019SAttilio Rao static int
fuse_vfsop_vget(struct mount * mp,ino_t ino,int flags,struct vnode ** vpp)533e5b50fe7SAlan Somers fuse_vfsop_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
534e5b50fe7SAlan Somers {
5356ff7f297SAlan Somers struct fuse_data *data = fuse_get_mpdata(mp);
536e5b50fe7SAlan Somers uint64_t nodeid = ino;
537e5b50fe7SAlan Somers struct thread *td = curthread;
538e5b50fe7SAlan Somers struct fuse_dispatcher fdi;
539e5b50fe7SAlan Somers struct fuse_entry_out *feo;
540e5b50fe7SAlan Somers struct fuse_vnode_data *fvdat;
54113d593a5SAlan Somers struct timespec now;
542e5b50fe7SAlan Somers const char dot[] = ".";
543ba8cc6d7SMateusz Guzik __enum_uint8(vtype) vtyp;
544e5b50fe7SAlan Somers int error;
545e5b50fe7SAlan Somers
5466ff7f297SAlan Somers if (!(data->dataflags & FSESS_EXPORT_SUPPORT)) {
5476ff7f297SAlan Somers /*
5486ff7f297SAlan Somers * Unreachable unless you do something stupid, like export a
5496ff7f297SAlan Somers * nullfs mount of a fusefs file system.
5506ff7f297SAlan Somers */
5516ff7f297SAlan Somers SDT_PROBE1(fusefs, , vfsops, invalidate_without_export, mp);
5526ff7f297SAlan Somers return (EOPNOTSUPP);
5536ff7f297SAlan Somers }
5546ff7f297SAlan Somers
555eae1ae13SAlan Somers error = fuse_internal_get_cached_vnode(mp, ino, flags, vpp);
556eae1ae13SAlan Somers if (error || *vpp != NULL)
5570d2bf489SAlan Somers return error;
558e5b50fe7SAlan Somers
55913d593a5SAlan Somers getnanouptime(&now);
56013d593a5SAlan Somers
561e5b50fe7SAlan Somers /* Do a LOOKUP, using nodeid as the parent and "." as filename */
562e5b50fe7SAlan Somers fdisp_init(&fdi, sizeof(dot));
563e5b50fe7SAlan Somers fdisp_make(&fdi, FUSE_LOOKUP, mp, nodeid, td, td->td_ucred);
564e5b50fe7SAlan Somers memcpy(fdi.indata, dot, sizeof(dot));
565e5b50fe7SAlan Somers error = fdisp_wait_answ(&fdi);
566e5b50fe7SAlan Somers
567e5b50fe7SAlan Somers if (error)
568*969d1aa4SAlan Somers goto out;
569e5b50fe7SAlan Somers
570e5b50fe7SAlan Somers feo = (struct fuse_entry_out *)fdi.answ;
571e5b50fe7SAlan Somers if (feo->nodeid == 0) {
572e5b50fe7SAlan Somers /* zero nodeid means ENOENT and cache it */
573e5b50fe7SAlan Somers error = ENOENT;
574e5b50fe7SAlan Somers goto out;
575e5b50fe7SAlan Somers }
576e5b50fe7SAlan Somers
577e5b50fe7SAlan Somers vtyp = IFTOVT(feo->attr.mode);
578e5b50fe7SAlan Somers error = fuse_vnode_get(mp, feo, nodeid, NULL, vpp, NULL, vtyp);
579e5b50fe7SAlan Somers if (error)
580e5b50fe7SAlan Somers goto out;
581e5b50fe7SAlan Somers fvdat = VTOFUD(*vpp);
582e5b50fe7SAlan Somers
58313d593a5SAlan Somers if (timespeccmp(&now, &fvdat->last_local_modify, >)) {
58413d593a5SAlan Somers /*
58513d593a5SAlan Somers * Attributes from the server are definitely newer than the
58613d593a5SAlan Somers * last attributes we sent to the server, so cache them.
58713d593a5SAlan Somers */
5880269ae4cSAlan Somers fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
5895d94aaacSAlan Somers feo->attr_valid_nsec, NULL, true);
59013d593a5SAlan Somers }
5910d2bf489SAlan Somers fuse_validity_2_bintime(feo->entry_valid, feo->entry_valid_nsec,
5920d2bf489SAlan Somers &fvdat->entry_cache_timeout);
593e5b50fe7SAlan Somers out:
594e5b50fe7SAlan Somers fdisp_destroy(&fdi);
595e5b50fe7SAlan Somers return error;
596e5b50fe7SAlan Somers }
597e5b50fe7SAlan Somers
598e5b50fe7SAlan Somers static int
fuse_vfsop_root(struct mount * mp,int lkflags,struct vnode ** vpp)5995fe58019SAttilio Rao fuse_vfsop_root(struct mount *mp, int lkflags, struct vnode **vpp)
6005fe58019SAttilio Rao {
6015fe58019SAttilio Rao struct fuse_data *data = fuse_get_mpdata(mp);
6025fe58019SAttilio Rao int err = 0;
6035fe58019SAttilio Rao
6045fe58019SAttilio Rao if (data->vroot != NULL) {
605a92a971bSMateusz Guzik err = vget(data->vroot, lkflags);
6065fe58019SAttilio Rao if (err == 0)
6075fe58019SAttilio Rao *vpp = data->vroot;
6085fe58019SAttilio Rao } else {
60909176f09SConrad Meyer err = fuse_vnode_get(mp, NULL, FUSE_ROOT_ID, NULL, vpp, NULL,
61009176f09SConrad Meyer VDIR);
6115fe58019SAttilio Rao if (err == 0) {
6125fe58019SAttilio Rao FUSE_LOCK();
6135fe58019SAttilio Rao MPASS(data->vroot == NULL || data->vroot == *vpp);
6145fe58019SAttilio Rao if (data->vroot == NULL) {
615419e7ff6SAlan Somers SDT_PROBE2(fusefs, , vfsops, trace, 1,
616cf169498SAlan Somers "new root vnode");
6175fe58019SAttilio Rao data->vroot = *vpp;
6185fe58019SAttilio Rao FUSE_UNLOCK();
6195fe58019SAttilio Rao vref(*vpp);
6205fe58019SAttilio Rao } else if (data->vroot != *vpp) {
621419e7ff6SAlan Somers SDT_PROBE2(fusefs, , vfsops, trace, 1,
622cf169498SAlan Somers "root vnode race");
6235fe58019SAttilio Rao FUSE_UNLOCK();
624b935e867SMateusz Guzik vput(*vpp);
6255fe58019SAttilio Rao vrecycle(*vpp);
6265fe58019SAttilio Rao *vpp = data->vroot;
6275fe58019SAttilio Rao } else
6285fe58019SAttilio Rao FUSE_UNLOCK();
6295fe58019SAttilio Rao }
6305fe58019SAttilio Rao }
6315fe58019SAttilio Rao return err;
6325fe58019SAttilio Rao }
6335fe58019SAttilio Rao
6345fe58019SAttilio Rao static int
fuse_vfsop_statfs(struct mount * mp,struct statfs * sbp)6355fe58019SAttilio Rao fuse_vfsop_statfs(struct mount *mp, struct statfs *sbp)
6365fe58019SAttilio Rao {
6375fe58019SAttilio Rao struct fuse_dispatcher fdi;
6385fe58019SAttilio Rao int err = 0;
6395fe58019SAttilio Rao
6405fe58019SAttilio Rao struct fuse_statfs_out *fsfo;
6415fe58019SAttilio Rao struct fuse_data *data;
6425fe58019SAttilio Rao
6435fe58019SAttilio Rao data = fuse_get_mpdata(mp);
6445fe58019SAttilio Rao
6455fe58019SAttilio Rao if (!(data->dataflags & FSESS_INITED))
6465fe58019SAttilio Rao goto fake;
6475fe58019SAttilio Rao
6485fe58019SAttilio Rao fdisp_init(&fdi, 0);
6495fe58019SAttilio Rao fdisp_make(&fdi, FUSE_STATFS, mp, FUSE_ROOT_ID, NULL, NULL);
6505fe58019SAttilio Rao err = fdisp_wait_answ(&fdi);
6515fe58019SAttilio Rao if (err) {
6525fe58019SAttilio Rao fdisp_destroy(&fdi);
6535fe58019SAttilio Rao if (err == ENOTCONN) {
6545fe58019SAttilio Rao /*
6555fe58019SAttilio Rao * We want to seem a legitimate fs even if the daemon
6565fe58019SAttilio Rao * is stiff dead... (so that, eg., we can still do path
6575fe58019SAttilio Rao * based unmounting after the daemon dies).
6585fe58019SAttilio Rao */
6595fe58019SAttilio Rao goto fake;
6605fe58019SAttilio Rao }
6615fe58019SAttilio Rao return err;
6625fe58019SAttilio Rao }
6635fe58019SAttilio Rao fsfo = fdi.answ;
6645fe58019SAttilio Rao
6655fe58019SAttilio Rao sbp->f_blocks = fsfo->st.blocks;
6665fe58019SAttilio Rao sbp->f_bfree = fsfo->st.bfree;
6675fe58019SAttilio Rao sbp->f_bavail = fsfo->st.bavail;
6685fe58019SAttilio Rao sbp->f_files = fsfo->st.files;
6695fe58019SAttilio Rao sbp->f_ffree = fsfo->st.ffree; /* cast from uint64_t to int64_t */
6705fe58019SAttilio Rao sbp->f_namemax = fsfo->st.namelen;
6715fe58019SAttilio Rao sbp->f_bsize = fsfo->st.frsize; /* cast from uint32_t to uint64_t */
6725fe58019SAttilio Rao
6735fe58019SAttilio Rao fdisp_destroy(&fdi);
6745fe58019SAttilio Rao return 0;
6755fe58019SAttilio Rao
6765fe58019SAttilio Rao fake:
6775fe58019SAttilio Rao sbp->f_blocks = 0;
6785fe58019SAttilio Rao sbp->f_bfree = 0;
6795fe58019SAttilio Rao sbp->f_bavail = 0;
6805fe58019SAttilio Rao sbp->f_files = 0;
6815fe58019SAttilio Rao sbp->f_ffree = 0;
6825fe58019SAttilio Rao sbp->f_namemax = 0;
683fd2749f2SAlan Somers sbp->f_bsize = S_BLKSIZE;
6845fe58019SAttilio Rao
6855fe58019SAttilio Rao return 0;
6865fe58019SAttilio Rao }
687