fuse_vfsops.c revision 1.37
1/* $OpenBSD: fuse_vfsops.c,v 1.37 2018/05/20 03:06:50 helg 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	fusefs_mount,
55	fusefs_start,
56	fusefs_unmount,
57	fusefs_root,
58	fusefs_quotactl,
59	fusefs_statfs,
60	fusefs_sync,
61	fusefs_vget,
62	fusefs_fhtovp,
63	fusefs_vptofh,
64	fusefs_init,
65	fusefs_sysctl,
66	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	fmp = malloc(sizeof(*fmp), M_FUSEFS, M_WAITOK | M_ZERO);
102	fmp->mp = mp;
103	fmp->sess_init = PENDING;
104	fmp->dev = vp->v_rdev;
105	if (args->max_read > 0)
106		fmp->max_read = MIN(args->max_read, FUSEBUFMAXSIZE);
107	else
108		fmp->max_read = FUSEBUFMAXSIZE;
109
110	mp->mnt_data = fmp;
111	mp->mnt_flag |= MNT_LOCAL;
112	vfs_getnewfsid(mp);
113
114	memset(mp->mnt_stat.f_mntonname, 0, MNAMELEN);
115	strlcpy(mp->mnt_stat.f_mntonname, path, MNAMELEN);
116	memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN);
117	strlcpy(mp->mnt_stat.f_mntfromname, "fusefs", MNAMELEN);
118	memset(mp->mnt_stat.f_mntfromspec, 0, MNAMELEN);
119	strlcpy(mp->mnt_stat.f_mntfromspec, "fusefs", MNAMELEN);
120
121	fuse_device_set_fmp(fmp, 1);
122	fbuf = fb_setup(0, 0, FBT_INIT, p);
123
124	/* cannot tsleep on mount */
125	fuse_device_queue_fbuf(fmp->dev, fbuf);
126
127bad:
128	FRELE(fp, p);
129	return (error);
130}
131
132int
133fusefs_start(struct mount *mp, int flags, struct proc *p)
134{
135	return (0);
136}
137
138int
139fusefs_unmount(struct mount *mp, int mntflags, struct proc *p)
140{
141	struct fusefs_mnt *fmp;
142	struct fusebuf *fbuf;
143	int flags = 0;
144	int error;
145
146	fmp = VFSTOFUSEFS(mp);
147
148	if (mntflags & MNT_FORCE)
149		flags |= FORCECLOSE;
150
151	if ((error = vflush(mp, NULLVP, flags)))
152		return (error);
153
154	if (fmp->sess_init) {
155		fmp->sess_init = 0;
156		fbuf = fb_setup(0, 0, FBT_DESTROY, p);
157
158		error = fb_queue(fmp->dev, fbuf);
159
160		if (error)
161			printf("fusefs: error %d on destroy\n", error);
162
163		fb_delete(fbuf);
164	}
165
166	fuse_device_cleanup(fmp->dev, NULL);
167	fuse_device_set_fmp(fmp, 0);
168	free(fmp, M_FUSEFS, sizeof(*fmp));
169	mp->mnt_data = NULL;
170
171	return (0);
172}
173
174int
175fusefs_root(struct mount *mp, struct vnode **vpp)
176{
177	struct vnode *nvp;
178	int error;
179
180	if ((error = VFS_VGET(mp, FUSE_ROOTINO, &nvp)) != 0)
181		return (error);
182
183	nvp->v_type = VDIR;
184
185	*vpp = nvp;
186	return (0);
187}
188
189int
190fusefs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg,
191    struct proc *p)
192{
193	return (EOPNOTSUPP);
194}
195
196int
197fusefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
198{
199	struct fusefs_mnt *fmp;
200	struct fusebuf *fbuf;
201	int error;
202
203	fmp = VFSTOFUSEFS(mp);
204
205	copy_statfs_info(sbp, mp);
206
207	/*
208	 * Both FBT_INIT and FBT_STATFS are sent to the FUSE file system
209	 * daemon when it is mounted. However, the daemon is the process
210	 * that called mount(2) so to prevent a deadlock return dummy
211	 * values until the response to FBT_INIT init is received. All
212	 * other VFS syscalls are queued.
213	 */
214	if (!fmp->sess_init || fmp->sess_init == PENDING) {
215		sbp->f_bavail = 0;
216		sbp->f_bfree = 0;
217		sbp->f_blocks = 0;
218		sbp->f_ffree = 0;
219		sbp->f_favail = 0;
220		sbp->f_files = 0;
221		sbp->f_bsize = 0;
222		sbp->f_iosize = 0;
223		sbp->f_namemax = 0;
224	} else {
225		fbuf = fb_setup(0, FUSE_ROOTINO, FBT_STATFS, p);
226
227		error = fb_queue(fmp->dev, fbuf);
228
229		if (error) {
230			fb_delete(fbuf);
231			return (error);
232		}
233
234		sbp->f_bavail = fbuf->fb_stat.f_bavail;
235		sbp->f_bfree = fbuf->fb_stat.f_bfree;
236		sbp->f_blocks = fbuf->fb_stat.f_blocks;
237		sbp->f_files = fbuf->fb_stat.f_files;
238		sbp->f_ffree = fbuf->fb_stat.f_ffree;
239		sbp->f_favail = fbuf->fb_stat.f_favail;
240		sbp->f_bsize = fbuf->fb_stat.f_frsize;
241		sbp->f_iosize = fbuf->fb_stat.f_bsize;
242		sbp->f_namemax = fbuf->fb_stat.f_namemax;
243		fb_delete(fbuf);
244	}
245
246	return (0);
247}
248
249int
250fusefs_sync(struct mount *mp, int waitfor, int stall, struct ucred *cred,
251    struct proc *p)
252{
253	return (0);
254}
255
256int
257fusefs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
258{
259	struct fusefs_mnt *fmp;
260	struct fusefs_node *ip;
261	struct vnode *nvp;
262	int i;
263	int error;
264retry:
265	fmp = VFSTOFUSEFS(mp);
266	/*
267	 * check if vnode is in hash.
268	 */
269	if ((*vpp = ufs_ihashget(fmp->dev, ino)) != NULLVP)
270		return (0);
271
272	/*
273	 * if not create it
274	 */
275	if ((error = getnewvnode(VT_FUSEFS, mp, &fusefs_vops, &nvp)) != 0) {
276		printf("fusefs: getnewvnode error\n");
277		*vpp = NULLVP;
278		return (error);
279	}
280
281	ip = malloc(sizeof(*ip), M_FUSEFS, M_WAITOK | M_ZERO);
282	rrw_init_flags(&ip->ufs_ino.i_lock, "fuseinode",
283	    RWL_DUPOK | RWL_IS_VNODE);
284	nvp->v_data = ip;
285	ip->ufs_ino.i_vnode = nvp;
286	ip->ufs_ino.i_dev = fmp->dev;
287	ip->ufs_ino.i_number = ino;
288
289	for (i = 0; i < FUFH_MAXTYPE; i++)
290		ip->fufh[i].fh_type = FUFH_INVALID;
291
292	error = ufs_ihashins(&ip->ufs_ino);
293	if (error) {
294		vrele(nvp);
295
296		if (error == EEXIST)
297			goto retry;
298
299		return (error);
300	}
301
302	ip->ufs_ino.i_ump = (struct ufsmount *)fmp;
303
304	if (ino == FUSE_ROOTINO)
305		nvp->v_flag |= VROOT;
306
307	*vpp = nvp;
308
309	return (0);
310}
311
312int
313fusefs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
314{
315	return (EINVAL);
316}
317
318int
319fusefs_vptofh(struct vnode *vp, struct fid *fhp)
320{
321	return (EINVAL);
322}
323
324int
325fusefs_init(struct vfsconf *vfc)
326{
327	pool_init(&fusefs_fbuf_pool, sizeof(struct fusebuf), 0, 0, PR_WAITOK,
328	    "fmsg", NULL);
329
330	return (0);
331}
332
333int
334fusefs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
335    void *newp, size_t newlen, struct proc *p)
336{
337	extern int stat_fbufs_in, stat_fbufs_wait, stat_opened_fusedev;
338
339	/* all sysctl names at this level are terminal */
340	if (namelen != 1)
341		return (ENOTDIR);		/* overloaded */
342
343	switch (name[0]) {
344	case FUSEFS_OPENDEVS:
345		return (sysctl_rdint(oldp, oldlenp, newp,
346		    stat_opened_fusedev));
347	case FUSEFS_INFBUFS:
348		return (sysctl_rdint(oldp, oldlenp, newp, stat_fbufs_in));
349	case FUSEFS_WAITFBUFS:
350		return (sysctl_rdint(oldp, oldlenp, newp, stat_fbufs_wait));
351	case FUSEFS_POOL_NBPAGES:
352		return (sysctl_rdint(oldp, oldlenp, newp,
353		    fusefs_fbuf_pool.pr_npages));
354	default:
355		return (EOPNOTSUPP);
356	}
357}
358
359int
360fusefs_checkexp(struct mount *mp, struct mbuf *nam, int *extflagsp,
361    struct ucred **credanonp)
362{
363	return (EOPNOTSUPP);
364}
365