fuse_vfsops.c revision 1.13
1/* $OpenBSD: fuse_vfsops.c,v 1.13 2014/12/16 18:30:04 tedu 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	bcopy("fusefs", mp->mnt_stat.f_mntfromname, sizeof("fusefs"));
115
116	fuse_device_set_fmp(fmp, 1);
117	fbuf = fb_setup(0, 0, FBT_INIT, p);
118
119	/* cannot tsleep on mount */
120	fuse_device_queue_fbuf(fmp->dev, fbuf);
121
122	return (0);
123}
124
125int
126fusefs_start(struct mount *mp, int flags, struct proc *p)
127{
128	return (0);
129}
130
131int
132fusefs_unmount(struct mount *mp, int mntflags, struct proc *p)
133{
134	struct fusefs_mnt *fmp;
135	struct fusebuf *fbuf;
136	extern int doforce;
137	int flags = 0;
138	int error;
139
140	fmp = VFSTOFUSEFS(mp);
141
142	if (mntflags & MNT_FORCE) {
143		/* fusefs can never be rootfs so don't check for it */
144		if (!doforce)
145			return (EINVAL);
146
147		flags |= FORCECLOSE;
148	}
149
150	if ((error = vflush(mp, NULLVP, flags)))
151		return (error);
152
153	if (fmp->sess_init) {
154		fmp->sess_init = 0;
155		fbuf = fb_setup(0, 0, FBT_DESTROY, p);
156
157		error = fb_queue(fmp->dev, fbuf);
158
159		if (error)
160			printf("fusefs: error %d on destroy\n", error);
161
162		fb_delete(fbuf);
163	}
164
165	fuse_device_cleanup(fmp->dev, NULL);
166	fuse_device_set_fmp(fmp, 0);
167	free(fmp, M_FUSEFS, 0);
168
169	return (error);
170}
171
172int
173fusefs_root(struct mount *mp, struct vnode **vpp)
174{
175	struct vnode *nvp;
176	struct fusefs_node *ip;
177	int error;
178
179	if ((error = VFS_VGET(mp, (ino_t)FUSE_ROOTINO, &nvp)) != 0)
180		return (error);
181
182	ip = VTOI(nvp);
183	nvp->v_type = VDIR;
184	ip->vtype = VDIR;
185
186	*vpp = nvp;
187	return (0);
188}
189
190int fusefs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg,
191    struct proc *p)
192{
193	return (EOPNOTSUPP);
194}
195
196int fusefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
197{
198	struct fusefs_mnt *fmp;
199	struct fusebuf *fbuf;
200	int error;
201
202	fmp = VFSTOFUSEFS(mp);
203
204	if (fmp->sess_init) {
205		fbuf = fb_setup(0, FUSE_ROOT_ID, FBT_STATFS, p);
206
207		error = fb_queue(fmp->dev, fbuf);
208
209		if (error) {
210			fb_delete(fbuf);
211			return (error);
212		}
213
214		sbp->f_bavail = fbuf->fb_stat.f_bavail;
215		sbp->f_bfree = fbuf->fb_stat.f_bfree;
216		sbp->f_blocks = fbuf->fb_stat.f_blocks;
217		sbp->f_files = fbuf->fb_stat.f_files;
218		sbp->f_ffree = fbuf->fb_stat.f_ffree;
219		sbp->f_bsize = fbuf->fb_stat.f_frsize;
220		sbp->f_namemax = fbuf->fb_stat.f_namemax;
221		fb_delete(fbuf);
222	} else {
223		sbp->f_bavail = 0;
224		sbp->f_bfree = 0;
225		sbp->f_blocks = 0;
226		sbp->f_ffree = 0;
227		sbp->f_files = 0;
228		sbp->f_bsize = 0;
229		sbp->f_namemax = 0;
230	}
231
232	return (0);
233}
234
235int fusefs_sync(struct mount *mp, int waitfor, struct ucred *cred,
236    struct proc *p)
237{
238	return (0);
239}
240
241int fusefs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
242{
243	struct fusefs_mnt *fmp;
244	struct fusefs_node *ip;
245	struct vnode *nvp;
246	int i;
247	int error;
248retry:
249	fmp = VFSTOFUSEFS(mp);
250	/*
251	 * check if vnode is in hash.
252	 */
253	if ((*vpp = ufs_ihashget(fmp->dev, ino)) != NULLVP)
254		return (0);
255
256	/*
257	 * if not create it
258	 */
259	if ((error = getnewvnode(VT_FUSEFS, mp, &fusefs_vops, &nvp)) != 0) {
260		printf("fusefs: getnewvnode error\n");
261		*vpp = NULLVP;
262		return (error);
263	}
264
265	ip = malloc(sizeof(*ip), M_FUSEFS, M_WAITOK | M_ZERO);
266	lockinit(&ip->ufs_ino.i_lock, PINOD, "fuseinode", 0, 0);
267	nvp->v_data = ip;
268	ip->ufs_ino.i_vnode = nvp;
269	ip->ufs_ino.i_dev = fmp->dev;
270	ip->ufs_ino.i_number = ino;
271	ip->parent = 0;
272
273	for (i = 0; i < FUFH_MAXTYPE; i++)
274		ip->fufh[i].fh_type = FUFH_INVALID;
275
276	error = ufs_ihashins(&ip->ufs_ino);
277	if (error) {
278		vrele(nvp);
279
280		if (error == EEXIST)
281			goto retry;
282
283		return (error);
284	}
285
286	ip->ufs_ino.i_ump = (struct ufsmount *)fmp;
287
288	if (ino == FUSE_ROOTINO)
289		nvp->v_flag |= VROOT;
290
291	*vpp = nvp;
292
293	return (0);
294}
295
296int fusefs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
297{
298	return (0);
299}
300
301int fusefs_vptofh(struct vnode *vp, struct fid *fhp)
302{
303	return (0);
304}
305
306int fusefs_init(struct vfsconf *vfc)
307{
308	pool_init(&fusefs_fbuf_pool, sizeof(struct fusebuf), 0, 0, 0,
309	    "fmsg", &pool_allocator_nointr);
310
311	return (0);
312}
313
314int fusefs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
315    void *newp, size_t newlen, struct proc *p)
316{
317	extern int stat_fbufs_in, stat_fbufs_wait, stat_opened_fusedev;
318
319	/* all sysctl names at this level are terminal */
320	if (namelen != 1)
321		return (ENOTDIR);		/* overloaded */
322
323	switch (name[0]) {
324	case FUSEFS_OPENDEVS:
325		return (sysctl_rdint(oldp, oldlenp, newp,
326		    stat_opened_fusedev));
327	case FUSEFS_INFBUFS:
328		return (sysctl_rdint(oldp, oldlenp, newp, stat_fbufs_in));
329	case FUSEFS_WAITFBUFS:
330		return (sysctl_rdint(oldp, oldlenp, newp, stat_fbufs_wait));
331	case FUSEFS_POOL_NBPAGES:
332		return (sysctl_rdint(oldp, oldlenp, newp,
333		    fusefs_fbuf_pool.pr_npages));
334	default:
335		return (EOPNOTSUPP);
336	}
337}
338
339int fusefs_checkexp(struct mount *mp, struct mbuf *nam, int *extflagsp,
340    struct ucred **credanonp)
341{
342	return (0);
343}
344