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