xref: /openbsd-src/sys/miscfs/fuse/fuse_vfsops.c (revision 51d8761d1e63aff7173adb7b282dbb4d6279257d)
1*51d8761dSclaudio /* $OpenBSD: fuse_vfsops.c,v 1.48 2024/10/31 13:55:21 claudio Exp $ */
2a2badd06Stedu /*
3a2badd06Stedu  * Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com>
4a2badd06Stedu  *
5a2badd06Stedu  * Permission to use, copy, modify, and distribute this software for any
6a2badd06Stedu  * purpose with or without fee is hereby granted, provided that the above
7a2badd06Stedu  * copyright notice and this permission notice appear in all copies.
8a2badd06Stedu  *
9a2badd06Stedu  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10a2badd06Stedu  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11a2badd06Stedu  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12a2badd06Stedu  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13a2badd06Stedu  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14a2badd06Stedu  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15a2badd06Stedu  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16a2badd06Stedu  */
17a2badd06Stedu 
18a2badd06Stedu #include <sys/param.h>
19fde894e5Stedu #include <sys/systm.h>
20f76a189dSsyl #include <sys/file.h>
21f76a189dSsyl #include <sys/filedesc.h>
22a2badd06Stedu #include <sys/malloc.h>
23a2badd06Stedu #include <sys/mount.h>
24a2badd06Stedu #include <sys/pool.h>
25f76a189dSsyl #include <sys/proc.h>
26f76a189dSsyl #include <sys/specdev.h>
276f9b5942Snatano #include <sys/stat.h>
28a2badd06Stedu #include <sys/statvfs.h>
29a2badd06Stedu #include <sys/sysctl.h>
30a2badd06Stedu #include <sys/vnode.h>
31a2badd06Stedu #include <sys/fusebuf.h>
32a2badd06Stedu 
33a2badd06Stedu #include "fusefs_node.h"
34a2badd06Stedu #include "fusefs.h"
35a2badd06Stedu 
36a2badd06Stedu int	fusefs_mount(struct mount *, const char *, void *, struct nameidata *,
37a2badd06Stedu 	    struct proc *);
38a2badd06Stedu int	fusefs_start(struct mount *, int, struct proc *);
39a2badd06Stedu int	fusefs_unmount(struct mount *, int, struct proc *);
40a2badd06Stedu int	fusefs_root(struct mount *, struct vnode **);
41a2badd06Stedu int	fusefs_quotactl(struct mount *, int, uid_t, caddr_t, struct proc *);
42a2badd06Stedu int	fusefs_statfs(struct mount *, struct statfs *, struct proc *);
43976e9839Sderaadt int	fusefs_sync(struct mount *, int, int, struct ucred *, struct proc *);
44a2badd06Stedu int	fusefs_vget(struct mount *, ino_t, struct vnode **);
45a2badd06Stedu int	fusefs_fhtovp(struct mount *, struct fid *, struct vnode **);
46a2badd06Stedu int	fusefs_vptofh(struct vnode *, struct fid *);
47a2badd06Stedu int	fusefs_init(struct vfsconf *);
48a2badd06Stedu int	fusefs_sysctl(int *, u_int, void *, size_t *, void *, size_t,
49a2badd06Stedu 	    struct proc *);
50a2badd06Stedu int	fusefs_checkexp(struct mount *, struct mbuf *, int *,
51a2badd06Stedu 	    struct ucred **);
52a2badd06Stedu 
53a2badd06Stedu const struct vfsops fusefs_vfsops = {
5441f642fcSbluhm 	.vfs_mount	= fusefs_mount,
5541f642fcSbluhm 	.vfs_start	= fusefs_start,
5641f642fcSbluhm 	.vfs_unmount	= fusefs_unmount,
5741f642fcSbluhm 	.vfs_root	= fusefs_root,
5841f642fcSbluhm 	.vfs_quotactl	= fusefs_quotactl,
5941f642fcSbluhm 	.vfs_statfs	= fusefs_statfs,
6041f642fcSbluhm 	.vfs_sync	= fusefs_sync,
6141f642fcSbluhm 	.vfs_vget	= fusefs_vget,
6241f642fcSbluhm 	.vfs_fhtovp	= fusefs_fhtovp,
6341f642fcSbluhm 	.vfs_vptofh	= fusefs_vptofh,
6441f642fcSbluhm 	.vfs_init	= fusefs_init,
6541f642fcSbluhm 	.vfs_sysctl	= fusefs_sysctl,
6641f642fcSbluhm 	.vfs_checkexp	= fusefs_checkexp,
67a2badd06Stedu };
68a2badd06Stedu 
69a2badd06Stedu struct pool fusefs_fbuf_pool;
70a2badd06Stedu 
715b6d7437Shelg #define PENDING 2	/* FBT_INIT reply not yet received */
725b6d7437Shelg 
73a2badd06Stedu int
74a2badd06Stedu fusefs_mount(struct mount *mp, const char *path, void *data,
75a2badd06Stedu     struct nameidata *ndp, struct proc *p)
76a2badd06Stedu {
77a2badd06Stedu 	struct fusefs_mnt *fmp;
78a2badd06Stedu 	struct fusebuf *fbuf;
797efda1a1Sderaadt 	struct fusefs_args *args = data;
80f76a189dSsyl 	struct vnode *vp;
81f76a189dSsyl 	struct file *fp;
8253146562Smpi 	int error = 0;
83a2badd06Stedu 
84a2badd06Stedu 	if (mp->mnt_flag & MNT_UPDATE)
85a2badd06Stedu 		return (EOPNOTSUPP);
86a2badd06Stedu 
877efda1a1Sderaadt 	if ((fp = fd_getfile(p->p_fd, args->fd)) == NULL)
88f76a189dSsyl 		return (EBADF);
89f76a189dSsyl 
9053146562Smpi 	if (fp->f_type != DTYPE_VNODE) {
9153146562Smpi 		error = EINVAL;
9253146562Smpi 		goto bad;
9353146562Smpi 	}
94f76a189dSsyl 
95f76a189dSsyl 	vp = fp->f_data;
9653146562Smpi 	if (vp->v_type != VCHR) {
9753146562Smpi 		error = EBADF;
9853146562Smpi 		goto bad;
9953146562Smpi 	}
100f76a189dSsyl 
10163849fffSmpi 	/* Only root may specify allow_other. */
10263849fffSmpi 	if (args->allow_other && (error = suser_ucred(p->p_ucred)))
10363849fffSmpi 		goto bad;
10463849fffSmpi 
105a2badd06Stedu 	fmp = malloc(sizeof(*fmp), M_FUSEFS, M_WAITOK | M_ZERO);
106a2badd06Stedu 	fmp->mp = mp;
1075b6d7437Shelg 	fmp->sess_init = PENDING;
108f76a189dSsyl 	fmp->dev = vp->v_rdev;
1097efda1a1Sderaadt 	if (args->max_read > 0)
1107efda1a1Sderaadt 		fmp->max_read = MIN(args->max_read, FUSEBUFMAXSIZE);
1114a254543Ssyl 	else
1124a254543Ssyl 		fmp->max_read = FUSEBUFMAXSIZE;
113a2badd06Stedu 
1140f4d2db5Shelg 	fmp->allow_other = args->allow_other;
1150f4d2db5Shelg 
1164a254543Ssyl 	mp->mnt_data = fmp;
117a2badd06Stedu 	vfs_getnewfsid(mp);
118a2badd06Stedu 
119bb72b40bShelg 	memset(mp->mnt_stat.f_mntonname, 0, MNAMELEN);
120a2badd06Stedu 	strlcpy(mp->mnt_stat.f_mntonname, path, MNAMELEN);
121bb72b40bShelg 	memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN);
122f28dec03Snatano 	strlcpy(mp->mnt_stat.f_mntfromname, "fusefs", MNAMELEN);
123bb72b40bShelg 	memset(mp->mnt_stat.f_mntfromspec, 0, MNAMELEN);
124f28dec03Snatano 	strlcpy(mp->mnt_stat.f_mntfromspec, "fusefs", MNAMELEN);
125a2badd06Stedu 
12648ee6ddaSpelikan 	fuse_device_set_fmp(fmp, 1);
127a2badd06Stedu 	fbuf = fb_setup(0, 0, FBT_INIT, p);
128a2badd06Stedu 
129a2badd06Stedu 	/* cannot tsleep on mount */
130a2badd06Stedu 	fuse_device_queue_fbuf(fmp->dev, fbuf);
131a2badd06Stedu 
13253146562Smpi bad:
13353146562Smpi 	FRELE(fp, p);
13453146562Smpi 	return (error);
135a2badd06Stedu }
136a2badd06Stedu 
137a2badd06Stedu int
138a2badd06Stedu fusefs_start(struct mount *mp, int flags, struct proc *p)
139a2badd06Stedu {
140a2badd06Stedu 	return (0);
141a2badd06Stedu }
142a2badd06Stedu 
143a2badd06Stedu int
144a2badd06Stedu fusefs_unmount(struct mount *mp, int mntflags, struct proc *p)
145a2badd06Stedu {
146a2badd06Stedu 	struct fusefs_mnt *fmp;
147a2badd06Stedu 	struct fusebuf *fbuf;
148a2badd06Stedu 	int flags = 0;
149a2badd06Stedu 	int error;
150a2badd06Stedu 
151a2badd06Stedu 	fmp = VFSTOFUSEFS(mp);
152a2badd06Stedu 
153065726a5Snatano 	if (mntflags & MNT_FORCE)
15446cd8370Ssyl 		flags |= FORCECLOSE;
15546cd8370Ssyl 
15646cd8370Ssyl 	if ((error = vflush(mp, NULLVP, flags)))
15746cd8370Ssyl 		return (error);
15846cd8370Ssyl 
1593270c159Shelg 	if (fmp->sess_init && fmp->sess_init != PENDING) {
160a2badd06Stedu 		fbuf = fb_setup(0, 0, FBT_DESTROY, p);
161a2badd06Stedu 
162a2badd06Stedu 		error = fb_queue(fmp->dev, fbuf);
163a2badd06Stedu 
164a2badd06Stedu 		if (error)
16548ee6ddaSpelikan 			printf("fusefs: error %d on destroy\n", error);
16637318181Ssyl 
16737318181Ssyl 		fb_delete(fbuf);
168a2badd06Stedu 	}
1693270c159Shelg 	fmp->sess_init = 0;
170a2badd06Stedu 
171b7e69da4Shelg 	fuse_device_cleanup(fmp->dev);
17248ee6ddaSpelikan 	fuse_device_set_fmp(fmp, 0);
173903f970aShelg 	free(fmp, M_FUSEFS, sizeof(*fmp));
174200e77f4Sbluhm 	mp->mnt_data = NULL;
175a2badd06Stedu 
1764b1ae25eSbluhm 	return (0);
177a2badd06Stedu }
178a2badd06Stedu 
179a2badd06Stedu int
180a2badd06Stedu fusefs_root(struct mount *mp, struct vnode **vpp)
181a2badd06Stedu {
182a2badd06Stedu 	struct vnode *nvp;
183a2badd06Stedu 	int error;
184a2badd06Stedu 
185a1055de8Snatano 	if ((error = VFS_VGET(mp, FUSE_ROOTINO, &nvp)) != 0)
186a2badd06Stedu 		return (error);
187a2badd06Stedu 
188a2badd06Stedu 	nvp->v_type = VDIR;
189a2badd06Stedu 
190a2badd06Stedu 	*vpp = nvp;
191a2badd06Stedu 	return (0);
192a2badd06Stedu }
193a2badd06Stedu 
194e3d2e44cStedu int
195e3d2e44cStedu fusefs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg,
196a2badd06Stedu     struct proc *p)
197a2badd06Stedu {
1980181dae0Stedu 	return (EOPNOTSUPP);
199a2badd06Stedu }
200a2badd06Stedu 
201e3d2e44cStedu int
202e3d2e44cStedu fusefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
203a2badd06Stedu {
204a2badd06Stedu 	struct fusefs_mnt *fmp;
205a2badd06Stedu 	struct fusebuf *fbuf;
206a2badd06Stedu 	int error;
207a2badd06Stedu 
208a2badd06Stedu 	fmp = VFSTOFUSEFS(mp);
209a2badd06Stedu 
2100f4d2db5Shelg 	/* Deny other users unless allow_other mount option was specified. */
2110f4d2db5Shelg 	if (!fmp->allow_other && p->p_ucred->cr_uid != mp->mnt_stat.f_owner)
2120f4d2db5Shelg 		return (EPERM);
2130f4d2db5Shelg 
214f28dec03Snatano 	copy_statfs_info(sbp, mp);
215f28dec03Snatano 
2165b6d7437Shelg 	/*
2175b6d7437Shelg 	 * Both FBT_INIT and FBT_STATFS are sent to the FUSE file system
2185b6d7437Shelg 	 * daemon when it is mounted. However, the daemon is the process
2195b6d7437Shelg 	 * that called mount(2) so to prevent a deadlock return dummy
2205b6d7437Shelg 	 * values until the response to FBT_INIT init is received. All
2215b6d7437Shelg 	 * other VFS syscalls are queued.
2225b6d7437Shelg 	 */
2235b6d7437Shelg 	if (!fmp->sess_init || fmp->sess_init == PENDING) {
2245b6d7437Shelg 		sbp->f_bavail = 0;
2255b6d7437Shelg 		sbp->f_bfree = 0;
2265b6d7437Shelg 		sbp->f_blocks = 0;
2275b6d7437Shelg 		sbp->f_ffree = 0;
2285b6d7437Shelg 		sbp->f_favail = 0;
2295b6d7437Shelg 		sbp->f_files = 0;
2305b6d7437Shelg 		sbp->f_bsize = 0;
2315b6d7437Shelg 		sbp->f_iosize = 0;
2325b6d7437Shelg 		sbp->f_namemax = 0;
2335b6d7437Shelg 	} else {
234a1055de8Snatano 		fbuf = fb_setup(0, FUSE_ROOTINO, FBT_STATFS, p);
235a2badd06Stedu 
236a2badd06Stedu 		error = fb_queue(fmp->dev, fbuf);
237a2badd06Stedu 
238a2badd06Stedu 		if (error) {
23937318181Ssyl 			fb_delete(fbuf);
240a2badd06Stedu 			return (error);
241a2badd06Stedu 		}
242a2badd06Stedu 
243a2badd06Stedu 		sbp->f_bavail = fbuf->fb_stat.f_bavail;
244a2badd06Stedu 		sbp->f_bfree = fbuf->fb_stat.f_bfree;
245a2badd06Stedu 		sbp->f_blocks = fbuf->fb_stat.f_blocks;
246a2badd06Stedu 		sbp->f_files = fbuf->fb_stat.f_files;
247a2badd06Stedu 		sbp->f_ffree = fbuf->fb_stat.f_ffree;
248f28dec03Snatano 		sbp->f_favail = fbuf->fb_stat.f_favail;
249a2badd06Stedu 		sbp->f_bsize = fbuf->fb_stat.f_frsize;
250f28dec03Snatano 		sbp->f_iosize = fbuf->fb_stat.f_bsize;
251a2badd06Stedu 		sbp->f_namemax = fbuf->fb_stat.f_namemax;
25237318181Ssyl 		fb_delete(fbuf);
253a2badd06Stedu 	}
254a2badd06Stedu 
255a2badd06Stedu 	return (0);
256a2badd06Stedu }
257a2badd06Stedu 
258e3d2e44cStedu int
259976e9839Sderaadt fusefs_sync(struct mount *mp, int waitfor, int stall, struct ucred *cred,
260a2badd06Stedu     struct proc *p)
261a2badd06Stedu {
262a2badd06Stedu 	return (0);
263a2badd06Stedu }
264a2badd06Stedu 
265e3d2e44cStedu int
266e3d2e44cStedu fusefs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
267a2badd06Stedu {
2683c992bbfShelg 	struct vattr vattr;
269a2badd06Stedu 	struct fusefs_mnt *fmp;
270a2badd06Stedu 	struct fusefs_node *ip;
271a2badd06Stedu 	struct vnode *nvp;
272a2badd06Stedu 	int i;
273a2badd06Stedu 	int error;
274a2badd06Stedu retry:
275a2badd06Stedu 	fmp = VFSTOFUSEFS(mp);
276a2badd06Stedu 	/*
277a2badd06Stedu 	 * check if vnode is in hash.
278a2badd06Stedu 	 */
279*51d8761dSclaudio 	if ((*vpp = fuse_ihashget(fmp->dev, ino)) != NULLVP)
280a2badd06Stedu 		return (0);
281a2badd06Stedu 
282a2badd06Stedu 	/*
283a2badd06Stedu 	 * if not create it
284a2badd06Stedu 	 */
285a2badd06Stedu 	if ((error = getnewvnode(VT_FUSEFS, mp, &fusefs_vops, &nvp)) != 0) {
2864f926cddSsyl 		printf("fusefs: getnewvnode error\n");
287a2badd06Stedu 		*vpp = NULLVP;
288a2badd06Stedu 		return (error);
289a2badd06Stedu 	}
290a2badd06Stedu 
291a2badd06Stedu 	ip = malloc(sizeof(*ip), M_FUSEFS, M_WAITOK | M_ZERO);
292*51d8761dSclaudio 	rrw_init_flags(&ip->i_lock, "fuseinode",
293d78cb2ffSvisa 	    RWL_DUPOK | RWL_IS_VNODE);
294a2badd06Stedu 	nvp->v_data = ip;
295*51d8761dSclaudio 	ip->i_vnode = nvp;
296*51d8761dSclaudio 	ip->i_dev = fmp->dev;
297*51d8761dSclaudio 	ip->i_number = ino;
298a2badd06Stedu 
299a2badd06Stedu 	for (i = 0; i < FUFH_MAXTYPE; i++)
300a2badd06Stedu 		ip->fufh[i].fh_type = FUFH_INVALID;
301a2badd06Stedu 
302*51d8761dSclaudio 	error = fuse_ihashins(ip);
303a2badd06Stedu 	if (error) {
304a2badd06Stedu 		vrele(nvp);
305a2badd06Stedu 
306a2badd06Stedu 		if (error == EEXIST)
307a2badd06Stedu 			goto retry;
308a2badd06Stedu 
309a2badd06Stedu 		return (error);
310a2badd06Stedu 	}
311a2badd06Stedu 
312*51d8761dSclaudio 	ip->i_ump = fmp;
313a2badd06Stedu 
314a2badd06Stedu 	if (ino == FUSE_ROOTINO)
315a2badd06Stedu 		nvp->v_flag |= VROOT;
3163270c159Shelg 	else {
3173c992bbfShelg 		/*
3183c992bbfShelg 		 * Initialise the file size so that file size changes can be
3193c992bbfShelg 		 * detected during file operations.
3203c992bbfShelg 		 */
3213c992bbfShelg 		error = VOP_GETATTR(nvp, &vattr, curproc->p_ucred, curproc);
3223c992bbfShelg 		if (error) {
3233c992bbfShelg 			vrele(nvp);
3243c992bbfShelg 			return (error);
3253c992bbfShelg 		}
3263c992bbfShelg 		ip->filesize = vattr.va_size;
3273270c159Shelg 	}
3283c992bbfShelg 
329a2badd06Stedu 	*vpp = nvp;
330a2badd06Stedu 
331a2badd06Stedu 	return (0);
332a2badd06Stedu }
333a2badd06Stedu 
334e3d2e44cStedu int
335e3d2e44cStedu fusefs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
336a2badd06Stedu {
337514c5354Snatano 	return (EINVAL);
338a2badd06Stedu }
339a2badd06Stedu 
340e3d2e44cStedu int
341e3d2e44cStedu fusefs_vptofh(struct vnode *vp, struct fid *fhp)
342a2badd06Stedu {
343514c5354Snatano 	return (EINVAL);
344a2badd06Stedu }
345a2badd06Stedu 
346e3d2e44cStedu int
347e3d2e44cStedu fusefs_init(struct vfsconf *vfc)
348a2badd06Stedu {
3490c632275Stedu 	pool_init(&fusefs_fbuf_pool, sizeof(struct fusebuf), 0, 0, PR_WAITOK,
3500c632275Stedu 	    "fmsg", NULL);
351*51d8761dSclaudio 	fuse_ihashinit();
352a2badd06Stedu 
353a2badd06Stedu 	return (0);
354a2badd06Stedu }
355a2badd06Stedu 
3566e044e75Sgnezdo extern int stat_fbufs_in, stat_fbufs_wait, stat_opened_fusedev;
3576e044e75Sgnezdo 
3586e044e75Sgnezdo const struct sysctl_bounded_args fusefs_vars[] = {
3599c14f6a5Sgnezdo 	{ FUSEFS_OPENDEVS, &stat_opened_fusedev, SYSCTL_INT_READONLY },
3609c14f6a5Sgnezdo 	{ FUSEFS_INFBUFS, &stat_fbufs_in, SYSCTL_INT_READONLY },
3619c14f6a5Sgnezdo 	{ FUSEFS_WAITFBUFS, &stat_fbufs_wait, SYSCTL_INT_READONLY },
3629c14f6a5Sgnezdo 	{ FUSEFS_POOL_NBPAGES, &fusefs_fbuf_pool.pr_npages, SYSCTL_INT_READONLY },
3636e044e75Sgnezdo };
3646e044e75Sgnezdo 
365e3d2e44cStedu int
366e3d2e44cStedu fusefs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
367a2badd06Stedu     void *newp, size_t newlen, struct proc *p)
368a2badd06Stedu {
3696e044e75Sgnezdo 	return sysctl_bounded_arr(fusefs_vars, nitems(fusefs_vars), name,
3706e044e75Sgnezdo 	    namelen, oldp, oldlenp, newp, newlen);
371a2badd06Stedu }
372a2badd06Stedu 
373e3d2e44cStedu int
374e3d2e44cStedu fusefs_checkexp(struct mount *mp, struct mbuf *nam, int *extflagsp,
375a2badd06Stedu     struct ucred **credanonp)
376a2badd06Stedu {
3773d0b1bbcSnatano 	return (EOPNOTSUPP);
378a2badd06Stedu }
379