2 * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org>
3 * Copyright (c) 2019 The DragonFly Project
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
30 #include <sys/device.h>
31 #include <sys/devfs.h>
32 #include <sys/nlookup.h>
34 #include <sys/sysctl.h>
35 #include <sys/statvfs.h>
37 #include <sys/spinlock.h>
39 #include <sys/spinlock2.h>
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, "");
53 fuse_cmp_version(struct fuse_mount
*fmp
, uint32_t major
, uint32_t minor
)
55 if (fmp
->abi_major
== major
&& fmp
->abi_minor
== minor
)
58 if (fmp
->abi_major
> major
||
59 (fmp
->abi_major
== major
&& fmp
->abi_minor
> minor
))
66 fuse_mount_kill(struct fuse_mount
*fmp
)
68 if (!fuse_test_dead(fmp
)) {
71 KNOTE(&fmp
->kq
.ki_note
, 0);
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
);
90 fuse_dbg("fmp=%p %u refcnt left\n", fmp
, fmp
->refcnt
);
96 fuse_mount(struct mount
*mp
, char *mntpt
, caddr_t data
, struct ucred
*cred
)
98 struct statfs
*sbp
= &mp
->mnt_stat
;
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
;
110 if (mp
->mnt_flag
& MNT_UPDATE
)
113 error
= copyin(data
, &args
, sizeof(args
));
117 memset(sbp
->f_mntfromname
, 0, sizeof(sbp
->f_mntfromname
));
118 error
= copyinstr(args
.from
, sbp
->f_mntfromname
,
119 sizeof(sbp
->f_mntfromname
), NULL
);
123 memset(sbp
->f_mntonname
, 0, sizeof(sbp
->f_mntonname
));
124 error
= copyinstr(mntpt
, sbp
->f_mntonname
, sizeof(sbp
->f_mntonname
),
129 memset(subtype
, 0, sizeof(subtype
));
130 if (args
.subtype
!= NULL
) {
131 error
= copyinstr(args
.subtype
, subtype
, sizeof(subtype
), NULL
);
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
);
142 error
= nlookup(&nd
);
144 error
= cache_vref(&nd
.nl_nch
, nd
.nl_cred
, &devvp
);
152 vn_lock(devvp
, LK_EXCLUSIVE
| LK_RETRY
);
153 error
= VOP_ACCESS(devvp
, VREAD
| VWRITE
, cred
);
155 error
= caps_priv_check(cred
, SYSCAP_NOMOUNT_FUSE
);
162 fuse_dbg("fd=%d\n", args
.fd
);
163 file
= holdfp_fdp(curthread
->td_proc
->p_fd
, args
.fd
, FREAD
| FWRITE
);
168 error
= devfs_get_cdevpriv(file
, (void**)&fmp
);
169 dropfp(curthread
, args
.fd
, file
);
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
);
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
);
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
);
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
;
210 error
= fuse_ipc_tx(fip
);
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) {
224 return EPROTONOSUPPORT
;
230 //fio->max_background
231 //fio->congestion_threshold
234 fuse_print("FUSE UABI %d.%d\n", fmp
->abi_major
, fmp
->abi_minor
);
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");
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
)
259 error
= vflush(mp
, 0, flags
);
261 mtx_unlock(&fmp
->mnt_lock
);
262 fuse_dbg("vflush error=%d\n", 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
))
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
);
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
);
291 mtx_unlock(&fmp
->mnt_lock
);
293 fuse_mount_free(fmp
);
295 mp
->mnt_flag
&= ~MNT_LOCAL
;
297 fuse_dbg("unmount done\n");
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
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
);
326 fuse_sync(struct mount
*mp
, int waitfor
)
328 struct scaninfo scaninfo
;
330 scaninfo
.allerror
= 0;
332 scaninfo
.waitfor
= waitfor
;
333 while (scaninfo
.rescan
) {
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?
345 fuse_sync_scan1(struct mount
*mp
, struct vnode
*vp
, void *data
)
347 if (RB_EMPTY(&vp
->v_rbdirty_tree
))
353 * Main flush (re-check)
356 fuse_sync_scan2(struct mount
*mp
, struct vnode
*vp
, void *data
)
358 struct scaninfo
*info
= data
;
361 if ((error
= VOP_FSYNC(vp
, info
->waitfor
, 0)) != 0)
362 info
->allerror
= error
;
367 fuse_root(struct mount
*mp
, struct vnode
**vpp
)
369 struct fuse_mount
*fmp
= VFSTOFUSE(mp
);
372 KASSERT(fmp
->rfnp
, ("no root node"));
373 KKASSERT(fmp
->rfnp
->fmp
);
375 error
= fuse_node_vn(fmp
->rfnp
, vpp
);
377 struct vnode
*vp
= *vpp
;
378 vsetflags(vp
, VROOT
);
379 KKASSERT(vp
->v_type
== VDIR
);
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
;
393 fip
= fuse_ipc_get(fmp
, 0);
394 fuse_ipc_fill(fip
, FUSE_STATFS
, FUSE_ROOT_ID
, cred
);
395 error
= fuse_ipc_tx(fip
);
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
);
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
;
424 fip
= fuse_ipc_get(fmp
, 0);
425 fuse_ipc_fill(fip
, FUSE_STATFS
, FUSE_ROOT_ID
, cred
);
426 error
= fuse_ipc_tx(fip
);
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
);
448 fuse_init(struct vfsconf
*vfsp
)
455 error
= fuse_device_init();
462 fuse_print("FUSE ABI %d.%d\n", FUSE_KERNEL_VERSION
,
463 FUSE_KERNEL_MINOR_VERSION
);
469 fuse_uninit(struct vfsconf
*vfsp
)
473 fuse_device_cleanup();
478 static struct vfsops fuse_vfsops
= {
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);