xref: /openbsd-src/sys/miscfs/fuse/fuse_vfsops.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /* $OpenBSD: fuse_vfsops.c,v 1.44 2020/11/17 03:23:10 gnezdo 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 	mp->mnt_flag |= MNT_LOCAL;
118 	vfs_getnewfsid(mp);
119 
120 	memset(mp->mnt_stat.f_mntonname, 0, MNAMELEN);
121 	strlcpy(mp->mnt_stat.f_mntonname, path, MNAMELEN);
122 	memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN);
123 	strlcpy(mp->mnt_stat.f_mntfromname, "fusefs", MNAMELEN);
124 	memset(mp->mnt_stat.f_mntfromspec, 0, MNAMELEN);
125 	strlcpy(mp->mnt_stat.f_mntfromspec, "fusefs", MNAMELEN);
126 
127 	fuse_device_set_fmp(fmp, 1);
128 	fbuf = fb_setup(0, 0, FBT_INIT, p);
129 
130 	/* cannot tsleep on mount */
131 	fuse_device_queue_fbuf(fmp->dev, fbuf);
132 
133 bad:
134 	FRELE(fp, p);
135 	return (error);
136 }
137 
138 int
139 fusefs_start(struct mount *mp, int flags, struct proc *p)
140 {
141 	return (0);
142 }
143 
144 int
145 fusefs_unmount(struct mount *mp, int mntflags, struct proc *p)
146 {
147 	struct fusefs_mnt *fmp;
148 	struct fusebuf *fbuf;
149 	int flags = 0;
150 	int error;
151 
152 	fmp = VFSTOFUSEFS(mp);
153 
154 	if (mntflags & MNT_FORCE)
155 		flags |= FORCECLOSE;
156 
157 	if ((error = vflush(mp, NULLVP, flags)))
158 		return (error);
159 
160 	if (fmp->sess_init && fmp->sess_init != PENDING) {
161 		fbuf = fb_setup(0, 0, FBT_DESTROY, p);
162 
163 		error = fb_queue(fmp->dev, fbuf);
164 
165 		if (error)
166 			printf("fusefs: error %d on destroy\n", error);
167 
168 		fb_delete(fbuf);
169 	}
170 	fmp->sess_init = 0;
171 
172 	fuse_device_cleanup(fmp->dev);
173 	fuse_device_set_fmp(fmp, 0);
174 	free(fmp, M_FUSEFS, sizeof(*fmp));
175 	mp->mnt_data = NULL;
176 
177 	return (0);
178 }
179 
180 int
181 fusefs_root(struct mount *mp, struct vnode **vpp)
182 {
183 	struct vnode *nvp;
184 	int error;
185 
186 	if ((error = VFS_VGET(mp, FUSE_ROOTINO, &nvp)) != 0)
187 		return (error);
188 
189 	nvp->v_type = VDIR;
190 
191 	*vpp = nvp;
192 	return (0);
193 }
194 
195 int
196 fusefs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg,
197     struct proc *p)
198 {
199 	return (EOPNOTSUPP);
200 }
201 
202 int
203 fusefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
204 {
205 	struct fusefs_mnt *fmp;
206 	struct fusebuf *fbuf;
207 	int error;
208 
209 	fmp = VFSTOFUSEFS(mp);
210 
211 	/* Deny other users unless allow_other mount option was specified. */
212 	if (!fmp->allow_other && p->p_ucred->cr_uid != mp->mnt_stat.f_owner)
213 		return (EPERM);
214 
215 	copy_statfs_info(sbp, mp);
216 
217 	/*
218 	 * Both FBT_INIT and FBT_STATFS are sent to the FUSE file system
219 	 * daemon when it is mounted. However, the daemon is the process
220 	 * that called mount(2) so to prevent a deadlock return dummy
221 	 * values until the response to FBT_INIT init is received. All
222 	 * other VFS syscalls are queued.
223 	 */
224 	if (!fmp->sess_init || fmp->sess_init == PENDING) {
225 		sbp->f_bavail = 0;
226 		sbp->f_bfree = 0;
227 		sbp->f_blocks = 0;
228 		sbp->f_ffree = 0;
229 		sbp->f_favail = 0;
230 		sbp->f_files = 0;
231 		sbp->f_bsize = 0;
232 		sbp->f_iosize = 0;
233 		sbp->f_namemax = 0;
234 	} else {
235 		fbuf = fb_setup(0, FUSE_ROOTINO, FBT_STATFS, p);
236 
237 		error = fb_queue(fmp->dev, fbuf);
238 
239 		if (error) {
240 			fb_delete(fbuf);
241 			return (error);
242 		}
243 
244 		sbp->f_bavail = fbuf->fb_stat.f_bavail;
245 		sbp->f_bfree = fbuf->fb_stat.f_bfree;
246 		sbp->f_blocks = fbuf->fb_stat.f_blocks;
247 		sbp->f_files = fbuf->fb_stat.f_files;
248 		sbp->f_ffree = fbuf->fb_stat.f_ffree;
249 		sbp->f_favail = fbuf->fb_stat.f_favail;
250 		sbp->f_bsize = fbuf->fb_stat.f_frsize;
251 		sbp->f_iosize = fbuf->fb_stat.f_bsize;
252 		sbp->f_namemax = fbuf->fb_stat.f_namemax;
253 		fb_delete(fbuf);
254 	}
255 
256 	return (0);
257 }
258 
259 int
260 fusefs_sync(struct mount *mp, int waitfor, int stall, struct ucred *cred,
261     struct proc *p)
262 {
263 	return (0);
264 }
265 
266 int
267 fusefs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
268 {
269 	struct vattr vattr;
270 	struct fusefs_mnt *fmp;
271 	struct fusefs_node *ip;
272 	struct vnode *nvp;
273 	int i;
274 	int error;
275 retry:
276 	fmp = VFSTOFUSEFS(mp);
277 	/*
278 	 * check if vnode is in hash.
279 	 */
280 	if ((*vpp = ufs_ihashget(fmp->dev, ino)) != NULLVP)
281 		return (0);
282 
283 	/*
284 	 * if not create it
285 	 */
286 	if ((error = getnewvnode(VT_FUSEFS, mp, &fusefs_vops, &nvp)) != 0) {
287 		printf("fusefs: getnewvnode error\n");
288 		*vpp = NULLVP;
289 		return (error);
290 	}
291 
292 	ip = malloc(sizeof(*ip), M_FUSEFS, M_WAITOK | M_ZERO);
293 	rrw_init_flags(&ip->ufs_ino.i_lock, "fuseinode",
294 	    RWL_DUPOK | RWL_IS_VNODE);
295 	nvp->v_data = ip;
296 	ip->ufs_ino.i_vnode = nvp;
297 	ip->ufs_ino.i_dev = fmp->dev;
298 	ip->ufs_ino.i_number = ino;
299 
300 	for (i = 0; i < FUFH_MAXTYPE; i++)
301 		ip->fufh[i].fh_type = FUFH_INVALID;
302 
303 	error = ufs_ihashins(&ip->ufs_ino);
304 	if (error) {
305 		vrele(nvp);
306 
307 		if (error == EEXIST)
308 			goto retry;
309 
310 		return (error);
311 	}
312 
313 	ip->ufs_ino.i_ump = (struct ufsmount *)fmp;
314 
315 	if (ino == FUSE_ROOTINO)
316 		nvp->v_flag |= VROOT;
317 	else {
318 		/*
319 		 * Initialise the file size so that file size changes can be
320 		 * detected during file operations.
321 		 */
322 		error = VOP_GETATTR(nvp, &vattr, curproc->p_ucred, curproc);
323 		if (error) {
324 			vrele(nvp);
325 			return (error);
326 		}
327 		ip->filesize = vattr.va_size;
328 	}
329 
330 	*vpp = nvp;
331 
332 	return (0);
333 }
334 
335 int
336 fusefs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
337 {
338 	return (EINVAL);
339 }
340 
341 int
342 fusefs_vptofh(struct vnode *vp, struct fid *fhp)
343 {
344 	return (EINVAL);
345 }
346 
347 int
348 fusefs_init(struct vfsconf *vfc)
349 {
350 	pool_init(&fusefs_fbuf_pool, sizeof(struct fusebuf), 0, 0, PR_WAITOK,
351 	    "fmsg", NULL);
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, 1, 0 },
360 	{ FUSEFS_INFBUFS, &stat_fbufs_in, 1, 0 },
361 	{ FUSEFS_WAITFBUFS, &stat_fbufs_wait, 1, 0 },
362 	{ FUSEFS_POOL_NBPAGES, &fusefs_fbuf_pool.pr_npages, 1, 0 },
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