379 lines
9 KiB
C
379 lines
9 KiB
C
/* $OpenBSD: fuse_vfsops.c,v 1.46 2024/05/07 14:27:11 mvs Exp $ */
|
|
/*
|
|
* Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com>
|
|
*
|
|
* 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 <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/file.h>
|
|
#include <sys/filedesc.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/pool.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/specdev.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/statvfs.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/fusebuf.h>
|
|
|
|
#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;
|
|
/* FUSE file system is not truly local. */
|
|
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);
|
|
}
|