fuse_vfsops.c revision 1.21
1/* $OpenBSD: fuse_vfsops.c,v 1.21 2016/04/26 18:37:02 natano 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/statvfs.h>
28#include <sys/sysctl.h>
29#include <sys/vnode.h>
30#include <sys/fusebuf.h>
31
32#include "fusefs_node.h"
33#include "fusefs.h"
34
35int	fusefs_mount(struct mount *, const char *, void *, struct nameidata *,
36	    struct proc *);
37int	fusefs_start(struct mount *, int, struct proc *);
38int	fusefs_unmount(struct mount *, int, struct proc *);
39int	fusefs_root(struct mount *, struct vnode **);
40int	fusefs_quotactl(struct mount *, int, uid_t, caddr_t, struct proc *);
41int	fusefs_statfs(struct mount *, struct statfs *, struct proc *);
42int	fusefs_sync(struct mount *, int, struct ucred *, struct proc *);
43int	fusefs_vget(struct mount *, ino_t, struct vnode **);
44int	fusefs_fhtovp(struct mount *, struct fid *, struct vnode **);
45int	fusefs_vptofh(struct vnode *, struct fid *);
46int	fusefs_init(struct vfsconf *);
47int	fusefs_sysctl(int *, u_int, void *, size_t *, void *, size_t,
48	    struct proc *);
49int	fusefs_checkexp(struct mount *, struct mbuf *, int *,
50	    struct ucred **);
51
52const struct vfsops fusefs_vfsops = {
53	fusefs_mount,
54	fusefs_start,
55	fusefs_unmount,
56	fusefs_root,
57	fusefs_quotactl,
58	fusefs_statfs,
59	fusefs_sync,
60	fusefs_vget,
61	fusefs_fhtovp,
62	fusefs_vptofh,
63	fusefs_init,
64	fusefs_sysctl,
65	fusefs_checkexp
66};
67
68struct pool fusefs_fbuf_pool;
69
70int
71fusefs_mount(struct mount *mp, const char *path, void *data,
72    struct nameidata *ndp, struct proc *p)
73{
74	struct fusefs_mnt *fmp;
75	struct fusebuf *fbuf;
76	struct fusefs_args args;
77	struct vnode *vp;
78	struct file *fp;
79	int error;
80
81	if (mp->mnt_flag & MNT_UPDATE)
82		return (EOPNOTSUPP);
83
84	error = copyin(data, &args, sizeof(struct fusefs_args));
85	if (error)
86		return (error);
87
88	if ((fp = fd_getfile(p->p_fd, args.fd)) == NULL)
89		return (EBADF);
90
91	if (fp->f_type != DTYPE_VNODE)
92		return (EINVAL);
93
94	vp = fp->f_data;
95	if (vp->v_type != VCHR)
96		return (EBADF);
97
98	fmp = malloc(sizeof(*fmp), M_FUSEFS, M_WAITOK | M_ZERO);
99	fmp->mp = mp;
100	fmp->sess_init = 0;
101	fmp->dev = vp->v_rdev;
102	if (args.max_read > 0)
103		fmp->max_read = MIN(args.max_read, FUSEBUFMAXSIZE);
104	else
105		fmp->max_read = FUSEBUFMAXSIZE;
106
107	mp->mnt_data = fmp;
108	mp->mnt_flag |= MNT_LOCAL;
109	vfs_getnewfsid(mp);
110
111	bzero(mp->mnt_stat.f_mntonname, MNAMELEN);
112	strlcpy(mp->mnt_stat.f_mntonname, path, MNAMELEN);
113	bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
114	strlcpy(mp->mnt_stat.f_mntfromname, "fusefs", MNAMELEN);
115	bzero(mp->mnt_stat.f_mntfromspec, MNAMELEN);
116	strlcpy(mp->mnt_stat.f_mntfromspec, "fusefs", MNAMELEN);
117
118	fuse_device_set_fmp(fmp, 1);
119	fbuf = fb_setup(0, 0, FBT_INIT, p);
120
121	/* cannot tsleep on mount */
122	fuse_device_queue_fbuf(fmp->dev, fbuf);
123
124	return (0);
125}
126
127int
128fusefs_start(struct mount *mp, int flags, struct proc *p)
129{
130	return (0);
131}
132
133int
134fusefs_unmount(struct mount *mp, int mntflags, struct proc *p)
135{
136	struct fusefs_mnt *fmp;
137	struct fusebuf *fbuf;
138	extern int doforce;
139	int flags = 0;
140	int error;
141
142	fmp = VFSTOFUSEFS(mp);
143
144	if (mntflags & MNT_FORCE) {
145		/* fusefs can never be rootfs so don't check for it */
146		if (!doforce)
147			return (EINVAL);
148
149		flags |= FORCECLOSE;
150	}
151
152	if ((error = vflush(mp, NULLVP, flags)))
153		return (error);
154
155	if (fmp->sess_init) {
156		fmp->sess_init = 0;
157		fbuf = fb_setup(0, 0, FBT_DESTROY, p);
158
159		error = fb_queue(fmp->dev, fbuf);
160
161		if (error)
162			printf("fusefs: error %d on destroy\n", error);
163
164		fb_delete(fbuf);
165	}
166
167	fuse_device_cleanup(fmp->dev, NULL);
168	fuse_device_set_fmp(fmp, 0);
169	free(fmp, M_FUSEFS, 0);
170	mp->mnt_data = NULL;
171
172	return (0);
173}
174
175int
176fusefs_root(struct mount *mp, struct vnode **vpp)
177{
178	struct vnode *nvp;
179	struct fusefs_node *ip;
180	int error;
181
182	if ((error = VFS_VGET(mp, (ino_t)FUSE_ROOTINO, &nvp)) != 0)
183		return (error);
184
185	ip = VTOI(nvp);
186	nvp->v_type = VDIR;
187	ip->vtype = VDIR;
188
189	*vpp = nvp;
190	return (0);
191}
192
193int
194fusefs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg,
195    struct proc *p)
196{
197	return (EOPNOTSUPP);
198}
199
200int
201fusefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
202{
203	struct fusefs_mnt *fmp;
204	struct fusebuf *fbuf;
205	int error;
206
207	fmp = VFSTOFUSEFS(mp);
208
209	copy_statfs_info(sbp, mp);
210
211	if (fmp->sess_init) {
212		fbuf = fb_setup(0, FUSE_ROOT_ID, FBT_STATFS, p);
213
214		error = fb_queue(fmp->dev, fbuf);
215
216		if (error) {
217			fb_delete(fbuf);
218			return (error);
219		}
220
221		sbp->f_bavail = fbuf->fb_stat.f_bavail;
222		sbp->f_bfree = fbuf->fb_stat.f_bfree;
223		sbp->f_blocks = fbuf->fb_stat.f_blocks;
224		sbp->f_files = fbuf->fb_stat.f_files;
225		sbp->f_ffree = fbuf->fb_stat.f_ffree;
226		sbp->f_favail = fbuf->fb_stat.f_favail;
227		sbp->f_bsize = fbuf->fb_stat.f_frsize;
228		sbp->f_iosize = fbuf->fb_stat.f_bsize;
229		sbp->f_namemax = fbuf->fb_stat.f_namemax;
230		fb_delete(fbuf);
231	} else {
232		sbp->f_bavail = 0;
233		sbp->f_bfree = 0;
234		sbp->f_blocks = 0;
235		sbp->f_ffree = 0;
236		sbp->f_favail = 0;
237		sbp->f_files = 0;
238		sbp->f_bsize = 0;
239		sbp->f_iosize = 0;
240		sbp->f_namemax = 0;
241	}
242
243	return (0);
244}
245
246int
247fusefs_sync(struct mount *mp, int waitfor, struct ucred *cred,
248    struct proc *p)
249{
250	return (0);
251}
252
253int
254fusefs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
255{
256	struct fusefs_mnt *fmp;
257	struct fusefs_node *ip;
258	struct vnode *nvp;
259	int i;
260	int error;
261retry:
262	fmp = VFSTOFUSEFS(mp);
263	/*
264	 * check if vnode is in hash.
265	 */
266	if ((*vpp = ufs_ihashget(fmp->dev, ino)) != NULLVP)
267		return (0);
268
269	/*
270	 * if not create it
271	 */
272	if ((error = getnewvnode(VT_FUSEFS, mp, &fusefs_vops, &nvp)) != 0) {
273		printf("fusefs: getnewvnode error\n");
274		*vpp = NULLVP;
275		return (error);
276	}
277
278	ip = malloc(sizeof(*ip), M_FUSEFS, M_WAITOK | M_ZERO);
279	lockinit(&ip->ufs_ino.i_lock, PINOD, "fuseinode", 0, 0);
280	nvp->v_data = ip;
281	ip->ufs_ino.i_vnode = nvp;
282	ip->ufs_ino.i_dev = fmp->dev;
283	ip->ufs_ino.i_number = ino;
284	ip->parent = 0;
285
286	for (i = 0; i < FUFH_MAXTYPE; i++)
287		ip->fufh[i].fh_type = FUFH_INVALID;
288
289	error = ufs_ihashins(&ip->ufs_ino);
290	if (error) {
291		vrele(nvp);
292
293		if (error == EEXIST)
294			goto retry;
295
296		return (error);
297	}
298
299	ip->ufs_ino.i_ump = (struct ufsmount *)fmp;
300
301	if (ino == FUSE_ROOTINO)
302		nvp->v_flag |= VROOT;
303
304	*vpp = nvp;
305
306	return (0);
307}
308
309int
310fusefs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
311{
312	struct ufid *ufhp;
313
314	ufhp = (struct ufid *)fhp;
315	if (ufhp->ufid_len != sizeof(struct ufid) ||
316	    ufhp->ufid_ino < FUSE_ROOTINO)
317		return (ESTALE);
318
319	return (VFS_VGET(mp, ufhp->ufid_ino, vpp));
320}
321
322int
323fusefs_vptofh(struct vnode *vp, struct fid *fhp)
324{
325	struct fusefs_node *ip;
326	struct ufid *ufhp;
327
328	ip = VTOI(vp);
329	ufhp = (struct ufid *)fhp;
330	ufhp->ufid_len = sizeof(struct ufid);
331	ufhp->ufid_ino = ip->ufs_ino.i_number;
332
333	return (0);
334}
335
336int
337fusefs_init(struct vfsconf *vfc)
338{
339	pool_init(&fusefs_fbuf_pool, sizeof(struct fusebuf), 0, 0, PR_WAITOK,
340	    "fmsg", NULL);
341
342	return (0);
343}
344
345int
346fusefs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
347    void *newp, size_t newlen, struct proc *p)
348{
349	extern int stat_fbufs_in, stat_fbufs_wait, stat_opened_fusedev;
350
351	/* all sysctl names at this level are terminal */
352	if (namelen != 1)
353		return (ENOTDIR);		/* overloaded */
354
355	switch (name[0]) {
356	case FUSEFS_OPENDEVS:
357		return (sysctl_rdint(oldp, oldlenp, newp,
358		    stat_opened_fusedev));
359	case FUSEFS_INFBUFS:
360		return (sysctl_rdint(oldp, oldlenp, newp, stat_fbufs_in));
361	case FUSEFS_WAITFBUFS:
362		return (sysctl_rdint(oldp, oldlenp, newp, stat_fbufs_wait));
363	case FUSEFS_POOL_NBPAGES:
364		return (sysctl_rdint(oldp, oldlenp, newp,
365		    fusefs_fbuf_pool.pr_npages));
366	default:
367		return (EOPNOTSUPP);
368	}
369}
370
371int
372fusefs_checkexp(struct mount *mp, struct mbuf *nam, int *extflagsp,
373    struct ucred **credanonp)
374{
375	return (EOPNOTSUPP);
376}
377