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