nrelease - fix/improve livecd
[dragonfly.git] / sys / vfs / fuse / fuse_vfsops.c
blobd67613264ba2aea2391ce82c4f623e632be10da8
1 /*-
2 * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org>
3 * Copyright (c) 2019 The DragonFly Project
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
28 #include "fuse.h"
30 #include <sys/device.h>
31 #include <sys/devfs.h>
32 #include <sys/nlookup.h>
33 #include <sys/file.h>
34 #include <sys/sysctl.h>
35 #include <sys/statvfs.h>
36 #include <sys/caps.h>
37 #include <sys/spinlock.h>
39 #include <sys/spinlock2.h>
41 int fuse_debug = 0;
43 SYSCTL_NODE(_vfs, OID_AUTO, fuse, CTLFLAG_RD, 0, "FUSE");
45 SYSCTL_INT(_vfs_fuse, OID_AUTO, version_major, CTLFLAG_RD, NULL,
46 FUSE_KERNEL_VERSION, "FUSE kernel version (major)");
47 SYSCTL_INT(_vfs_fuse, OID_AUTO, version_minor, CTLFLAG_RD, NULL,
48 FUSE_KERNEL_MINOR_VERSION, "FUSE kernel version (minor)");
50 SYSCTL_INT(_vfs_fuse, OID_AUTO, debug, CTLFLAG_RW, &fuse_debug, 1, "");
52 int
53 fuse_cmp_version(struct fuse_mount *fmp, uint32_t major, uint32_t minor)
55 if (fmp->abi_major == major && fmp->abi_minor == minor)
56 return 0;
58 if (fmp->abi_major > major ||
59 (fmp->abi_major == major && fmp->abi_minor > minor))
60 return 1;
62 return -1;
65 int
66 fuse_mount_kill(struct fuse_mount *fmp)
68 if (!fuse_test_dead(fmp)) {
69 fuse_set_dead(fmp);
70 wakeup(fmp);
71 KNOTE(&fmp->kq.ki_note, 0);
72 return 0;
75 return -1;
78 int
79 fuse_mount_free(struct fuse_mount *fmp)
81 if (refcount_release(&fmp->refcnt)) {
82 fuse_dbg("fmp=%p free\n", fmp);
83 mtx_uninit(&fmp->ipc_lock);
84 mtx_uninit(&fmp->mnt_lock);
85 mtx_uninit(&fmp->ino_lock);
86 crfree(fmp->cred);
87 kfree(fmp, M_TEMP);
88 return 0;
90 fuse_dbg("fmp=%p %u refcnt left\n", fmp, fmp->refcnt);
92 return -1;
95 static int
96 fuse_mount(struct mount *mp, char *mntpt, caddr_t data, struct ucred *cred)
98 struct statfs *sbp = &mp->mnt_stat;
99 struct vnode *devvp;
100 struct file *file;
101 struct nlookupdata nd;
102 struct fuse_mount_info args;
103 struct fuse_mount *fmp;
104 struct fuse_ipc *fip;
105 struct fuse_init_in *fii;
106 struct fuse_init_out *fio;
107 char subtype[512];
108 int error;
110 if (mp->mnt_flag & MNT_UPDATE)
111 return EOPNOTSUPP;
113 error = copyin(data, &args, sizeof(args));
114 if (error)
115 return error;
117 memset(sbp->f_mntfromname, 0, sizeof(sbp->f_mntfromname));
118 error = copyinstr(args.from, sbp->f_mntfromname,
119 sizeof(sbp->f_mntfromname), NULL);
120 if (error)
121 return error;
123 memset(sbp->f_mntonname, 0, sizeof(sbp->f_mntonname));
124 error = copyinstr(mntpt, sbp->f_mntonname, sizeof(sbp->f_mntonname),
125 NULL);
126 if (error)
127 return error;
129 memset(subtype, 0, sizeof(subtype));
130 if (args.subtype != NULL) {
131 error = copyinstr(args.subtype, subtype, sizeof(subtype), NULL);
132 if (error)
133 return error;
134 if (strlen(subtype)) {
135 strlcat(sbp->f_fstypename, ".", sizeof(sbp->f_fstypename));
136 strlcat(sbp->f_fstypename, subtype, sizeof(sbp->f_fstypename));
140 error = nlookup_init(&nd, sbp->f_mntfromname, UIO_SYSSPACE, NLC_FOLLOW);
141 if (!error) {
142 error = nlookup(&nd);
143 if (!error)
144 error = cache_vref(&nd.nl_nch, nd.nl_cred, &devvp);
145 nlookup_done(&nd);
147 if (error)
148 return error;
149 if (!devvp)
150 return ENODEV;
152 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
153 error = VOP_ACCESS(devvp, VREAD | VWRITE, cred);
154 if (error == 0)
155 error = caps_priv_check(cred, SYSCAP_NOMOUNT_FUSE);
156 if (error) {
157 vput(devvp);
158 return error;
160 vn_unlock(devvp);
162 fuse_dbg("fd=%d\n", args.fd);
163 file = holdfp_fdp(curthread->td_proc->p_fd, args.fd, FREAD | FWRITE);
164 if (!file) {
165 vrele(devvp);
166 return EBADF;
168 error = devfs_get_cdevpriv(file, (void**)&fmp);
169 dropfp(curthread, args.fd, file);
170 if (error) {
171 vrele(devvp);
172 return error;
174 KKASSERT(fmp);
176 fmp->mp = mp;
177 fmp->dead = false;
178 mtx_init(&fmp->mnt_lock, "fuse_mnt_lock");
179 mtx_init(&fmp->ipc_lock, "fuse_ipc_lock");
180 mtx_init(&fmp->ino_lock, "fuse_ino_lock");
181 TAILQ_INIT(&fmp->request_head);
182 TAILQ_INIT(&fmp->reply_head);
183 RB_INIT(&fmp->node_head);
184 fmp->devvp = devvp;
185 fmp->cred = crhold(cred);
186 KKASSERT(fmp->refcnt > 0);
187 refcount_acquire(&fmp->refcnt);
189 mp->mnt_flag |= MNT_LOCAL;
190 mp->mnt_kern_flag |= MNTK_ALL_MPSAFE;
191 mp->mnt_kern_flag |= MNTK_THR_SYNC;
192 mp->mnt_data = (qaddr_t)fmp;
194 fuse_node_new(fmp, FUSE_ROOT_ID, VDIR, &fmp->rfnp);
195 KKASSERT(fmp->rfnp->ino == FUSE_ROOT_ID);
197 vfs_getnewfsid(mp);
198 vfs_add_vnodeops(mp, &fuse_vnode_vops, &mp->mnt_vn_norm_ops);
199 vfs_add_vnodeops(mp, &fuse_spec_vops, &mp->mnt_vn_spec_ops);
200 /* XXX fifo ops */
202 fip = fuse_ipc_get(fmp, sizeof(*fii));
203 fii = fuse_ipc_fill(fip, FUSE_INIT, FUSE_ROOT_ID, NULL);
204 fii->major = FUSE_KERNEL_VERSION;
205 fii->minor = FUSE_KERNEL_MINOR_VERSION;
206 fii->max_readahead = FUSE_BLKSIZE;
207 /* unused */
208 //fii->flags = ...;
210 error = fuse_ipc_tx(fip);
211 if (error) {
212 vrele(devvp);
213 return error;
216 fio = fuse_out_data(fip);
217 fmp->abi_major = fio->major;
218 fmp->abi_minor = fio->minor;
219 fmp->max_write = fio->max_write;
221 if (fuse_cmp_version(fmp, 7, 0) < 0) {
222 fuse_ipc_put(fip);
223 vrele(devvp);
224 return EPROTONOSUPPORT;
227 /* unused */
228 //fio->max_readahead
229 //fio->flags
230 //fio->max_background
231 //fio->congestion_threshold
232 //fio->time_gran
233 //fio->max_pages
234 fuse_print("FUSE UABI %d.%d\n", fmp->abi_major, fmp->abi_minor);
236 fuse_ipc_put(fip);
238 VFS_STATFS(mp, &mp->mnt_stat, cred);
240 spin_init(&fmp->helper_spin, "fuse_spin");
241 TAILQ_INIT(&fmp->bioq);
242 lwkt_create(fuse_io_thread, fmp, &fmp->helper_td,
243 NULL, 0, -1, "fuse_helper");
245 return 0;
248 static int
249 fuse_unmount(struct mount *mp, int mntflags)
251 struct fuse_mount *fmp = VFSTOFUSE(mp);
252 struct fuse_ipc *fip;
253 int error, flags = 0;
255 mtx_lock(&fmp->mnt_lock);
256 if (mntflags & MNT_FORCE)
257 flags |= FORCECLOSE;
259 error = vflush(mp, 0, flags);
260 if (error) {
261 mtx_unlock(&fmp->mnt_lock);
262 fuse_dbg("vflush error=%d\n", error);
263 return error;
266 if (!fuse_test_dead(fmp)) {
267 fuse_dbg("not dead yet, destroying\n");
268 fip = fuse_ipc_get(fmp, 0);
269 fuse_ipc_fill(fip, FUSE_DESTROY, FUSE_ROOT_ID, NULL);
270 if (!fuse_ipc_tx(fip))
271 fuse_ipc_put(fip);
272 fuse_mount_kill(fmp);
275 /* Wait for helper thread to exit */
276 while (fmp->helper_td) {
277 wakeup(&fmp->helper_td);
278 tsleep(&fmp->helper_td, 0, "fusehumnt", 2);
281 KKASSERT(fmp->rfnp->vp == NULL);
282 fuse_node_free(fmp, fmp->rfnp);
283 fmp->rfnp = NULL;
285 /* The userspace fs will exit anyway after FUSE_DESTROY. */
286 vn_lock(fmp->devvp, LK_EXCLUSIVE | LK_RETRY);
287 VOP_CLOSE(fmp->devvp, FREAD | FWRITE, NULL);
288 vn_unlock(fmp->devvp);
290 vrele(fmp->devvp);
291 mtx_unlock(&fmp->mnt_lock);
293 fuse_mount_free(fmp);
294 mp->mnt_data = NULL;
295 mp->mnt_flag &= ~MNT_LOCAL;
297 fuse_dbg("unmount done\n");
299 return 0;
304 * fuse_sync() and friends
306 * This is an alternative faster way for DragonFlyBSD to flush vnodes,
307 * but requires a bit of code structure. vsetisdirty() puts the vnode
308 * on a per-thread syncer list. When the list is non-empty, .vfs_sync()
309 * is called periodically to flush dirty vnodes.
311 * In the case of fuse, at the moment file writes are asynchronous and
312 * other attribute changes are synchronous so we only have to check for
313 * dirty buffers.
315 static int fuse_sync_scan1(struct mount *mp, struct vnode *vp, void *data);
316 static int fuse_sync_scan2(struct mount *mp, struct vnode *vp, void *data);
318 struct scaninfo {
319 int rescan;
320 int waitfor;
321 int allerror;
325 static int
326 fuse_sync(struct mount *mp, int waitfor)
328 struct scaninfo scaninfo;
330 scaninfo.allerror = 0;
331 scaninfo.rescan = 1;
332 scaninfo.waitfor = waitfor;
333 while (scaninfo.rescan) {
334 scaninfo.rescan = 0;
335 vmntvnodescan(mp, VMSC_GETVP|VMSC_NOWAIT,
336 fuse_sync_scan1, fuse_sync_scan2, &scaninfo);
338 return (scaninfo.allerror);
342 * Fast pre-check requires flush?
344 static int
345 fuse_sync_scan1(struct mount *mp, struct vnode *vp, void *data)
347 if (RB_EMPTY(&vp->v_rbdirty_tree))
348 return -1;
349 return 0;
353 * Main flush (re-check)
355 static int
356 fuse_sync_scan2(struct mount *mp, struct vnode *vp, void *data)
358 struct scaninfo *info = data;
359 int error;
361 if ((error = VOP_FSYNC(vp, info->waitfor, 0)) != 0)
362 info->allerror = error;
363 return 0;
366 static int
367 fuse_root(struct mount *mp, struct vnode **vpp)
369 struct fuse_mount *fmp = VFSTOFUSE(mp);
370 int error;
372 KASSERT(fmp->rfnp, ("no root node"));
373 KKASSERT(fmp->rfnp->fmp);
375 error = fuse_node_vn(fmp->rfnp, vpp);
376 if (!error) {
377 struct vnode *vp = *vpp;
378 vsetflags(vp, VROOT);
379 KKASSERT(vp->v_type == VDIR);
382 return error;
385 static int
386 fuse_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
388 struct fuse_mount *fmp = VFSTOFUSE(mp);
389 struct fuse_ipc *fip;
390 struct fuse_statfs_out *fso;
391 int error;
393 fip = fuse_ipc_get(fmp, 0);
394 fuse_ipc_fill(fip, FUSE_STATFS, FUSE_ROOT_ID, cred);
395 error = fuse_ipc_tx(fip);
396 if (error)
397 return error;
399 fso = fuse_out_data(fip);
401 mtx_lock(&fmp->mnt_lock);
402 sbp->f_bsize = fso->st.frsize;
403 sbp->f_iosize = FUSE_BLKSIZE;
404 sbp->f_blocks = fso->st.blocks;
405 sbp->f_bfree = fso->st.bfree;
406 sbp->f_bavail = fso->st.bavail;
407 sbp->f_files = fso->st.files;
408 sbp->f_ffree = fso->st.ffree;
409 mtx_unlock(&fmp->mnt_lock);
411 fuse_ipc_put(fip);
413 return 0;
416 static int
417 fuse_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred)
419 struct fuse_mount *fmp = VFSTOFUSE(mp);
420 struct fuse_ipc *fip;
421 struct fuse_statfs_out *fso;
422 int error;
424 fip = fuse_ipc_get(fmp, 0);
425 fuse_ipc_fill(fip, FUSE_STATFS, FUSE_ROOT_ID, cred);
426 error = fuse_ipc_tx(fip);
427 if (error)
428 return error;
430 fso = fuse_out_data(fip);
432 mtx_lock(&fmp->mnt_lock);
433 sbp->f_bsize = fso->st.frsize;
434 sbp->f_frsize = FUSE_BLKSIZE;
435 sbp->f_blocks = fso->st.blocks;
436 sbp->f_bfree = fso->st.bfree;
437 sbp->f_bavail = fso->st.bavail;
438 sbp->f_files = fso->st.files;
439 sbp->f_ffree = fso->st.ffree;
440 mtx_unlock(&fmp->mnt_lock);
442 fuse_ipc_put(fip);
444 return 0;
447 static int
448 fuse_init(struct vfsconf *vfsp)
450 int error;
452 fuse_node_init();
453 fuse_ipc_init();
455 error = fuse_device_init();
456 if (error) {
457 fuse_ipc_cleanup();
458 fuse_node_cleanup();
459 return error;
462 fuse_print("FUSE ABI %d.%d\n", FUSE_KERNEL_VERSION,
463 FUSE_KERNEL_MINOR_VERSION);
465 return 0;
468 static int
469 fuse_uninit(struct vfsconf *vfsp)
471 fuse_ipc_cleanup();
472 fuse_node_cleanup();
473 fuse_device_cleanup();
475 return 0;
478 static struct vfsops fuse_vfsops = {
479 .vfs_flags = 0,
480 .vfs_init = fuse_init,
481 .vfs_uninit = fuse_uninit,
482 .vfs_mount = fuse_mount,
483 .vfs_unmount = fuse_unmount,
484 .vfs_sync = fuse_sync,
485 .vfs_root = fuse_root,
486 .vfs_statfs = fuse_statfs,
487 .vfs_statvfs = fuse_statvfs,
490 VFS_SET(fuse_vfsops, fuse, VFCF_SYNTHETIC | VFCF_MPSAFE);
491 MODULE_VERSION(fuse, 1);