xref: /openbsd/sys/miscfs/fuse/fuse_vfsops.c (revision 5af055cd)
1 /* $OpenBSD: fuse_vfsops.c,v 1.19 2016/03/17 18:52:31 bluhm 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 
35 int	fusefs_mount(struct mount *, const char *, void *, struct nameidata *,
36 	    struct proc *);
37 int	fusefs_start(struct mount *, int, struct proc *);
38 int	fusefs_unmount(struct mount *, int, struct proc *);
39 int	fusefs_root(struct mount *, struct vnode **);
40 int	fusefs_quotactl(struct mount *, int, uid_t, caddr_t, struct proc *);
41 int	fusefs_statfs(struct mount *, struct statfs *, struct proc *);
42 int	fusefs_sync(struct mount *, int, struct ucred *, struct proc *);
43 int	fusefs_vget(struct mount *, ino_t, struct vnode **);
44 int	fusefs_fhtovp(struct mount *, struct fid *, struct vnode **);
45 int	fusefs_vptofh(struct vnode *, struct fid *);
46 int	fusefs_init(struct vfsconf *);
47 int	fusefs_sysctl(int *, u_int, void *, size_t *, void *, size_t,
48 	    struct proc *);
49 int	fusefs_checkexp(struct mount *, struct mbuf *, int *,
50 	    struct ucred **);
51 
52 const 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 
68 struct pool fusefs_fbuf_pool;
69 
70 int
71 fusefs_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 
125 int
126 fusefs_start(struct mount *mp, int flags, struct proc *p)
127 {
128 	return (0);
129 }
130 
131 int
132 fusefs_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 	mp->mnt_data = NULL;
169 
170 	return (error);
171 }
172 
173 int
174 fusefs_root(struct mount *mp, struct vnode **vpp)
175 {
176 	struct vnode *nvp;
177 	struct fusefs_node *ip;
178 	int error;
179 
180 	if ((error = VFS_VGET(mp, (ino_t)FUSE_ROOTINO, &nvp)) != 0)
181 		return (error);
182 
183 	ip = VTOI(nvp);
184 	nvp->v_type = VDIR;
185 	ip->vtype = VDIR;
186 
187 	*vpp = nvp;
188 	return (0);
189 }
190 
191 int
192 fusefs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg,
193     struct proc *p)
194 {
195 	return (EOPNOTSUPP);
196 }
197 
198 int
199 fusefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
200 {
201 	struct fusefs_mnt *fmp;
202 	struct fusebuf *fbuf;
203 	int error;
204 
205 	fmp = VFSTOFUSEFS(mp);
206 
207 	if (fmp->sess_init) {
208 		fbuf = fb_setup(0, FUSE_ROOT_ID, FBT_STATFS, p);
209 
210 		error = fb_queue(fmp->dev, fbuf);
211 
212 		if (error) {
213 			fb_delete(fbuf);
214 			return (error);
215 		}
216 
217 		sbp->f_bavail = fbuf->fb_stat.f_bavail;
218 		sbp->f_bfree = fbuf->fb_stat.f_bfree;
219 		sbp->f_blocks = fbuf->fb_stat.f_blocks;
220 		sbp->f_files = fbuf->fb_stat.f_files;
221 		sbp->f_ffree = fbuf->fb_stat.f_ffree;
222 		sbp->f_bsize = fbuf->fb_stat.f_frsize;
223 		sbp->f_namemax = fbuf->fb_stat.f_namemax;
224 		fb_delete(fbuf);
225 	} else {
226 		sbp->f_bavail = 0;
227 		sbp->f_bfree = 0;
228 		sbp->f_blocks = 0;
229 		sbp->f_ffree = 0;
230 		sbp->f_files = 0;
231 		sbp->f_bsize = 0;
232 		sbp->f_namemax = 0;
233 	}
234 
235 	return (0);
236 }
237 
238 int
239 fusefs_sync(struct mount *mp, int waitfor, struct ucred *cred,
240     struct proc *p)
241 {
242 	return (0);
243 }
244 
245 int
246 fusefs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
247 {
248 	struct fusefs_mnt *fmp;
249 	struct fusefs_node *ip;
250 	struct vnode *nvp;
251 	int i;
252 	int error;
253 retry:
254 	fmp = VFSTOFUSEFS(mp);
255 	/*
256 	 * check if vnode is in hash.
257 	 */
258 	if ((*vpp = ufs_ihashget(fmp->dev, ino)) != NULLVP)
259 		return (0);
260 
261 	/*
262 	 * if not create it
263 	 */
264 	if ((error = getnewvnode(VT_FUSEFS, mp, &fusefs_vops, &nvp)) != 0) {
265 		printf("fusefs: getnewvnode error\n");
266 		*vpp = NULLVP;
267 		return (error);
268 	}
269 
270 	ip = malloc(sizeof(*ip), M_FUSEFS, M_WAITOK | M_ZERO);
271 	lockinit(&ip->ufs_ino.i_lock, PINOD, "fuseinode", 0, 0);
272 	nvp->v_data = ip;
273 	ip->ufs_ino.i_vnode = nvp;
274 	ip->ufs_ino.i_dev = fmp->dev;
275 	ip->ufs_ino.i_number = ino;
276 	ip->parent = 0;
277 
278 	for (i = 0; i < FUFH_MAXTYPE; i++)
279 		ip->fufh[i].fh_type = FUFH_INVALID;
280 
281 	error = ufs_ihashins(&ip->ufs_ino);
282 	if (error) {
283 		vrele(nvp);
284 
285 		if (error == EEXIST)
286 			goto retry;
287 
288 		return (error);
289 	}
290 
291 	ip->ufs_ino.i_ump = (struct ufsmount *)fmp;
292 
293 	if (ino == FUSE_ROOTINO)
294 		nvp->v_flag |= VROOT;
295 
296 	*vpp = nvp;
297 
298 	return (0);
299 }
300 
301 int
302 fusefs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
303 {
304 	struct ufid *ufhp;
305 
306 	ufhp = (struct ufid *)fhp;
307 	if (ufhp->ufid_len != sizeof(struct ufid) ||
308 	    ufhp->ufid_ino < FUSE_ROOTINO)
309 		return (ESTALE);
310 
311 	return (VFS_VGET(mp, ufhp->ufid_ino, vpp));
312 }
313 
314 int
315 fusefs_vptofh(struct vnode *vp, struct fid *fhp)
316 {
317 	struct fusefs_node *ip;
318 	struct ufid *ufhp;
319 
320 	ip = VTOI(vp);
321 	ufhp = (struct ufid *)fhp;
322 	ufhp->ufid_len = sizeof(struct ufid);
323 	ufhp->ufid_ino = ip->ufs_ino.i_number;
324 
325 	return (0);
326 }
327 
328 int
329 fusefs_init(struct vfsconf *vfc)
330 {
331 	pool_init(&fusefs_fbuf_pool, sizeof(struct fusebuf), 0, 0, PR_WAITOK,
332 	    "fmsg", NULL);
333 
334 	return (0);
335 }
336 
337 int
338 fusefs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
339     void *newp, size_t newlen, struct proc *p)
340 {
341 	extern int stat_fbufs_in, stat_fbufs_wait, stat_opened_fusedev;
342 
343 	/* all sysctl names at this level are terminal */
344 	if (namelen != 1)
345 		return (ENOTDIR);		/* overloaded */
346 
347 	switch (name[0]) {
348 	case FUSEFS_OPENDEVS:
349 		return (sysctl_rdint(oldp, oldlenp, newp,
350 		    stat_opened_fusedev));
351 	case FUSEFS_INFBUFS:
352 		return (sysctl_rdint(oldp, oldlenp, newp, stat_fbufs_in));
353 	case FUSEFS_WAITFBUFS:
354 		return (sysctl_rdint(oldp, oldlenp, newp, stat_fbufs_wait));
355 	case FUSEFS_POOL_NBPAGES:
356 		return (sysctl_rdint(oldp, oldlenp, newp,
357 		    fusefs_fbuf_pool.pr_npages));
358 	default:
359 		return (EOPNOTSUPP);
360 	}
361 }
362 
363 int
364 fusefs_checkexp(struct mount *mp, struct mbuf *nam, int *extflagsp,
365     struct ucred **credanonp)
366 {
367 	return (EOPNOTSUPP);
368 }
369