1*51d8761dSclaudio /* $OpenBSD: fuse_vfsops.c,v 1.48 2024/10/31 13:55:21 claudio Exp $ */ 2a2badd06Stedu /* 3a2badd06Stedu * Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com> 4a2badd06Stedu * 5a2badd06Stedu * Permission to use, copy, modify, and distribute this software for any 6a2badd06Stedu * purpose with or without fee is hereby granted, provided that the above 7a2badd06Stedu * copyright notice and this permission notice appear in all copies. 8a2badd06Stedu * 9a2badd06Stedu * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10a2badd06Stedu * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11a2badd06Stedu * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12a2badd06Stedu * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13a2badd06Stedu * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14a2badd06Stedu * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15a2badd06Stedu * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16a2badd06Stedu */ 17a2badd06Stedu 18a2badd06Stedu #include <sys/param.h> 19fde894e5Stedu #include <sys/systm.h> 20f76a189dSsyl #include <sys/file.h> 21f76a189dSsyl #include <sys/filedesc.h> 22a2badd06Stedu #include <sys/malloc.h> 23a2badd06Stedu #include <sys/mount.h> 24a2badd06Stedu #include <sys/pool.h> 25f76a189dSsyl #include <sys/proc.h> 26f76a189dSsyl #include <sys/specdev.h> 276f9b5942Snatano #include <sys/stat.h> 28a2badd06Stedu #include <sys/statvfs.h> 29a2badd06Stedu #include <sys/sysctl.h> 30a2badd06Stedu #include <sys/vnode.h> 31a2badd06Stedu #include <sys/fusebuf.h> 32a2badd06Stedu 33a2badd06Stedu #include "fusefs_node.h" 34a2badd06Stedu #include "fusefs.h" 35a2badd06Stedu 36a2badd06Stedu int fusefs_mount(struct mount *, const char *, void *, struct nameidata *, 37a2badd06Stedu struct proc *); 38a2badd06Stedu int fusefs_start(struct mount *, int, struct proc *); 39a2badd06Stedu int fusefs_unmount(struct mount *, int, struct proc *); 40a2badd06Stedu int fusefs_root(struct mount *, struct vnode **); 41a2badd06Stedu int fusefs_quotactl(struct mount *, int, uid_t, caddr_t, struct proc *); 42a2badd06Stedu int fusefs_statfs(struct mount *, struct statfs *, struct proc *); 43976e9839Sderaadt int fusefs_sync(struct mount *, int, int, struct ucred *, struct proc *); 44a2badd06Stedu int fusefs_vget(struct mount *, ino_t, struct vnode **); 45a2badd06Stedu int fusefs_fhtovp(struct mount *, struct fid *, struct vnode **); 46a2badd06Stedu int fusefs_vptofh(struct vnode *, struct fid *); 47a2badd06Stedu int fusefs_init(struct vfsconf *); 48a2badd06Stedu int fusefs_sysctl(int *, u_int, void *, size_t *, void *, size_t, 49a2badd06Stedu struct proc *); 50a2badd06Stedu int fusefs_checkexp(struct mount *, struct mbuf *, int *, 51a2badd06Stedu struct ucred **); 52a2badd06Stedu 53a2badd06Stedu const struct vfsops fusefs_vfsops = { 5441f642fcSbluhm .vfs_mount = fusefs_mount, 5541f642fcSbluhm .vfs_start = fusefs_start, 5641f642fcSbluhm .vfs_unmount = fusefs_unmount, 5741f642fcSbluhm .vfs_root = fusefs_root, 5841f642fcSbluhm .vfs_quotactl = fusefs_quotactl, 5941f642fcSbluhm .vfs_statfs = fusefs_statfs, 6041f642fcSbluhm .vfs_sync = fusefs_sync, 6141f642fcSbluhm .vfs_vget = fusefs_vget, 6241f642fcSbluhm .vfs_fhtovp = fusefs_fhtovp, 6341f642fcSbluhm .vfs_vptofh = fusefs_vptofh, 6441f642fcSbluhm .vfs_init = fusefs_init, 6541f642fcSbluhm .vfs_sysctl = fusefs_sysctl, 6641f642fcSbluhm .vfs_checkexp = fusefs_checkexp, 67a2badd06Stedu }; 68a2badd06Stedu 69a2badd06Stedu struct pool fusefs_fbuf_pool; 70a2badd06Stedu 715b6d7437Shelg #define PENDING 2 /* FBT_INIT reply not yet received */ 725b6d7437Shelg 73a2badd06Stedu int 74a2badd06Stedu fusefs_mount(struct mount *mp, const char *path, void *data, 75a2badd06Stedu struct nameidata *ndp, struct proc *p) 76a2badd06Stedu { 77a2badd06Stedu struct fusefs_mnt *fmp; 78a2badd06Stedu struct fusebuf *fbuf; 797efda1a1Sderaadt struct fusefs_args *args = data; 80f76a189dSsyl struct vnode *vp; 81f76a189dSsyl struct file *fp; 8253146562Smpi int error = 0; 83a2badd06Stedu 84a2badd06Stedu if (mp->mnt_flag & MNT_UPDATE) 85a2badd06Stedu return (EOPNOTSUPP); 86a2badd06Stedu 877efda1a1Sderaadt if ((fp = fd_getfile(p->p_fd, args->fd)) == NULL) 88f76a189dSsyl return (EBADF); 89f76a189dSsyl 9053146562Smpi if (fp->f_type != DTYPE_VNODE) { 9153146562Smpi error = EINVAL; 9253146562Smpi goto bad; 9353146562Smpi } 94f76a189dSsyl 95f76a189dSsyl vp = fp->f_data; 9653146562Smpi if (vp->v_type != VCHR) { 9753146562Smpi error = EBADF; 9853146562Smpi goto bad; 9953146562Smpi } 100f76a189dSsyl 10163849fffSmpi /* Only root may specify allow_other. */ 10263849fffSmpi if (args->allow_other && (error = suser_ucred(p->p_ucred))) 10363849fffSmpi goto bad; 10463849fffSmpi 105a2badd06Stedu fmp = malloc(sizeof(*fmp), M_FUSEFS, M_WAITOK | M_ZERO); 106a2badd06Stedu fmp->mp = mp; 1075b6d7437Shelg fmp->sess_init = PENDING; 108f76a189dSsyl fmp->dev = vp->v_rdev; 1097efda1a1Sderaadt if (args->max_read > 0) 1107efda1a1Sderaadt fmp->max_read = MIN(args->max_read, FUSEBUFMAXSIZE); 1114a254543Ssyl else 1124a254543Ssyl fmp->max_read = FUSEBUFMAXSIZE; 113a2badd06Stedu 1140f4d2db5Shelg fmp->allow_other = args->allow_other; 1150f4d2db5Shelg 1164a254543Ssyl mp->mnt_data = fmp; 117a2badd06Stedu vfs_getnewfsid(mp); 118a2badd06Stedu 119bb72b40bShelg memset(mp->mnt_stat.f_mntonname, 0, MNAMELEN); 120a2badd06Stedu strlcpy(mp->mnt_stat.f_mntonname, path, MNAMELEN); 121bb72b40bShelg memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN); 122f28dec03Snatano strlcpy(mp->mnt_stat.f_mntfromname, "fusefs", MNAMELEN); 123bb72b40bShelg memset(mp->mnt_stat.f_mntfromspec, 0, MNAMELEN); 124f28dec03Snatano strlcpy(mp->mnt_stat.f_mntfromspec, "fusefs", MNAMELEN); 125a2badd06Stedu 12648ee6ddaSpelikan fuse_device_set_fmp(fmp, 1); 127a2badd06Stedu fbuf = fb_setup(0, 0, FBT_INIT, p); 128a2badd06Stedu 129a2badd06Stedu /* cannot tsleep on mount */ 130a2badd06Stedu fuse_device_queue_fbuf(fmp->dev, fbuf); 131a2badd06Stedu 13253146562Smpi bad: 13353146562Smpi FRELE(fp, p); 13453146562Smpi return (error); 135a2badd06Stedu } 136a2badd06Stedu 137a2badd06Stedu int 138a2badd06Stedu fusefs_start(struct mount *mp, int flags, struct proc *p) 139a2badd06Stedu { 140a2badd06Stedu return (0); 141a2badd06Stedu } 142a2badd06Stedu 143a2badd06Stedu int 144a2badd06Stedu fusefs_unmount(struct mount *mp, int mntflags, struct proc *p) 145a2badd06Stedu { 146a2badd06Stedu struct fusefs_mnt *fmp; 147a2badd06Stedu struct fusebuf *fbuf; 148a2badd06Stedu int flags = 0; 149a2badd06Stedu int error; 150a2badd06Stedu 151a2badd06Stedu fmp = VFSTOFUSEFS(mp); 152a2badd06Stedu 153065726a5Snatano if (mntflags & MNT_FORCE) 15446cd8370Ssyl flags |= FORCECLOSE; 15546cd8370Ssyl 15646cd8370Ssyl if ((error = vflush(mp, NULLVP, flags))) 15746cd8370Ssyl return (error); 15846cd8370Ssyl 1593270c159Shelg if (fmp->sess_init && fmp->sess_init != PENDING) { 160a2badd06Stedu fbuf = fb_setup(0, 0, FBT_DESTROY, p); 161a2badd06Stedu 162a2badd06Stedu error = fb_queue(fmp->dev, fbuf); 163a2badd06Stedu 164a2badd06Stedu if (error) 16548ee6ddaSpelikan printf("fusefs: error %d on destroy\n", error); 16637318181Ssyl 16737318181Ssyl fb_delete(fbuf); 168a2badd06Stedu } 1693270c159Shelg fmp->sess_init = 0; 170a2badd06Stedu 171b7e69da4Shelg fuse_device_cleanup(fmp->dev); 17248ee6ddaSpelikan fuse_device_set_fmp(fmp, 0); 173903f970aShelg free(fmp, M_FUSEFS, sizeof(*fmp)); 174200e77f4Sbluhm mp->mnt_data = NULL; 175a2badd06Stedu 1764b1ae25eSbluhm return (0); 177a2badd06Stedu } 178a2badd06Stedu 179a2badd06Stedu int 180a2badd06Stedu fusefs_root(struct mount *mp, struct vnode **vpp) 181a2badd06Stedu { 182a2badd06Stedu struct vnode *nvp; 183a2badd06Stedu int error; 184a2badd06Stedu 185a1055de8Snatano if ((error = VFS_VGET(mp, FUSE_ROOTINO, &nvp)) != 0) 186a2badd06Stedu return (error); 187a2badd06Stedu 188a2badd06Stedu nvp->v_type = VDIR; 189a2badd06Stedu 190a2badd06Stedu *vpp = nvp; 191a2badd06Stedu return (0); 192a2badd06Stedu } 193a2badd06Stedu 194e3d2e44cStedu int 195e3d2e44cStedu fusefs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg, 196a2badd06Stedu struct proc *p) 197a2badd06Stedu { 1980181dae0Stedu return (EOPNOTSUPP); 199a2badd06Stedu } 200a2badd06Stedu 201e3d2e44cStedu int 202e3d2e44cStedu fusefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p) 203a2badd06Stedu { 204a2badd06Stedu struct fusefs_mnt *fmp; 205a2badd06Stedu struct fusebuf *fbuf; 206a2badd06Stedu int error; 207a2badd06Stedu 208a2badd06Stedu fmp = VFSTOFUSEFS(mp); 209a2badd06Stedu 2100f4d2db5Shelg /* Deny other users unless allow_other mount option was specified. */ 2110f4d2db5Shelg if (!fmp->allow_other && p->p_ucred->cr_uid != mp->mnt_stat.f_owner) 2120f4d2db5Shelg return (EPERM); 2130f4d2db5Shelg 214f28dec03Snatano copy_statfs_info(sbp, mp); 215f28dec03Snatano 2165b6d7437Shelg /* 2175b6d7437Shelg * Both FBT_INIT and FBT_STATFS are sent to the FUSE file system 2185b6d7437Shelg * daemon when it is mounted. However, the daemon is the process 2195b6d7437Shelg * that called mount(2) so to prevent a deadlock return dummy 2205b6d7437Shelg * values until the response to FBT_INIT init is received. All 2215b6d7437Shelg * other VFS syscalls are queued. 2225b6d7437Shelg */ 2235b6d7437Shelg if (!fmp->sess_init || fmp->sess_init == PENDING) { 2245b6d7437Shelg sbp->f_bavail = 0; 2255b6d7437Shelg sbp->f_bfree = 0; 2265b6d7437Shelg sbp->f_blocks = 0; 2275b6d7437Shelg sbp->f_ffree = 0; 2285b6d7437Shelg sbp->f_favail = 0; 2295b6d7437Shelg sbp->f_files = 0; 2305b6d7437Shelg sbp->f_bsize = 0; 2315b6d7437Shelg sbp->f_iosize = 0; 2325b6d7437Shelg sbp->f_namemax = 0; 2335b6d7437Shelg } else { 234a1055de8Snatano fbuf = fb_setup(0, FUSE_ROOTINO, FBT_STATFS, p); 235a2badd06Stedu 236a2badd06Stedu error = fb_queue(fmp->dev, fbuf); 237a2badd06Stedu 238a2badd06Stedu if (error) { 23937318181Ssyl fb_delete(fbuf); 240a2badd06Stedu return (error); 241a2badd06Stedu } 242a2badd06Stedu 243a2badd06Stedu sbp->f_bavail = fbuf->fb_stat.f_bavail; 244a2badd06Stedu sbp->f_bfree = fbuf->fb_stat.f_bfree; 245a2badd06Stedu sbp->f_blocks = fbuf->fb_stat.f_blocks; 246a2badd06Stedu sbp->f_files = fbuf->fb_stat.f_files; 247a2badd06Stedu sbp->f_ffree = fbuf->fb_stat.f_ffree; 248f28dec03Snatano sbp->f_favail = fbuf->fb_stat.f_favail; 249a2badd06Stedu sbp->f_bsize = fbuf->fb_stat.f_frsize; 250f28dec03Snatano sbp->f_iosize = fbuf->fb_stat.f_bsize; 251a2badd06Stedu sbp->f_namemax = fbuf->fb_stat.f_namemax; 25237318181Ssyl fb_delete(fbuf); 253a2badd06Stedu } 254a2badd06Stedu 255a2badd06Stedu return (0); 256a2badd06Stedu } 257a2badd06Stedu 258e3d2e44cStedu int 259976e9839Sderaadt fusefs_sync(struct mount *mp, int waitfor, int stall, struct ucred *cred, 260a2badd06Stedu struct proc *p) 261a2badd06Stedu { 262a2badd06Stedu return (0); 263a2badd06Stedu } 264a2badd06Stedu 265e3d2e44cStedu int 266e3d2e44cStedu fusefs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) 267a2badd06Stedu { 2683c992bbfShelg struct vattr vattr; 269a2badd06Stedu struct fusefs_mnt *fmp; 270a2badd06Stedu struct fusefs_node *ip; 271a2badd06Stedu struct vnode *nvp; 272a2badd06Stedu int i; 273a2badd06Stedu int error; 274a2badd06Stedu retry: 275a2badd06Stedu fmp = VFSTOFUSEFS(mp); 276a2badd06Stedu /* 277a2badd06Stedu * check if vnode is in hash. 278a2badd06Stedu */ 279*51d8761dSclaudio if ((*vpp = fuse_ihashget(fmp->dev, ino)) != NULLVP) 280a2badd06Stedu return (0); 281a2badd06Stedu 282a2badd06Stedu /* 283a2badd06Stedu * if not create it 284a2badd06Stedu */ 285a2badd06Stedu if ((error = getnewvnode(VT_FUSEFS, mp, &fusefs_vops, &nvp)) != 0) { 2864f926cddSsyl printf("fusefs: getnewvnode error\n"); 287a2badd06Stedu *vpp = NULLVP; 288a2badd06Stedu return (error); 289a2badd06Stedu } 290a2badd06Stedu 291a2badd06Stedu ip = malloc(sizeof(*ip), M_FUSEFS, M_WAITOK | M_ZERO); 292*51d8761dSclaudio rrw_init_flags(&ip->i_lock, "fuseinode", 293d78cb2ffSvisa RWL_DUPOK | RWL_IS_VNODE); 294a2badd06Stedu nvp->v_data = ip; 295*51d8761dSclaudio ip->i_vnode = nvp; 296*51d8761dSclaudio ip->i_dev = fmp->dev; 297*51d8761dSclaudio ip->i_number = ino; 298a2badd06Stedu 299a2badd06Stedu for (i = 0; i < FUFH_MAXTYPE; i++) 300a2badd06Stedu ip->fufh[i].fh_type = FUFH_INVALID; 301a2badd06Stedu 302*51d8761dSclaudio error = fuse_ihashins(ip); 303a2badd06Stedu if (error) { 304a2badd06Stedu vrele(nvp); 305a2badd06Stedu 306a2badd06Stedu if (error == EEXIST) 307a2badd06Stedu goto retry; 308a2badd06Stedu 309a2badd06Stedu return (error); 310a2badd06Stedu } 311a2badd06Stedu 312*51d8761dSclaudio ip->i_ump = fmp; 313a2badd06Stedu 314a2badd06Stedu if (ino == FUSE_ROOTINO) 315a2badd06Stedu nvp->v_flag |= VROOT; 3163270c159Shelg else { 3173c992bbfShelg /* 3183c992bbfShelg * Initialise the file size so that file size changes can be 3193c992bbfShelg * detected during file operations. 3203c992bbfShelg */ 3213c992bbfShelg error = VOP_GETATTR(nvp, &vattr, curproc->p_ucred, curproc); 3223c992bbfShelg if (error) { 3233c992bbfShelg vrele(nvp); 3243c992bbfShelg return (error); 3253c992bbfShelg } 3263c992bbfShelg ip->filesize = vattr.va_size; 3273270c159Shelg } 3283c992bbfShelg 329a2badd06Stedu *vpp = nvp; 330a2badd06Stedu 331a2badd06Stedu return (0); 332a2badd06Stedu } 333a2badd06Stedu 334e3d2e44cStedu int 335e3d2e44cStedu fusefs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) 336a2badd06Stedu { 337514c5354Snatano return (EINVAL); 338a2badd06Stedu } 339a2badd06Stedu 340e3d2e44cStedu int 341e3d2e44cStedu fusefs_vptofh(struct vnode *vp, struct fid *fhp) 342a2badd06Stedu { 343514c5354Snatano return (EINVAL); 344a2badd06Stedu } 345a2badd06Stedu 346e3d2e44cStedu int 347e3d2e44cStedu fusefs_init(struct vfsconf *vfc) 348a2badd06Stedu { 3490c632275Stedu pool_init(&fusefs_fbuf_pool, sizeof(struct fusebuf), 0, 0, PR_WAITOK, 3500c632275Stedu "fmsg", NULL); 351*51d8761dSclaudio fuse_ihashinit(); 352a2badd06Stedu 353a2badd06Stedu return (0); 354a2badd06Stedu } 355a2badd06Stedu 3566e044e75Sgnezdo extern int stat_fbufs_in, stat_fbufs_wait, stat_opened_fusedev; 3576e044e75Sgnezdo 3586e044e75Sgnezdo const struct sysctl_bounded_args fusefs_vars[] = { 3599c14f6a5Sgnezdo { FUSEFS_OPENDEVS, &stat_opened_fusedev, SYSCTL_INT_READONLY }, 3609c14f6a5Sgnezdo { FUSEFS_INFBUFS, &stat_fbufs_in, SYSCTL_INT_READONLY }, 3619c14f6a5Sgnezdo { FUSEFS_WAITFBUFS, &stat_fbufs_wait, SYSCTL_INT_READONLY }, 3629c14f6a5Sgnezdo { FUSEFS_POOL_NBPAGES, &fusefs_fbuf_pool.pr_npages, SYSCTL_INT_READONLY }, 3636e044e75Sgnezdo }; 3646e044e75Sgnezdo 365e3d2e44cStedu int 366e3d2e44cStedu fusefs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, 367a2badd06Stedu void *newp, size_t newlen, struct proc *p) 368a2badd06Stedu { 3696e044e75Sgnezdo return sysctl_bounded_arr(fusefs_vars, nitems(fusefs_vars), name, 3706e044e75Sgnezdo namelen, oldp, oldlenp, newp, newlen); 371a2badd06Stedu } 372a2badd06Stedu 373e3d2e44cStedu int 374e3d2e44cStedu fusefs_checkexp(struct mount *mp, struct mbuf *nam, int *extflagsp, 375a2badd06Stedu struct ucred **credanonp) 376a2badd06Stedu { 3773d0b1bbcSnatano return (EOPNOTSUPP); 378a2badd06Stedu } 379