/* $OpenBSD: fuse_vfsops.c,v 1.45 2021/05/01 16:18:29 gnezdo Exp $ */ /* * Copyright (c) 2012-2013 Sylvestre Gallon * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fusefs_node.h" #include "fusefs.h" int fusefs_mount(struct mount *, const char *, void *, struct nameidata *, struct proc *); int fusefs_start(struct mount *, int, struct proc *); int fusefs_unmount(struct mount *, int, struct proc *); int fusefs_root(struct mount *, struct vnode **); int fusefs_quotactl(struct mount *, int, uid_t, caddr_t, struct proc *); int fusefs_statfs(struct mount *, struct statfs *, struct proc *); int fusefs_sync(struct mount *, int, int, struct ucred *, struct proc *); int fusefs_vget(struct mount *, ino_t, struct vnode **); int fusefs_fhtovp(struct mount *, struct fid *, struct vnode **); int fusefs_vptofh(struct vnode *, struct fid *); int fusefs_init(struct vfsconf *); int fusefs_sysctl(int *, u_int, void *, size_t *, void *, size_t, struct proc *); int fusefs_checkexp(struct mount *, struct mbuf *, int *, struct ucred **); const struct vfsops fusefs_vfsops = { .vfs_mount = fusefs_mount, .vfs_start = fusefs_start, .vfs_unmount = fusefs_unmount, .vfs_root = fusefs_root, .vfs_quotactl = fusefs_quotactl, .vfs_statfs = fusefs_statfs, .vfs_sync = fusefs_sync, .vfs_vget = fusefs_vget, .vfs_fhtovp = fusefs_fhtovp, .vfs_vptofh = fusefs_vptofh, .vfs_init = fusefs_init, .vfs_sysctl = fusefs_sysctl, .vfs_checkexp = fusefs_checkexp, }; struct pool fusefs_fbuf_pool; #define PENDING 2 /* FBT_INIT reply not yet received */ int fusefs_mount(struct mount *mp, const char *path, void *data, struct nameidata *ndp, struct proc *p) { struct fusefs_mnt *fmp; struct fusebuf *fbuf; struct fusefs_args *args = data; struct vnode *vp; struct file *fp; int error = 0; if (mp->mnt_flag & MNT_UPDATE) return (EOPNOTSUPP); if ((fp = fd_getfile(p->p_fd, args->fd)) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE) { error = EINVAL; goto bad; } vp = fp->f_data; if (vp->v_type != VCHR) { error = EBADF; goto bad; } /* Only root may specify allow_other. */ if (args->allow_other && (error = suser_ucred(p->p_ucred))) goto bad; fmp = malloc(sizeof(*fmp), M_FUSEFS, M_WAITOK | M_ZERO); fmp->mp = mp; fmp->sess_init = PENDING; fmp->dev = vp->v_rdev; if (args->max_read > 0) fmp->max_read = MIN(args->max_read, FUSEBUFMAXSIZE); else fmp->max_read = FUSEBUFMAXSIZE; fmp->allow_other = args->allow_other; mp->mnt_data = fmp; mp->mnt_flag |= MNT_LOCAL; vfs_getnewfsid(mp); memset(mp->mnt_stat.f_mntonname, 0, MNAMELEN); strlcpy(mp->mnt_stat.f_mntonname, path, MNAMELEN); memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN); strlcpy(mp->mnt_stat.f_mntfromname, "fusefs", MNAMELEN); memset(mp->mnt_stat.f_mntfromspec, 0, MNAMELEN); strlcpy(mp->mnt_stat.f_mntfromspec, "fusefs", MNAMELEN); fuse_device_set_fmp(fmp, 1); fbuf = fb_setup(0, 0, FBT_INIT, p); /* cannot tsleep on mount */ fuse_device_queue_fbuf(fmp->dev, fbuf); bad: FRELE(fp, p); return (error); } int fusefs_start(struct mount *mp, int flags, struct proc *p) { return (0); } int fusefs_unmount(struct mount *mp, int mntflags, struct proc *p) { struct fusefs_mnt *fmp; struct fusebuf *fbuf; int flags = 0; int error; fmp = VFSTOFUSEFS(mp); if (mntflags & MNT_FORCE) flags |= FORCECLOSE; if ((error = vflush(mp, NULLVP, flags))) return (error); if (fmp->sess_init && fmp->sess_init != PENDING) { fbuf = fb_setup(0, 0, FBT_DESTROY, p); error = fb_queue(fmp->dev, fbuf); if (error) printf("fusefs: error %d on destroy\n", error); fb_delete(fbuf); } fmp->sess_init = 0; fuse_device_cleanup(fmp->dev); fuse_device_set_fmp(fmp, 0); free(fmp, M_FUSEFS, sizeof(*fmp)); mp->mnt_data = NULL; return (0); } int fusefs_root(struct mount *mp, struct vnode **vpp) { struct vnode *nvp; int error; if ((error = VFS_VGET(mp, FUSE_ROOTINO, &nvp)) != 0) return (error); nvp->v_type = VDIR; *vpp = nvp; return (0); } int fusefs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg, struct proc *p) { return (EOPNOTSUPP); } int fusefs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p) { struct fusefs_mnt *fmp; struct fusebuf *fbuf; int error; fmp = VFSTOFUSEFS(mp); /* Deny other users unless allow_other mount option was specified. */ if (!fmp->allow_other && p->p_ucred->cr_uid != mp->mnt_stat.f_owner) return (EPERM); copy_statfs_info(sbp, mp); /* * Both FBT_INIT and FBT_STATFS are sent to the FUSE file system * daemon when it is mounted. However, the daemon is the process * that called mount(2) so to prevent a deadlock return dummy * values until the response to FBT_INIT init is received. All * other VFS syscalls are queued. */ if (!fmp->sess_init || fmp->sess_init == PENDING) { sbp->f_bavail = 0; sbp->f_bfree = 0; sbp->f_blocks = 0; sbp->f_ffree = 0; sbp->f_favail = 0; sbp->f_files = 0; sbp->f_bsize = 0; sbp->f_iosize = 0; sbp->f_namemax = 0; } else { fbuf = fb_setup(0, FUSE_ROOTINO, FBT_STATFS, p); error = fb_queue(fmp->dev, fbuf); if (error) { fb_delete(fbuf); return (error); } sbp->f_bavail = fbuf->fb_stat.f_bavail; sbp->f_bfree = fbuf->fb_stat.f_bfree; sbp->f_blocks = fbuf->fb_stat.f_blocks; sbp->f_files = fbuf->fb_stat.f_files; sbp->f_ffree = fbuf->fb_stat.f_ffree; sbp->f_favail = fbuf->fb_stat.f_favail; sbp->f_bsize = fbuf->fb_stat.f_frsize; sbp->f_iosize = fbuf->fb_stat.f_bsize; sbp->f_namemax = fbuf->fb_stat.f_namemax; fb_delete(fbuf); } return (0); } int fusefs_sync(struct mount *mp, int waitfor, int stall, struct ucred *cred, struct proc *p) { return (0); } int fusefs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) { struct vattr vattr; struct fusefs_mnt *fmp; struct fusefs_node *ip; struct vnode *nvp; int i; int error; retry: fmp = VFSTOFUSEFS(mp); /* * check if vnode is in hash. */ if ((*vpp = ufs_ihashget(fmp->dev, ino)) != NULLVP) return (0); /* * if not create it */ if ((error = getnewvnode(VT_FUSEFS, mp, &fusefs_vops, &nvp)) != 0) { printf("fusefs: getnewvnode error\n"); *vpp = NULLVP; return (error); } ip = malloc(sizeof(*ip), M_FUSEFS, M_WAITOK | M_ZERO); rrw_init_flags(&ip->ufs_ino.i_lock, "fuseinode", RWL_DUPOK | RWL_IS_VNODE); nvp->v_data = ip; ip->ufs_ino.i_vnode = nvp; ip->ufs_ino.i_dev = fmp->dev; ip->ufs_ino.i_number = ino; for (i = 0; i < FUFH_MAXTYPE; i++) ip->fufh[i].fh_type = FUFH_INVALID; error = ufs_ihashins(&ip->ufs_ino); if (error) { vrele(nvp); if (error == EEXIST) goto retry; return (error); } ip->ufs_ino.i_ump = (struct ufsmount *)fmp; if (ino == FUSE_ROOTINO) nvp->v_flag |= VROOT; else { /* * Initialise the file size so that file size changes can be * detected during file operations. */ error = VOP_GETATTR(nvp, &vattr, curproc->p_ucred, curproc); if (error) { vrele(nvp); return (error); } ip->filesize = vattr.va_size; } *vpp = nvp; return (0); } int fusefs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) { return (EINVAL); } int fusefs_vptofh(struct vnode *vp, struct fid *fhp) { return (EINVAL); } int fusefs_init(struct vfsconf *vfc) { pool_init(&fusefs_fbuf_pool, sizeof(struct fusebuf), 0, 0, PR_WAITOK, "fmsg", NULL); return (0); } extern int stat_fbufs_in, stat_fbufs_wait, stat_opened_fusedev; const struct sysctl_bounded_args fusefs_vars[] = { { FUSEFS_OPENDEVS, &stat_opened_fusedev, SYSCTL_INT_READONLY }, { FUSEFS_INFBUFS, &stat_fbufs_in, SYSCTL_INT_READONLY }, { FUSEFS_WAITFBUFS, &stat_fbufs_wait, SYSCTL_INT_READONLY }, { FUSEFS_POOL_NBPAGES, &fusefs_fbuf_pool.pr_npages, SYSCTL_INT_READONLY }, }; int fusefs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen, struct proc *p) { return sysctl_bounded_arr(fusefs_vars, nitems(fusefs_vars), name, namelen, oldp, oldlenp, newp, newlen); } int fusefs_checkexp(struct mount *mp, struct mbuf *nam, int *extflagsp, struct ucred **credanonp) { return (EOPNOTSUPP); }