1 /*- 2 * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org> 3 * Copyright (c) 2019 The DragonFly Project 4 * All rights reserved. 5 * 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. 14 * 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. 26 */ 27 28 #include "fuse.h" 29 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 38 int fuse_debug = 0; 39 40 SYSCTL_NODE(_vfs, OID_AUTO, fuse, CTLFLAG_RD, 0, "FUSE"); 41 42 SYSCTL_INT(_vfs_fuse, OID_AUTO, version_major, CTLFLAG_RD, NULL, 43 FUSE_KERNEL_VERSION, "FUSE kernel version (major)"); 44 SYSCTL_INT(_vfs_fuse, OID_AUTO, version_minor, CTLFLAG_RD, NULL, 45 FUSE_KERNEL_MINOR_VERSION, "FUSE kernel version (minor)"); 46 47 SYSCTL_INT(_vfs_fuse, OID_AUTO, debug, CTLFLAG_RW, &fuse_debug, 1, ""); 48 49 int 50 fuse_cmp_version(struct fuse_mount *fmp, uint32_t major, uint32_t minor) 51 { 52 if (fmp->abi_major == major && fmp->abi_minor == minor) 53 return 0; 54 55 if (fmp->abi_major > major || 56 (fmp->abi_major == major && fmp->abi_minor > minor)) 57 return 1; 58 59 return -1; 60 } 61 62 int 63 fuse_mount_kill(struct fuse_mount *fmp) 64 { 65 if (!fuse_test_dead(fmp)) { 66 fuse_set_dead(fmp); 67 wakeup(fmp); 68 KNOTE(&fmp->kq.ki_note, 0); 69 return 0; 70 } 71 72 return -1; 73 } 74 75 int 76 fuse_mount_free(struct fuse_mount *fmp) 77 { 78 if (refcount_release(&fmp->refcnt)) { 79 fuse_dbg("fmp=%p free\n", fmp); 80 mtx_uninit(&fmp->ipc_lock); 81 mtx_uninit(&fmp->mnt_lock); 82 crfree(fmp->cred); 83 kfree(fmp, M_TEMP); 84 return 0; 85 } 86 fuse_dbg("fmp=%p %u refcnt left\n", fmp, fmp->refcnt); 87 88 return -1; 89 } 90 91 static int 92 fuse_mount(struct mount *mp, char *mntpt, caddr_t data, struct ucred *cred) 93 { 94 struct statfs *sbp = &mp->mnt_stat; 95 struct vnode *devvp; 96 struct file *file; 97 struct nlookupdata nd; 98 struct fuse_mount_info args; 99 struct fuse_mount *fmp; 100 struct fuse_ipc *fip; 101 struct fuse_init_in *fii; 102 struct fuse_init_out *fio; 103 char subtype[512]; 104 int error; 105 106 if (mp->mnt_flag & MNT_UPDATE) 107 return EOPNOTSUPP; 108 109 error = copyin(data, &args, sizeof(args)); 110 if (error) 111 return error; 112 memcpy(sbp->f_mntfromname, args.from, sizeof(sbp->f_mntfromname)); 113 114 memset(sbp->f_mntfromname, 0, sizeof(sbp->f_mntfromname)); 115 error = copyinstr(args.from, sbp->f_mntfromname, 116 sizeof(sbp->f_mntfromname), NULL); 117 if (error) 118 return error; 119 120 memset(sbp->f_mntonname, 0, sizeof(sbp->f_mntonname)); 121 error = copyinstr(mntpt, sbp->f_mntonname, sizeof(sbp->f_mntonname), 122 NULL); 123 if (error) 124 return error; 125 126 memset(subtype, 0, sizeof(subtype)); 127 error = copyinstr(args.subtype, subtype, sizeof(subtype), NULL); 128 if (error) 129 return error; 130 if (strlen(subtype)) { 131 strlcat(sbp->f_fstypename, ".", sizeof(sbp->f_fstypename)); 132 strlcat(sbp->f_fstypename, subtype, sizeof(sbp->f_fstypename)); 133 } 134 135 error = nlookup_init(&nd, sbp->f_mntfromname, UIO_SYSSPACE, NLC_FOLLOW); 136 if (!error) { 137 error = nlookup(&nd); 138 if (!error) 139 error = cache_vref(&nd.nl_nch, nd.nl_cred, &devvp); 140 nlookup_done(&nd); 141 } 142 if (error) 143 return error; 144 if (!devvp) 145 return ENODEV; 146 147 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 148 error = VOP_ACCESS(devvp, VREAD | VWRITE, cred); 149 if (error == 0) 150 error = caps_priv_check(cred, SYSCAP_NOMOUNT_FUSE); 151 if (error) { 152 vput(devvp); 153 return error; 154 } 155 vn_unlock(devvp); 156 157 fuse_dbg("fd=%d\n", args.fd); 158 file = holdfp_fdp(curthread->td_proc->p_fd, args.fd, FREAD | FWRITE); 159 if (!file) { 160 vrele(devvp); 161 return EBADF; 162 } 163 error = devfs_get_cdevpriv(file, (void**)&fmp); 164 dropfp(curthread, args.fd, file); 165 if (error) { 166 vrele(devvp); 167 return error; 168 } 169 KKASSERT(fmp); 170 171 fmp->mp = mp; 172 fmp->dead = false; 173 mtx_init(&fmp->mnt_lock, "fuse_mnt_lock"); 174 mtx_init(&fmp->ipc_lock, "fuse_ipc_lock"); 175 TAILQ_INIT(&fmp->request_head); 176 TAILQ_INIT(&fmp->reply_head); 177 fmp->devvp = devvp; 178 fmp->cred = crhold(cred); 179 KKASSERT(fmp->refcnt > 0); 180 refcount_acquire(&fmp->refcnt); 181 182 mp->mnt_flag |= MNT_LOCAL; 183 mp->mnt_kern_flag |= MNTK_ALL_MPSAFE; 184 mp->mnt_data = (qaddr_t)fmp; 185 186 fuse_node_new(fmp, FUSE_ROOT_ID, VDIR, &fmp->rfnp); 187 KKASSERT(fmp->rfnp->ino == FUSE_ROOT_ID); 188 189 vfs_getnewfsid(mp); 190 vfs_add_vnodeops(mp, &fuse_vnode_vops, &mp->mnt_vn_norm_ops); 191 vfs_add_vnodeops(mp, &fuse_spec_vops, &mp->mnt_vn_spec_ops); 192 193 fip = fuse_ipc_get(fmp, sizeof(*fii)); 194 fii = fuse_ipc_fill(fip, FUSE_INIT, FUSE_ROOT_ID, NULL); 195 fii->major = FUSE_KERNEL_VERSION; 196 fii->minor = FUSE_KERNEL_MINOR_VERSION; 197 fii->max_readahead = FUSE_BLKSIZE; 198 /* unused */ 199 //fii->flags = ...; 200 201 error = fuse_ipc_tx(fip); 202 if (error) { 203 vrele(devvp); 204 return error; 205 } 206 207 fio = fuse_out_data(fip); 208 fmp->abi_major = fio->major; 209 fmp->abi_minor = fio->minor; 210 fmp->max_write = fio->max_write; 211 212 if (fuse_cmp_version(fmp, 7, 0) < 0) { 213 fuse_ipc_put(fip); 214 vrele(devvp); 215 return EPROTONOSUPPORT; 216 } 217 218 /* unused */ 219 //fio->max_readahead 220 //fio->flags 221 //fio->max_background 222 //fio->congestion_threshold 223 //fio->time_gran 224 //fio->max_pages 225 fuse_print("FUSE UABI %d.%d\n", fmp->abi_major, fmp->abi_minor); 226 227 fuse_ipc_put(fip); 228 229 VFS_STATFS(mp, &mp->mnt_stat, cred); 230 231 return 0; 232 } 233 234 static int 235 fuse_unmount(struct mount *mp, int mntflags) 236 { 237 struct fuse_mount *fmp = VFSTOFUSE(mp); 238 struct fuse_ipc *fip; 239 int error, flags = 0; 240 241 mtx_lock(&fmp->mnt_lock); 242 if (mntflags & MNT_FORCE) 243 flags |= FORCECLOSE; 244 245 error = vflush(mp, 0, flags); 246 if (error) { 247 mtx_unlock(&fmp->mnt_lock); 248 fuse_dbg("vflush error=%d\n", error); 249 return error; 250 } 251 252 if (!fuse_test_dead(fmp)) { 253 fuse_dbg("not dead yet, destroying\n"); 254 fip = fuse_ipc_get(fmp, 0); 255 fuse_ipc_fill(fip, FUSE_DESTROY, FUSE_ROOT_ID, NULL); 256 if (!fuse_ipc_tx(fip)) 257 fuse_ipc_put(fip); 258 fuse_mount_kill(fmp); 259 } 260 261 /* The userspace fs will exit anyway after FUSE_DESTROY. */ 262 vn_lock(fmp->devvp, LK_EXCLUSIVE | LK_RETRY); 263 VOP_CLOSE(fmp->devvp, FREAD | FWRITE, NULL); 264 vn_unlock(fmp->devvp); 265 266 vrele(fmp->devvp); 267 mtx_unlock(&fmp->mnt_lock); 268 269 fuse_mount_free(fmp); 270 mp->mnt_data = NULL; 271 mp->mnt_flag &= ~MNT_LOCAL; 272 273 fuse_dbg("unmount done\n"); 274 275 return 0; 276 } 277 278 static int 279 fuse_root(struct mount *mp, struct vnode **vpp) 280 { 281 struct fuse_mount *fmp = VFSTOFUSE(mp); 282 int error; 283 284 KASSERT(fmp->rfnp, ("no root node")); 285 KKASSERT(fmp->rfnp->fmp); 286 287 error = fuse_node_vn(fmp->rfnp, LK_EXCLUSIVE, vpp); 288 if (!error) { 289 struct vnode *vp = *vpp; 290 vp->v_flag |= VROOT; 291 KKASSERT(vp->v_type == VDIR); 292 } 293 294 return error; 295 } 296 297 static int 298 fuse_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred) 299 { 300 struct fuse_mount *fmp = VFSTOFUSE(mp); 301 struct fuse_ipc *fip; 302 struct fuse_statfs_out *fso; 303 int error; 304 305 fip = fuse_ipc_get(fmp, 0); 306 fuse_ipc_fill(fip, FUSE_STATFS, FUSE_ROOT_ID, cred); 307 error = fuse_ipc_tx(fip); 308 if (error) 309 return error; 310 311 fso = fuse_out_data(fip); 312 313 mtx_lock(&fmp->mnt_lock); 314 sbp->f_bsize = fso->st.frsize; 315 sbp->f_iosize = FUSE_BLKSIZE; 316 sbp->f_blocks = fso->st.blocks; 317 sbp->f_bfree = fso->st.bfree; 318 sbp->f_bavail = fso->st.bavail; 319 sbp->f_files = fso->st.files; 320 sbp->f_ffree = fso->st.ffree; 321 mtx_unlock(&fmp->mnt_lock); 322 323 fuse_ipc_put(fip); 324 325 return 0; 326 } 327 328 static int 329 fuse_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred) 330 { 331 struct fuse_mount *fmp = VFSTOFUSE(mp); 332 struct fuse_ipc *fip; 333 struct fuse_statfs_out *fso; 334 int error; 335 336 fip = fuse_ipc_get(fmp, 0); 337 fuse_ipc_fill(fip, FUSE_STATFS, FUSE_ROOT_ID, cred); 338 error = fuse_ipc_tx(fip); 339 if (error) 340 return error; 341 342 fso = fuse_out_data(fip); 343 344 mtx_lock(&fmp->mnt_lock); 345 sbp->f_bsize = fso->st.frsize; 346 sbp->f_frsize = FUSE_BLKSIZE; 347 sbp->f_blocks = fso->st.blocks; 348 sbp->f_bfree = fso->st.bfree; 349 sbp->f_bavail = fso->st.bavail; 350 sbp->f_files = fso->st.files; 351 sbp->f_ffree = fso->st.ffree; 352 mtx_unlock(&fmp->mnt_lock); 353 354 fuse_ipc_put(fip); 355 356 return 0; 357 } 358 359 static int 360 fuse_init(struct vfsconf *vfsp) 361 { 362 int error; 363 364 fuse_node_init(); 365 fuse_ipc_init(); 366 fuse_file_init(); 367 368 error = fuse_device_init(); 369 if (error) { 370 fuse_file_cleanup(); 371 fuse_ipc_cleanup(); 372 fuse_node_cleanup(); 373 return error; 374 } 375 376 fuse_print("FUSE ABI %d.%d\n", FUSE_KERNEL_VERSION, 377 FUSE_KERNEL_MINOR_VERSION); 378 379 return 0; 380 } 381 382 static int 383 fuse_uninit(struct vfsconf *vfsp) 384 { 385 fuse_file_cleanup(); 386 fuse_ipc_cleanup(); 387 fuse_node_cleanup(); 388 fuse_device_cleanup(); 389 390 return 0; 391 } 392 393 static struct vfsops fuse_vfsops = { 394 .vfs_flags = 0, 395 .vfs_init = fuse_init, 396 .vfs_uninit = fuse_uninit, 397 .vfs_mount = fuse_mount, 398 .vfs_unmount = fuse_unmount, 399 .vfs_root = fuse_root, 400 .vfs_statfs = fuse_statfs, 401 .vfs_statvfs = fuse_statvfs, 402 }; 403 404 VFS_SET(fuse_vfsops, fuse, VFCF_SYNTHETIC | VFCF_MPSAFE); 405 MODULE_VERSION(fuse, 1); 406