xref: /freebsd/sys/fs/fuse/fuse_vfsops.c (revision 969d1aa4dbfcbccd8de965f7761203208bf04e46)
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