xref: /openbsd-src/sys/miscfs/fuse/fuse_vfsops.c (revision 51d8761d1e63aff7173adb7b282dbb4d6279257d)
1 /* $OpenBSD: fuse_vfsops.c,v 1.48 2024/10/31 13:55:21 claudio Exp $ */
2 /*
3  * Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/file.h>
21 #include <sys/filedesc.h>
22 #include <sys/malloc.h>
23 #include <sys/mount.h>
24 #include <sys/pool.h>
25 #include <sys/proc.h>
26 #include <sys/specdev.h>
27 #include <sys/stat.h>
28 #include <sys/statvfs.h>
29 #include <sys/sysctl.h>
30 #include <sys/vnode.h>
31 #include <sys/fusebuf.h>
32 
33 #include "fusefs_node.h"
34 #include "fusefs.h"
35 
36 int	fusefs_mount(struct mount *, const char *, void *, struct nameidata *,
37 	    struct proc *);
38 int	fusefs_start(struct mount *, int, struct proc *);
39 int	fusefs_unmount(struct mount *, int, struct proc *);
40 int	fusefs_root(struct mount *, struct vnode **);
41 int	fusefs_quotactl(struct mount *, int, uid_t, caddr_t, struct proc *);
42 int	fusefs_statfs(struct mount *, struct statfs *, struct proc *);
43 int	fusefs_sync(struct mount *, int, int, struct ucred *, struct proc *);
44 int	fusefs_vget(struct mount *, ino_t, struct vnode **);
45 int	fusefs_fhtovp(struct mount *, struct fid *, struct vnode **);
46 int	fusefs_vptofh(struct vnode *, struct fid *);
47 int	fusefs_init(struct vfsconf *);
48 int	fusefs_sysctl(int *, u_int, void *, size_t *, void *, size_t,
49 	    struct proc *);
50 int	fusefs_checkexp(struct mount *, struct mbuf *, int *,
51 	    struct ucred **);
52 
53 const struct vfsops fusefs_vfsops = {
54 	.vfs_mount	= fusefs_mount,
55 	.vfs_start	= fusefs_start,
56 	.vfs_unmount	= fusefs_unmount,
57 	.vfs_root	= fusefs_root,
58 	.vfs_quotactl	= fusefs_quotactl,
59 	.vfs_statfs	= fusefs_statfs,
60 	.vfs_sync	= fusefs_sync,
61 	.vfs_vget	= fusefs_vget,
62 	.vfs_fhtovp	= fusefs_fhtovp,
63 	.vfs_vptofh	= fusefs_vptofh,
64 	.vfs_init	= fusefs_init,
65 	.vfs_sysctl	= fusefs_sysctl,
66 	.vfs_checkexp	= fusefs_checkexp,
67 };
68 
69 struct pool fusefs_fbuf_pool;
70 
71 #define PENDING 2	/* FBT_INIT reply not yet received */
72 
73 int
74 fusefs_mount(struct mount *mp, const char *path, void *data,
75     struct nameidata *ndp, struct proc *p)
76 {
77 	struct fusefs_mnt *fmp;
78 	struct fusebuf *fbuf;
79 	struct fusefs_args *args = data;
80 	struct vnode *vp;
81 	struct file *fp;
82 	int error = 0;
83 
84 	if (mp->mnt_flag & MNT_UPDATE)
85 		return (EOPNOTSUPP);
86 
87 	if ((fp = fd_getfile(p->p_fd, args->fd)) == NULL)
88 		return (EBADF);
89 
90 	if (fp->f_type != DTYPE_VNODE) {
91 		error = EINVAL;
92 		goto bad;
93 	}
94 
95 	vp = fp->f_data;
96 	if (vp->v_type != VCHR) {
97 		error = EBADF;
98 		goto bad;
99 	}
100 
101 	/* Only root may specify allow_other. */
102 	if (args->allow_other && (error = suser_ucred(p->p_ucred)))
103 		goto bad;
104 
105 	fmp = malloc(sizeof(*fmp), M_FUSEFS, M_WAITOK | M_ZERO);
106 	fmp->mp = mp;
107 	fmp->sess_init = PENDING;
108 	fmp->dev = vp->v_rdev;
109 	if (args->max_read > 0)
110 		fmp->max_read = MIN(args->max_read, FUSEBUFMAXSIZE);
111 	else
112 		fmp->max_read = FUSEBUFMAXSIZE;
113 
114 	fmp->allow_other = args->allow_other;
115 
116 	mp->mnt_data = fmp;
117 	vfs_getnewfsid(mp);
118 
119 	memset(mp->mnt_stat.f_mntonname, 0, MNAMELEN);
120 	strlcpy(mp->mnt_stat.f_mntonname, path, MNAMELEN);
121 	memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN);
122 	strlcpy(mp->mnt_stat.f_mntfromname, "fusefs", MNAMELEN);
123 	memset(mp->mnt_stat.f_mntfromspec, 0, MNAMELEN);
124 	strlcpy(mp->mnt_stat.f_mntfromspec, "fusefs", MNAMELEN);
125 
126 	fuse_device_set_fmp(fmp, 1);
127 	fbuf = fb_setup(0, 0, FBT_INIT, p);
128 
129 	/* cannot tsleep on mount */
130 	fuse_device_queue_fbuf(fmp->dev, fbuf);
131 
132 bad:
133 	FRELE(fp, p);
134 	return (error);
135 }
136 
137 int
138 fusefs_start(struct mount *mp, int flags, struct proc *p)
139 {
140 	return (0);
141 }
142 
143 int
144 fusefs_unmount(struct mount *mp, int mntflags, struct proc *p)
145 {
146 	struct fusefs_mnt *fmp;
147 	struct fusebuf *fbuf;
148 	int flags = 0;
149 	int error;
150 
151 	fmp = VFSTOFUSEFS(mp);
152 
153 	if (mntflags & MNT_FORCE)
154 		flags |= FORCECLOSE;
155 
156 	if ((error = vflush(mp, NULLVP, flags)))
157 		return (error);
158 
159 	if (fmp->sess_init && fmp->sess_init != PENDING) {
160 		fbuf = fb_setup(0, 0, FBT_DESTROY, p);
161 
162 		error = fb_queue(fmp->dev, fbuf);
163 
164 		if (error)
165 			printf("fusefs: error %d on destroy\n", error);
166 
167 		fb_delete(fbuf);
168 	}
169 	fmp->sess_init = 0;
170 
171 	fuse_device_cleanup(fmp->dev);
172 	fuse_device_set_fmp(fmp, 0);
173 	free(fmp, M_FUSEFS, sizeof(*fmp));
174 	mp->mnt_data = NULL;
175 
176 	return (0);
177 }
178 
179 int
180 fusefs_root(struct mount *mp, struct vnode **vpp)
181 {
182 	struct vnode *nvp;
183 	int error;
184 
185 	if ((error = VFS_VGET(mp, FUSE_ROOTINO, &nvp)) != 0)
186 		return (error);
187 
188 	nvp->v_type = VDIR;
189 
190 	*vpp = nvp;
191 	return (0);
192 }
193 
194 int
195 fusefs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg,
196     struct proc *p)
197 {
198 	return (EOPNOTSUPP);
199 }
200 
201 int
202 fusefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
203 {
204 	struct fusefs_mnt *fmp;
205 	struct fusebuf *fbuf;
206 	int error;
207 
208 	fmp = VFSTOFUSEFS(mp);
209 
210 	/* Deny other users unless allow_other mount option was specified. */
211 	if (!fmp->allow_other && p->p_ucred->cr_uid != mp->mnt_stat.f_owner)
212 		return (EPERM);
213 
214 	copy_statfs_info(sbp, mp);
215 
216 	/*
217 	 * Both FBT_INIT and FBT_STATFS are sent to the FUSE file system
218 	 * daemon when it is mounted. However, the daemon is the process
219 	 * that called mount(2) so to prevent a deadlock return dummy
220 	 * values until the response to FBT_INIT init is received. All
221 	 * other VFS syscalls are queued.
222 	 */
223 	if (!fmp->sess_init || fmp->sess_init == PENDING) {
224 		sbp->f_bavail = 0;
225 		sbp->f_bfree = 0;
226 		sbp->f_blocks = 0;
227 		sbp->f_ffree = 0;
228 		sbp->f_favail = 0;
229 		sbp->f_files = 0;
230 		sbp->f_bsize = 0;
231 		sbp->f_iosize = 0;
232 		sbp->f_namemax = 0;
233 	} else {
234 		fbuf = fb_setup(0, FUSE_ROOTINO, FBT_STATFS, p);
235 
236 		error = fb_queue(fmp->dev, fbuf);
237 
238 		if (error) {
239 			fb_delete(fbuf);
240 			return (error);
241 		}
242 
243 		sbp->f_bavail = fbuf->fb_stat.f_bavail;
244 		sbp->f_bfree = fbuf->fb_stat.f_bfree;
245 		sbp->f_blocks = fbuf->fb_stat.f_blocks;
246 		sbp->f_files = fbuf->fb_stat.f_files;
247 		sbp->f_ffree = fbuf->fb_stat.f_ffree;
248 		sbp->f_favail = fbuf->fb_stat.f_favail;
249 		sbp->f_bsize = fbuf->fb_stat.f_frsize;
250 		sbp->f_iosize = fbuf->fb_stat.f_bsize;
251 		sbp->f_namemax = fbuf->fb_stat.f_namemax;
252 		fb_delete(fbuf);
253 	}
254 
255 	return (0);
256 }
257 
258 int
259 fusefs_sync(struct mount *mp, int waitfor, int stall, struct ucred *cred,
260     struct proc *p)
261 {
262 	return (0);
263 }
264 
265 int
266 fusefs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
267 {
268 	struct vattr vattr;
269 	struct fusefs_mnt *fmp;
270 	struct fusefs_node *ip;
271 	struct vnode *nvp;
272 	int i;
273 	int error;
274 retry:
275 	fmp = VFSTOFUSEFS(mp);
276 	/*
277 	 * check if vnode is in hash.
278 	 */
279 	if ((*vpp = fuse_ihashget(fmp->dev, ino)) != NULLVP)
280 		return (0);
281 
282 	/*
283 	 * if not create it
284 	 */
285 	if ((error = getnewvnode(VT_FUSEFS, mp, &fusefs_vops, &nvp)) != 0) {
286 		printf("fusefs: getnewvnode error\n");
287 		*vpp = NULLVP;
288 		return (error);
289 	}
290 
291 	ip = malloc(sizeof(*ip), M_FUSEFS, M_WAITOK | M_ZERO);
292 	rrw_init_flags(&ip->i_lock, "fuseinode",
293 	    RWL_DUPOK | RWL_IS_VNODE);
294 	nvp->v_data = ip;
295 	ip->i_vnode = nvp;
296 	ip->i_dev = fmp->dev;
297 	ip->i_number = ino;
298 
299 	for (i = 0; i < FUFH_MAXTYPE; i++)
300 		ip->fufh[i].fh_type = FUFH_INVALID;
301 
302 	error = fuse_ihashins(ip);
303 	if (error) {
304 		vrele(nvp);
305 
306 		if (error == EEXIST)
307 			goto retry;
308 
309 		return (error);
310 	}
311 
312 	ip->i_ump = fmp;
313 
314 	if (ino == FUSE_ROOTINO)
315 		nvp->v_flag |= VROOT;
316 	else {
317 		/*
318 		 * Initialise the file size so that file size changes can be
319 		 * detected during file operations.
320 		 */
321 		error = VOP_GETATTR(nvp, &vattr, curproc->p_ucred, curproc);
322 		if (error) {
323 			vrele(nvp);
324 			return (error);
325 		}
326 		ip->filesize = vattr.va_size;
327 	}
328 
329 	*vpp = nvp;
330 
331 	return (0);
332 }
333 
334 int
335 fusefs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
336 {
337 	return (EINVAL);
338 }
339 
340 int
341 fusefs_vptofh(struct vnode *vp, struct fid *fhp)
342 {
343 	return (EINVAL);
344 }
345 
346 int
347 fusefs_init(struct vfsconf *vfc)
348 {
349 	pool_init(&fusefs_fbuf_pool, sizeof(struct fusebuf), 0, 0, PR_WAITOK,
350 	    "fmsg", NULL);
351 	fuse_ihashinit();
352 
353 	return (0);
354 }
355 
356 extern int stat_fbufs_in, stat_fbufs_wait, stat_opened_fusedev;
357 
358 const struct sysctl_bounded_args fusefs_vars[] = {
359 	{ FUSEFS_OPENDEVS, &stat_opened_fusedev, SYSCTL_INT_READONLY },
360 	{ FUSEFS_INFBUFS, &stat_fbufs_in, SYSCTL_INT_READONLY },
361 	{ FUSEFS_WAITFBUFS, &stat_fbufs_wait, SYSCTL_INT_READONLY },
362 	{ FUSEFS_POOL_NBPAGES, &fusefs_fbuf_pool.pr_npages, SYSCTL_INT_READONLY },
363 };
364 
365 int
366 fusefs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
367     void *newp, size_t newlen, struct proc *p)
368 {
369 	return sysctl_bounded_arr(fusefs_vars, nitems(fusefs_vars), name,
370 	    namelen, oldp, oldlenp, newp, newlen);
371 }
372 
373 int
374 fusefs_checkexp(struct mount *mp, struct mbuf *nam, int *extflagsp,
375     struct ucred **credanonp)
376 {
377 	return (EOPNOTSUPP);
378 }
379