2023-09-10 05:16:20 +00:00
|
|
|
/* $OpenBSD: fuse_vnops.c,v 1.67 2023/09/08 20:00:28 mvs Exp $ */
|
2023-04-30 01:15:27 +00:00
|
|
|
/*
|
|
|
|
* 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/dirent.h>
|
|
|
|
#include <sys/fcntl.h>
|
|
|
|
#include <sys/file.h>
|
|
|
|
#include <sys/lockf.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/namei.h>
|
|
|
|
#include <sys/pool.h>
|
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/specdev.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/statvfs.h>
|
|
|
|
#include <sys/vnode.h>
|
|
|
|
#include <sys/lock.h>
|
|
|
|
#include <sys/fusebuf.h>
|
|
|
|
|
|
|
|
#include "fusefs_node.h"
|
|
|
|
#include "fusefs.h"
|
|
|
|
|
|
|
|
/* Prototypes for fusefs vnode ops */
|
|
|
|
int fusefs_kqfilter(void *);
|
|
|
|
int fusefs_lookup(void *);
|
|
|
|
int fusefs_open(void *);
|
|
|
|
int fusefs_close(void *);
|
|
|
|
int fusefs_access(void *);
|
|
|
|
int fusefs_getattr(void *);
|
|
|
|
int fusefs_setattr(void *);
|
|
|
|
int fusefs_ioctl(void *);
|
|
|
|
int fusefs_link(void *);
|
|
|
|
int fusefs_symlink(void *);
|
|
|
|
int fusefs_readdir(void *);
|
|
|
|
int fusefs_readlink(void *);
|
|
|
|
int fusefs_inactive(void *);
|
|
|
|
int fusefs_reclaim(void *);
|
|
|
|
int fusefs_print(void *);
|
|
|
|
int fusefs_create(void *);
|
|
|
|
int fusefs_mknod(void *);
|
|
|
|
int fusefs_read(void *);
|
|
|
|
int fusefs_write(void *);
|
|
|
|
int fusefs_remove(void *);
|
|
|
|
int fusefs_rename(void *);
|
|
|
|
int fusefs_mkdir(void *);
|
|
|
|
int fusefs_rmdir(void *);
|
|
|
|
int fusefs_strategy(void *);
|
|
|
|
int fusefs_lock(void *);
|
|
|
|
int fusefs_unlock(void *);
|
|
|
|
int fusefs_islocked(void *);
|
|
|
|
int fusefs_advlock(void *);
|
|
|
|
int fusefs_fsync(void *);
|
|
|
|
|
|
|
|
/* Prototypes for fusefs kqfilter */
|
|
|
|
int filt_fusefsread(struct knote *, long);
|
|
|
|
int filt_fusefswrite(struct knote *, long);
|
|
|
|
int filt_fusefsvnode(struct knote *, long);
|
|
|
|
void filt_fusefsdetach(struct knote *);
|
|
|
|
|
|
|
|
const struct vops fusefs_vops = {
|
|
|
|
.vop_lookup = fusefs_lookup,
|
|
|
|
.vop_create = fusefs_create,
|
|
|
|
.vop_mknod = fusefs_mknod,
|
|
|
|
.vop_open = fusefs_open,
|
|
|
|
.vop_close = fusefs_close,
|
|
|
|
.vop_access = fusefs_access,
|
|
|
|
.vop_getattr = fusefs_getattr,
|
|
|
|
.vop_setattr = fusefs_setattr,
|
|
|
|
.vop_read = fusefs_read,
|
|
|
|
.vop_write = fusefs_write,
|
|
|
|
.vop_ioctl = fusefs_ioctl,
|
|
|
|
.vop_kqfilter = fusefs_kqfilter,
|
|
|
|
.vop_revoke = NULL,
|
|
|
|
.vop_fsync = fusefs_fsync,
|
|
|
|
.vop_remove = fusefs_remove,
|
|
|
|
.vop_link = fusefs_link,
|
|
|
|
.vop_rename = fusefs_rename,
|
|
|
|
.vop_mkdir = fusefs_mkdir,
|
|
|
|
.vop_rmdir = fusefs_rmdir,
|
|
|
|
.vop_symlink = fusefs_symlink,
|
|
|
|
.vop_readdir = fusefs_readdir,
|
|
|
|
.vop_readlink = fusefs_readlink,
|
|
|
|
.vop_abortop = vop_generic_abortop,
|
|
|
|
.vop_inactive = fusefs_inactive,
|
|
|
|
.vop_reclaim = fusefs_reclaim,
|
|
|
|
.vop_lock = fusefs_lock,
|
|
|
|
.vop_unlock = fusefs_unlock,
|
|
|
|
.vop_bmap = vop_generic_bmap,
|
|
|
|
.vop_strategy = fusefs_strategy,
|
|
|
|
.vop_print = fusefs_print,
|
|
|
|
.vop_islocked = fusefs_islocked,
|
|
|
|
.vop_pathconf = spec_pathconf,
|
|
|
|
.vop_advlock = fusefs_advlock,
|
|
|
|
.vop_bwrite = NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct filterops fusefsread_filtops = {
|
|
|
|
.f_flags = FILTEROP_ISFD,
|
|
|
|
.f_attach = NULL,
|
|
|
|
.f_detach = filt_fusefsdetach,
|
|
|
|
.f_event = filt_fusefsread,
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct filterops fusefswrite_filtops = {
|
|
|
|
.f_flags = FILTEROP_ISFD,
|
|
|
|
.f_attach = NULL,
|
|
|
|
.f_detach = filt_fusefsdetach,
|
|
|
|
.f_event = filt_fusefswrite,
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct filterops fusefsvnode_filtops = {
|
|
|
|
.f_flags = FILTEROP_ISFD,
|
|
|
|
.f_attach = NULL,
|
|
|
|
.f_detach = filt_fusefsdetach,
|
|
|
|
.f_event = filt_fusefsvnode,
|
|
|
|
};
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_kqfilter(void *v)
|
|
|
|
{
|
|
|
|
struct vop_kqfilter_args *ap = v;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct knote *kn = ap->a_kn;
|
|
|
|
|
|
|
|
switch (kn->kn_filter) {
|
|
|
|
case EVFILT_READ:
|
|
|
|
kn->kn_fop = &fusefsread_filtops;
|
|
|
|
break;
|
|
|
|
case EVFILT_WRITE:
|
|
|
|
kn->kn_fop = &fusefswrite_filtops;
|
|
|
|
break;
|
|
|
|
case EVFILT_VNODE:
|
|
|
|
kn->kn_fop = &fusefsvnode_filtops;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
kn->kn_hook = (caddr_t)vp;
|
|
|
|
|
2023-09-10 05:16:20 +00:00
|
|
|
klist_insert_locked(&vp->v_klist, kn);
|
2023-04-30 01:15:27 +00:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
filt_fusefsdetach(struct knote *kn)
|
|
|
|
{
|
|
|
|
struct vnode *vp = (struct vnode *)kn->kn_hook;
|
|
|
|
|
2023-09-10 05:16:20 +00:00
|
|
|
klist_remove_locked(&vp->v_klist, kn);
|
2023-04-30 01:15:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
filt_fusefsread(struct knote *kn, long hint)
|
|
|
|
{
|
|
|
|
struct vnode *vp = (struct vnode *)kn->kn_hook;
|
|
|
|
struct fusefs_node *ip = VTOI(vp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* filesystem is gone, so set the EOF flag and schedule
|
|
|
|
* the knote for deletion
|
|
|
|
*/
|
|
|
|
if (hint == NOTE_REVOKE) {
|
|
|
|
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
kn->kn_data = ip->filesize - foffset(kn->kn_fp);
|
|
|
|
if (kn->kn_data == 0 && kn->kn_sfflags & NOTE_EOF) {
|
|
|
|
kn->kn_fflags |= NOTE_EOF;
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (kn->kn_flags & (__EV_POLL | __EV_SELECT))
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
return (kn->kn_data != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
filt_fusefswrite(struct knote *kn, long hint)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* filesystem is gone, so set the EOF flag and schedule
|
|
|
|
* the knote for deletion
|
|
|
|
*/
|
|
|
|
if (hint == NOTE_REVOKE) {
|
|
|
|
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
kn->kn_data = 0;
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
filt_fusefsvnode(struct knote *kn, long int hint)
|
|
|
|
{
|
|
|
|
if (kn->kn_sfflags & hint)
|
|
|
|
kn->kn_fflags |= hint;
|
|
|
|
if (hint == NOTE_REVOKE) {
|
|
|
|
kn->kn_flags |= EV_EOF;
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
return (kn->kn_fflags != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FUSE file systems can maintain a file handle for each VFS file descriptor
|
2023-06-20 20:38:03 +00:00
|
|
|
* that is opened. The OpenBSD VFS does not make file descriptors visible to
|
2023-04-30 01:15:27 +00:00
|
|
|
* us so we fake it by mapping open flags to file handles.
|
|
|
|
* There is no way for FUSE to know which file descriptor is being used
|
|
|
|
* by an application for a file operation. We only maintain 3 descriptors,
|
|
|
|
* one each for O_RDONLY, O_WRONLY and O_RDWR. When reading and writing, the
|
|
|
|
* first open descriptor is used and this may well not be the one that was set
|
|
|
|
* by FUSE open and may have even been opened by another application.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fusefs_open(void *v)
|
|
|
|
{
|
|
|
|
struct vop_open_args *ap;
|
|
|
|
struct fusefs_node *ip;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct vnode *vp;
|
|
|
|
enum fufh_type fufh_type = FUFH_RDONLY;
|
|
|
|
int flags;
|
|
|
|
int error;
|
|
|
|
int isdir;
|
|
|
|
|
|
|
|
ap = v;
|
|
|
|
vp = ap->a_vp;
|
|
|
|
ip = VTOI(vp);
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
|
|
|
|
if (!fmp->sess_init)
|
|
|
|
return (ENXIO);
|
|
|
|
|
|
|
|
isdir = 0;
|
|
|
|
if (vp->v_type == VDIR)
|
|
|
|
isdir = 1;
|
|
|
|
else {
|
|
|
|
if ((ap->a_mode & FREAD) && (ap->a_mode & FWRITE))
|
|
|
|
fufh_type = FUFH_RDWR;
|
|
|
|
else if (ap->a_mode & (FWRITE))
|
|
|
|
fufh_type = FUFH_WRONLY;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Due to possible attribute caching, there is no
|
|
|
|
* reliable way to determine if the file was modified
|
|
|
|
* externally (e.g. network file system) so clear the
|
|
|
|
* UVM cache to ensure that it is not stale. The file
|
|
|
|
* can still become stale later on read but this will
|
|
|
|
* satisfy most situations.
|
|
|
|
*/
|
|
|
|
uvm_vnp_uncache(vp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* already open i think all is ok */
|
|
|
|
if (ip->fufh[fufh_type].fh_type != FUFH_INVALID)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The file has already been created and/or truncated so FUSE dictates
|
|
|
|
* that no creation and truncation flags are passed to open.
|
|
|
|
*/
|
|
|
|
flags = OFLAGS(ap->a_mode) & ~(O_CREAT|O_EXCL|O_TRUNC);
|
|
|
|
error = fusefs_file_open(fmp, ip, fufh_type, flags, isdir, ap->a_p);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_close(void *v)
|
|
|
|
{
|
|
|
|
struct vop_close_args *ap;
|
|
|
|
struct fusefs_node *ip;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct fusebuf *fbuf;
|
|
|
|
enum fufh_type fufh_type = FUFH_RDONLY;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
ap = v;
|
|
|
|
ip = VTOI(ap->a_vp);
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
|
|
|
|
if (!fmp->sess_init)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The file or directory may have been opened more than once so there
|
|
|
|
* is no reliable way to determine when to ask the FUSE daemon to
|
|
|
|
* release its file descriptor. For files, ask the daemon to flush any
|
|
|
|
* buffers to disk now. All open file descriptors will be released on
|
|
|
|
* VOP_INACTIVE(9).
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (ap->a_vp->v_type == VDIR)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
/* Implementing flush is optional so don't error. */
|
|
|
|
if (fmp->undef_op & UNDEF_FLUSH)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
/* Only flush writeable file descriptors. */
|
|
|
|
if ((ap->a_fflag & FREAD) && (ap->a_fflag & FWRITE))
|
|
|
|
fufh_type = FUFH_RDWR;
|
|
|
|
else if (ap->a_fflag & (FWRITE))
|
|
|
|
fufh_type = FUFH_WRONLY;
|
|
|
|
else
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
if (ip->fufh[fufh_type].fh_type == FUFH_INVALID)
|
|
|
|
return (EBADF);
|
|
|
|
|
|
|
|
fbuf = fb_setup(0, ip->ufs_ino.i_number, FBT_FLUSH, ap->a_p);
|
|
|
|
fbuf->fb_io_fd = ip->fufh[fufh_type].fh_id;
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
fb_delete(fbuf);
|
|
|
|
if (error == ENOSYS) {
|
|
|
|
fmp->undef_op |= UNDEF_FLUSH;
|
|
|
|
|
|
|
|
/* Implementing flush is optional so don't error. */
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_access(void *v)
|
|
|
|
{
|
|
|
|
struct vop_access_args *ap;
|
|
|
|
struct fusefs_node *ip;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct ucred *cred;
|
|
|
|
struct vattr vattr;
|
|
|
|
struct proc *p;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
ap = v;
|
|
|
|
p = ap->a_p;
|
|
|
|
cred = p->p_ucred;
|
|
|
|
ip = VTOI(ap->a_vp);
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
|
2023-06-20 20:38:03 +00:00
|
|
|
/*
|
2023-04-30 01:15:27 +00:00
|
|
|
* Only user that mounted the file system can access it unless
|
|
|
|
* allow_other mount option was specified.
|
|
|
|
*/
|
|
|
|
if (!fmp->allow_other && cred->cr_uid != fmp->mp->mnt_stat.f_owner)
|
|
|
|
return (EACCES);
|
|
|
|
|
|
|
|
if (!fmp->sess_init)
|
|
|
|
return (ENXIO);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Disallow write attempts on filesystems mounted read-only;
|
|
|
|
* unless the file is a socket, fifo, or a block or character
|
|
|
|
* device resident on the filesystem.
|
|
|
|
*/
|
|
|
|
if ((ap->a_mode & VWRITE) && (fmp->mp->mnt_flag & MNT_RDONLY)) {
|
|
|
|
switch (ap->a_vp->v_type) {
|
|
|
|
case VREG:
|
|
|
|
case VDIR:
|
|
|
|
case VLNK:
|
|
|
|
return (EROFS);
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = VOP_GETATTR(ap->a_vp, &vattr, cred, p)) != 0)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
return (vaccess(ap->a_vp->v_type, vattr.va_mode & ALLPERMS,
|
|
|
|
vattr.va_uid, vattr.va_gid, ap->a_mode,
|
|
|
|
ap->a_cred));
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_getattr(void *v)
|
|
|
|
{
|
|
|
|
struct vop_getattr_args *ap = v;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct vattr *vap = ap->a_vap;
|
|
|
|
struct proc *p = ap->a_p;
|
|
|
|
struct ucred *cred = p->p_ucred;
|
|
|
|
struct fusefs_node *ip;
|
|
|
|
struct fusebuf *fbuf;
|
|
|
|
struct stat *st;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
ip = VTOI(vp);
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
|
2023-06-20 20:38:03 +00:00
|
|
|
/*
|
2023-04-30 01:15:27 +00:00
|
|
|
* Only user that mounted the file system can access it unless
|
|
|
|
* allow_other mount option was specified. Return dummy values
|
|
|
|
* for the root inode in this situation.
|
|
|
|
*/
|
|
|
|
if (!fmp->allow_other && cred->cr_uid != fmp->mp->mnt_stat.f_owner) {
|
|
|
|
memset(vap, 0, sizeof(*vap));
|
|
|
|
vap->va_type = VNON;
|
|
|
|
if (vp->v_mount->mnt_flag & MNT_RDONLY)
|
|
|
|
vap->va_mode = S_IRUSR | S_IXUSR;
|
|
|
|
else
|
|
|
|
vap->va_mode = S_IRWXU;
|
|
|
|
vap->va_nlink = 1;
|
|
|
|
vap->va_uid = fmp->mp->mnt_stat.f_owner;
|
|
|
|
vap->va_gid = fmp->mp->mnt_stat.f_owner;
|
|
|
|
vap->va_fsid = fmp->mp->mnt_stat.f_fsid.val[0];
|
|
|
|
vap->va_fileid = ip->ufs_ino.i_number;
|
|
|
|
vap->va_size = S_BLKSIZE;
|
|
|
|
vap->va_blocksize = S_BLKSIZE;
|
|
|
|
vap->va_atime.tv_sec = fmp->mp->mnt_stat.f_ctime;
|
|
|
|
vap->va_mtime.tv_sec = fmp->mp->mnt_stat.f_ctime;
|
|
|
|
vap->va_ctime.tv_sec = fmp->mp->mnt_stat.f_ctime;
|
|
|
|
vap->va_rdev = fmp->dev;
|
|
|
|
vap->va_bytes = S_BLKSIZE;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fmp->sess_init)
|
|
|
|
return (ENXIO);
|
|
|
|
|
|
|
|
fbuf = fb_setup(0, ip->ufs_ino.i_number, FBT_GETATTR, p);
|
|
|
|
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
if (error) {
|
|
|
|
fb_delete(fbuf);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
st = &fbuf->fb_attr;
|
|
|
|
|
|
|
|
memset(vap, 0, sizeof(*vap));
|
|
|
|
vap->va_type = IFTOVT(st->st_mode);
|
|
|
|
vap->va_mode = st->st_mode & ~S_IFMT;
|
|
|
|
vap->va_nlink = st->st_nlink;
|
|
|
|
vap->va_uid = st->st_uid;
|
|
|
|
vap->va_gid = st->st_gid;
|
|
|
|
vap->va_fsid = fmp->mp->mnt_stat.f_fsid.val[0];
|
|
|
|
vap->va_fileid = st->st_ino;
|
|
|
|
vap->va_size = st->st_size;
|
|
|
|
vap->va_blocksize = st->st_blksize;
|
|
|
|
vap->va_atime = st->st_atim;
|
|
|
|
vap->va_mtime = st->st_mtim;
|
|
|
|
vap->va_ctime = st->st_ctim;
|
|
|
|
vap->va_rdev = st->st_rdev;
|
|
|
|
vap->va_bytes = st->st_blocks * S_BLKSIZE;
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_setattr(void *v)
|
|
|
|
{
|
|
|
|
struct vop_setattr_args *ap = v;
|
|
|
|
struct vattr *vap = ap->a_vap;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct fusefs_node *ip = VTOI(vp);
|
|
|
|
struct ucred *cred = ap->a_cred;
|
|
|
|
struct proc *p = ap->a_p;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct fusebuf *fbuf;
|
|
|
|
struct fb_io *io;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
/*
|
|
|
|
* Check for unsettable attributes.
|
|
|
|
*/
|
|
|
|
if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
|
|
|
|
(vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
|
|
|
|
(vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
|
|
|
|
((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL))
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
if (!fmp->sess_init)
|
|
|
|
return (ENXIO);
|
|
|
|
|
|
|
|
if (fmp->undef_op & UNDEF_SETATTR)
|
|
|
|
return (ENOSYS);
|
|
|
|
|
|
|
|
fbuf = fb_setup(sizeof(*io), ip->ufs_ino.i_number, FBT_SETATTR, p);
|
|
|
|
io = fbtod(fbuf, struct fb_io *);
|
|
|
|
io->fi_flags = 0;
|
|
|
|
|
|
|
|
if (vap->va_uid != (uid_t)VNOVAL) {
|
|
|
|
if (vp->v_mount->mnt_flag & MNT_RDONLY) {
|
|
|
|
error = EROFS;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
fbuf->fb_attr.st_uid = vap->va_uid;
|
|
|
|
io->fi_flags |= FUSE_FATTR_UID;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vap->va_gid != (gid_t)VNOVAL) {
|
|
|
|
if (vp->v_mount->mnt_flag & MNT_RDONLY) {
|
|
|
|
error = EROFS;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
fbuf->fb_attr.st_gid = vap->va_gid;
|
|
|
|
io->fi_flags |= FUSE_FATTR_GID;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vap->va_size != VNOVAL) {
|
|
|
|
/*
|
|
|
|
* Disallow write attempts on read-only file systems;
|
|
|
|
* unless the file is a socket, fifo, or a block or
|
|
|
|
* character device resident on the file system.
|
|
|
|
*/
|
|
|
|
switch (vp->v_type) {
|
|
|
|
case VDIR:
|
|
|
|
error = EISDIR;
|
|
|
|
goto out;
|
|
|
|
case VLNK:
|
|
|
|
case VREG:
|
|
|
|
if (vp->v_mount->mnt_flag & MNT_RDONLY) {
|
|
|
|
error = EROFS;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
fbuf->fb_attr.st_size = vap->va_size;
|
|
|
|
io->fi_flags |= FUSE_FATTR_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vap->va_atime.tv_nsec != VNOVAL) {
|
|
|
|
if (vp->v_mount->mnt_flag & MNT_RDONLY) {
|
|
|
|
error = EROFS;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
fbuf->fb_attr.st_atim = vap->va_atime;
|
|
|
|
io->fi_flags |= FUSE_FATTR_ATIME;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vap->va_mtime.tv_nsec != VNOVAL) {
|
|
|
|
if (vp->v_mount->mnt_flag & MNT_RDONLY) {
|
|
|
|
error = EROFS;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
fbuf->fb_attr.st_mtim = vap->va_mtime;
|
|
|
|
io->fi_flags |= FUSE_FATTR_MTIME;
|
|
|
|
}
|
|
|
|
/* XXX should set a flag if (vap->va_vaflags & VA_UTIMES_CHANGE) */
|
|
|
|
|
|
|
|
if (vap->va_mode != (mode_t)VNOVAL) {
|
|
|
|
if (vp->v_mount->mnt_flag & MNT_RDONLY) {
|
|
|
|
error = EROFS;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* chmod returns EFTYPE if the effective user ID is not the
|
|
|
|
* super-user, the mode includes the sticky bit (S_ISVTX), and
|
|
|
|
* path does not refer to a directory
|
|
|
|
*/
|
|
|
|
if (cred->cr_uid != 0 && vp->v_type != VDIR &&
|
|
|
|
(vap->va_mode & S_ISTXT)) {
|
|
|
|
error = EFTYPE;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
fbuf->fb_attr.st_mode = vap->va_mode & ALLPERMS;
|
|
|
|
io->fi_flags |= FUSE_FATTR_MODE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!io->fi_flags) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
if (error) {
|
|
|
|
if (error == ENOSYS)
|
|
|
|
fmp->undef_op |= UNDEF_SETATTR;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* truncate was successful, let uvm know */
|
|
|
|
if (vap->va_size != VNOVAL && vap->va_size != ip->filesize) {
|
|
|
|
ip->filesize = vap->va_size;
|
|
|
|
uvm_vnp_setsize(vp, vap->va_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
VN_KNOTE(ap->a_vp, NOTE_ATTRIB);
|
|
|
|
|
|
|
|
out:
|
|
|
|
fb_delete(fbuf);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_ioctl(void *v)
|
|
|
|
{
|
|
|
|
return (ENOTTY);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_link(void *v)
|
|
|
|
{
|
|
|
|
struct vop_link_args *ap = v;
|
|
|
|
struct vnode *dvp = ap->a_dvp;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct componentname *cnp = ap->a_cnp;
|
|
|
|
struct proc *p = cnp->cn_proc;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct fusefs_node *ip;
|
|
|
|
struct fusefs_node *dip;
|
|
|
|
struct fusebuf *fbuf;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
ip = VTOI(vp);
|
|
|
|
dip = VTOI(dvp);
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
|
|
|
|
if (!fmp->sess_init) {
|
|
|
|
VOP_ABORTOP(dvp, cnp);
|
|
|
|
error = ENXIO;
|
|
|
|
goto out2;
|
|
|
|
}
|
|
|
|
if (fmp->undef_op & UNDEF_LINK) {
|
|
|
|
VOP_ABORTOP(dvp, cnp);
|
|
|
|
error = ENOSYS;
|
|
|
|
goto out2;
|
|
|
|
}
|
|
|
|
if (vp->v_type == VDIR) {
|
|
|
|
VOP_ABORTOP(dvp, cnp);
|
|
|
|
error = EPERM;
|
|
|
|
goto out2;
|
|
|
|
}
|
|
|
|
if (dvp->v_mount != vp->v_mount) {
|
|
|
|
VOP_ABORTOP(dvp, cnp);
|
|
|
|
error = EXDEV;
|
|
|
|
goto out2;
|
|
|
|
}
|
|
|
|
if (dvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE))) {
|
|
|
|
VOP_ABORTOP(dvp, cnp);
|
|
|
|
goto out2;
|
|
|
|
}
|
|
|
|
|
|
|
|
fbuf = fb_setup(cnp->cn_namelen + 1, dip->ufs_ino.i_number,
|
|
|
|
FBT_LINK, p);
|
|
|
|
|
|
|
|
fbuf->fb_io_ino = ip->ufs_ino.i_number;
|
|
|
|
memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
|
|
|
|
fbuf->fb_dat[cnp->cn_namelen] = '\0';
|
|
|
|
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
if (error == ENOSYS)
|
|
|
|
fmp->undef_op |= UNDEF_LINK;
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
goto out1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
VN_KNOTE(vp, NOTE_LINK);
|
|
|
|
VN_KNOTE(dvp, NOTE_WRITE);
|
|
|
|
|
|
|
|
out1:
|
|
|
|
pool_put(&namei_pool, cnp->cn_pnbuf);
|
|
|
|
if (dvp != vp)
|
|
|
|
VOP_UNLOCK(vp);
|
|
|
|
out2:
|
|
|
|
vput(dvp);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_symlink(void *v)
|
|
|
|
{
|
|
|
|
struct vop_symlink_args *ap = v;
|
|
|
|
struct vnode **vpp = ap->a_vpp;
|
|
|
|
struct componentname *cnp = ap->a_cnp;
|
|
|
|
struct vnode *dvp = ap->a_dvp;
|
|
|
|
struct proc *p = cnp->cn_proc;
|
|
|
|
char *target = ap->a_target;
|
|
|
|
struct fusefs_node *dp;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct fusebuf *fbuf;
|
|
|
|
struct vnode *tdp;
|
|
|
|
int error = 0;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
dp = VTOI(dvp);
|
|
|
|
fmp = (struct fusefs_mnt *)dp->ufs_ino.i_ump;
|
|
|
|
|
|
|
|
if (!fmp->sess_init) {
|
|
|
|
error = ENXIO;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fmp->undef_op & UNDEF_SYMLINK) {
|
|
|
|
error = ENOSYS;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = strlen(target) + 1;
|
|
|
|
|
|
|
|
fbuf = fb_setup(len + cnp->cn_namelen + 1, dp->ufs_ino.i_number,
|
|
|
|
FBT_SYMLINK, p);
|
|
|
|
|
|
|
|
memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
|
|
|
|
fbuf->fb_dat[cnp->cn_namelen] = '\0';
|
|
|
|
memcpy(&fbuf->fb_dat[cnp->cn_namelen + 1], target, len);
|
|
|
|
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
if (error) {
|
|
|
|
if (error == ENOSYS)
|
|
|
|
fmp->undef_op |= UNDEF_SYMLINK;
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = VFS_VGET(fmp->mp, fbuf->fb_ino, &tdp))) {
|
|
|
|
fb_delete(fbuf);
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
tdp->v_type = VLNK;
|
|
|
|
VN_KNOTE(ap->a_dvp, NOTE_WRITE);
|
|
|
|
|
|
|
|
*vpp = tdp;
|
|
|
|
fb_delete(fbuf);
|
|
|
|
vput(tdp);
|
|
|
|
bad:
|
|
|
|
pool_put(&namei_pool, cnp->cn_pnbuf);
|
|
|
|
vput(dvp);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_readdir(void *v)
|
|
|
|
{
|
|
|
|
struct vop_readdir_args *ap = v;
|
|
|
|
struct fusefs_node *ip;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct fusebuf *fbuf;
|
|
|
|
struct vnode *vp;
|
|
|
|
struct proc *p;
|
|
|
|
struct uio *uio;
|
|
|
|
int error = 0, eofflag = 0, diropen = 0;
|
|
|
|
|
|
|
|
vp = ap->a_vp;
|
|
|
|
uio = ap->a_uio;
|
|
|
|
p = uio->uio_procp;
|
|
|
|
|
|
|
|
ip = VTOI(vp);
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
|
|
|
|
if (!fmp->sess_init)
|
|
|
|
return (ENXIO);
|
|
|
|
|
|
|
|
if (uio->uio_resid < sizeof(struct dirent))
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
if (ip->fufh[FUFH_RDONLY].fh_type == FUFH_INVALID) {
|
|
|
|
error = fusefs_file_open(fmp, ip, FUFH_RDONLY, O_RDONLY, 1, p);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
diropen = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (uio->uio_resid > 0) {
|
|
|
|
fbuf = fb_setup(0, ip->ufs_ino.i_number, FBT_READDIR, p);
|
|
|
|
|
|
|
|
fbuf->fb_io_fd = ip->fufh[FUFH_RDONLY].fh_id;
|
|
|
|
fbuf->fb_io_off = uio->uio_offset;
|
|
|
|
fbuf->fb_io_len = MIN(uio->uio_resid, fmp->max_read);
|
|
|
|
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
/*
|
|
|
|
* dirent was larger than residual space left in
|
|
|
|
* buffer.
|
|
|
|
*/
|
|
|
|
if (error == ENOBUFS)
|
|
|
|
error = 0;
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ack end of readdir */
|
|
|
|
if (fbuf->fb_len == 0) {
|
|
|
|
eofflag = 1;
|
|
|
|
fb_delete(fbuf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = uiomove(fbuf->fb_dat, fbuf->fb_len, uio))) {
|
|
|
|
fb_delete(fbuf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!error && ap->a_eofflag != NULL)
|
|
|
|
*ap->a_eofflag = eofflag;
|
|
|
|
|
|
|
|
if (diropen)
|
|
|
|
fusefs_file_close(fmp, ip, FUFH_RDONLY, O_RDONLY, 1, p);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_inactive(void *v)
|
|
|
|
{
|
|
|
|
struct vop_inactive_args *ap = v;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct proc *p = ap->a_p;
|
|
|
|
struct fusefs_node *ip = VTOI(vp);
|
|
|
|
struct fusefs_filehandle *fufh = NULL;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
int type, flags;
|
|
|
|
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
|
|
|
|
/* Close all open file handles. */
|
|
|
|
for (type = 0; type < FUFH_MAXTYPE; type++) {
|
|
|
|
fufh = &(ip->fufh[type]);
|
|
|
|
if (fufh->fh_type != FUFH_INVALID) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FUSE file systems expect the same flags to be sent
|
2023-06-20 20:38:03 +00:00
|
|
|
* on release that were sent on open. We don't have a
|
2023-04-30 01:15:27 +00:00
|
|
|
* record of them so make a best guess.
|
|
|
|
*/
|
|
|
|
switch (type) {
|
|
|
|
case FUFH_RDONLY:
|
|
|
|
flags = O_RDONLY;
|
|
|
|
break;
|
|
|
|
case FUFH_WRONLY:
|
|
|
|
flags = O_WRONLY;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
flags = O_RDWR;
|
|
|
|
}
|
|
|
|
|
|
|
|
fusefs_file_close(fmp, ip, fufh->fh_type, flags,
|
|
|
|
(vp->v_type == VDIR), p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VOP_UNLOCK(vp);
|
|
|
|
|
|
|
|
/* Don't return error to prevent kernel panic in vclean(9). */
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_readlink(void *v)
|
|
|
|
{
|
|
|
|
struct vop_readlink_args *ap = v;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct fusefs_node *ip;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct fusebuf *fbuf;
|
|
|
|
struct uio *uio;
|
|
|
|
struct proc *p;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
ip = VTOI(vp);
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
uio = ap->a_uio;
|
|
|
|
p = uio->uio_procp;
|
|
|
|
|
|
|
|
if (!fmp->sess_init)
|
|
|
|
return (ENXIO);
|
|
|
|
|
|
|
|
if (fmp->undef_op & UNDEF_READLINK)
|
|
|
|
return (ENOSYS);
|
|
|
|
|
|
|
|
fbuf = fb_setup(0, ip->ufs_ino.i_number, FBT_READLINK, p);
|
|
|
|
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
if (error == ENOSYS)
|
|
|
|
fmp->undef_op |= UNDEF_READLINK;
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
error = uiomove(fbuf->fb_dat, fbuf->fb_len, uio);
|
|
|
|
fb_delete(fbuf);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_reclaim(void *v)
|
|
|
|
{
|
|
|
|
struct vop_reclaim_args *ap = v;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct proc *p = ap->a_p;
|
|
|
|
struct fusefs_node *ip = VTOI(vp);
|
|
|
|
struct fusefs_filehandle *fufh = NULL;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct fusebuf *fbuf;
|
|
|
|
int type, error = 0;
|
|
|
|
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
|
|
|
|
/* Close opened files. */
|
|
|
|
for (type = 0; type < FUFH_MAXTYPE; type++) {
|
|
|
|
fufh = &(ip->fufh[type]);
|
|
|
|
if (fufh->fh_type != FUFH_INVALID) {
|
|
|
|
printf("fusefs: vnode being reclaimed is valid\n");
|
|
|
|
fusefs_file_close(fmp, ip, fufh->fh_type, type,
|
|
|
|
(vp->v_type == VDIR), ap->a_p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the fuse connection is opened ask libfuse to free the vnodes.
|
|
|
|
*/
|
|
|
|
if (fmp->sess_init && ip->ufs_ino.i_number != FUSE_ROOTINO) {
|
|
|
|
fbuf = fb_setup(0, ip->ufs_ino.i_number, FBT_RECLAIM, p);
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
if (error)
|
|
|
|
printf("fusefs: vnode reclaim failed: %d\n", error);
|
|
|
|
fb_delete(fbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove the inode from its hash chain.
|
|
|
|
*/
|
|
|
|
ufs_ihashrem(&ip->ufs_ino);
|
|
|
|
|
|
|
|
free(ip, M_FUSEFS, sizeof(*ip));
|
|
|
|
vp->v_data = NULL;
|
|
|
|
|
|
|
|
/* Must return success otherwise kernel panic in vclean(9). */
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_print(void *v)
|
|
|
|
{
|
|
|
|
struct vop_print_args *ap = v;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct fusefs_node *ip = VTOI(vp);
|
|
|
|
|
|
|
|
/* Complete the information given by vprint(). */
|
|
|
|
printf("tag VT_FUSE, hash id %u ", ip->ufs_ino.i_number);
|
|
|
|
printf("\n");
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_create(void *v)
|
|
|
|
{
|
|
|
|
struct vop_create_args *ap = v;
|
|
|
|
struct componentname *cnp = ap->a_cnp;
|
|
|
|
struct vnode **vpp = ap->a_vpp;
|
|
|
|
struct vnode *dvp = ap->a_dvp;
|
|
|
|
struct vattr *vap = ap->a_vap;
|
|
|
|
struct proc *p = cnp->cn_proc;
|
|
|
|
struct vnode *tdp = NULL;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct fusefs_node *ip;
|
|
|
|
struct fusebuf *fbuf;
|
|
|
|
int error = 0;
|
|
|
|
mode_t mode;
|
|
|
|
|
|
|
|
ip = VTOI(dvp);
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
mode = MAKEIMODE(vap->va_type, vap->va_mode);
|
|
|
|
|
|
|
|
if (!fmp->sess_init) {
|
|
|
|
VOP_ABORTOP(dvp, cnp);
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fmp->undef_op & UNDEF_MKNOD) {
|
|
|
|
VOP_ABORTOP(dvp, cnp);
|
|
|
|
return (ENOSYS);
|
|
|
|
}
|
|
|
|
|
|
|
|
fbuf = fb_setup(cnp->cn_namelen + 1, ip->ufs_ino.i_number,
|
|
|
|
FBT_MKNOD, p);
|
|
|
|
|
|
|
|
fbuf->fb_io_mode = mode;
|
|
|
|
|
|
|
|
memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
|
|
|
|
fbuf->fb_dat[cnp->cn_namelen] = '\0';
|
|
|
|
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
if (error) {
|
|
|
|
if (error == ENOSYS)
|
|
|
|
fmp->undef_op |= UNDEF_MKNOD;
|
|
|
|
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = VFS_VGET(fmp->mp, fbuf->fb_ino, &tdp)))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
tdp->v_type = IFTOVT(fbuf->fb_io_mode);
|
|
|
|
|
|
|
|
*vpp = tdp;
|
|
|
|
VN_KNOTE(ap->a_dvp, NOTE_WRITE);
|
|
|
|
out:
|
|
|
|
fb_delete(fbuf);
|
|
|
|
pool_put(&namei_pool, cnp->cn_pnbuf);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_mknod(void *v)
|
|
|
|
{
|
|
|
|
struct vop_mknod_args *ap = v;
|
|
|
|
struct componentname *cnp = ap->a_cnp;
|
|
|
|
struct vnode **vpp = ap->a_vpp;
|
|
|
|
struct vnode *dvp = ap->a_dvp;
|
|
|
|
struct vattr *vap = ap->a_vap;
|
|
|
|
struct proc *p = cnp->cn_proc;
|
|
|
|
struct vnode *tdp = NULL;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct fusefs_node *ip;
|
|
|
|
struct fusebuf *fbuf;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
ip = VTOI(dvp);
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
|
|
|
|
if (!fmp->sess_init) {
|
|
|
|
VOP_ABORTOP(dvp, cnp);
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fmp->undef_op & UNDEF_MKNOD) {
|
|
|
|
VOP_ABORTOP(dvp, cnp);
|
|
|
|
return (ENOSYS);
|
|
|
|
}
|
|
|
|
|
|
|
|
fbuf = fb_setup(cnp->cn_namelen + 1, ip->ufs_ino.i_number,
|
|
|
|
FBT_MKNOD, p);
|
|
|
|
|
|
|
|
fbuf->fb_io_mode = MAKEIMODE(vap->va_type, vap->va_mode);
|
|
|
|
if (vap->va_rdev != VNOVAL)
|
|
|
|
fbuf->fb_io_rdev = vap->va_rdev;
|
|
|
|
|
|
|
|
memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
|
|
|
|
fbuf->fb_dat[cnp->cn_namelen] = '\0';
|
|
|
|
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
if (error) {
|
|
|
|
if (error == ENOSYS)
|
|
|
|
fmp->undef_op |= UNDEF_MKNOD;
|
|
|
|
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = VFS_VGET(fmp->mp, fbuf->fb_ino, &tdp)))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
tdp->v_type = IFTOVT(fbuf->fb_io_mode);
|
|
|
|
|
|
|
|
*vpp = tdp;
|
|
|
|
VN_KNOTE(ap->a_dvp, NOTE_WRITE);
|
|
|
|
|
|
|
|
/* Remove inode so that it will be reloaded by VFS_VGET and
|
|
|
|
* checked to see if it is an alias of an existing entry in
|
|
|
|
* the inode cache.
|
|
|
|
*/
|
|
|
|
vput(*vpp);
|
|
|
|
(*vpp)->v_type = VNON;
|
|
|
|
vgone(*vpp);
|
|
|
|
*vpp = NULL;
|
|
|
|
out:
|
|
|
|
fb_delete(fbuf);
|
|
|
|
pool_put(&namei_pool, cnp->cn_pnbuf);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_read(void *v)
|
|
|
|
{
|
|
|
|
struct vop_read_args *ap = v;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct uio *uio = ap->a_uio;
|
|
|
|
struct proc *p = uio->uio_procp;
|
|
|
|
struct fusefs_node *ip;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct fusebuf *fbuf = NULL;
|
|
|
|
size_t size;
|
|
|
|
int error=0;
|
|
|
|
|
|
|
|
ip = VTOI(vp);
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
|
|
|
|
if (!fmp->sess_init)
|
|
|
|
return (ENXIO);
|
|
|
|
if (uio->uio_resid == 0)
|
|
|
|
return (error);
|
|
|
|
if (uio->uio_offset < 0)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
while (uio->uio_resid > 0) {
|
|
|
|
fbuf = fb_setup(0, ip->ufs_ino.i_number, FBT_READ, p);
|
|
|
|
|
|
|
|
size = MIN(uio->uio_resid, fmp->max_read);
|
|
|
|
fbuf->fb_io_fd = fusefs_fd_get(ip, FUFH_RDONLY);
|
|
|
|
fbuf->fb_io_off = uio->uio_offset;
|
|
|
|
fbuf->fb_io_len = size;
|
|
|
|
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
|
|
|
|
error = uiomove(fbuf->fb_dat, ulmin(size, fbuf->fb_len), uio);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (fbuf->fb_len < size)
|
|
|
|
break;
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
fbuf = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_write(void *v)
|
|
|
|
{
|
|
|
|
struct vop_write_args *ap = v;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct uio *uio = ap->a_uio;
|
|
|
|
struct proc *p = uio->uio_procp;
|
|
|
|
struct ucred *cred = p->p_ucred;
|
|
|
|
struct vattr vattr;
|
|
|
|
int ioflag = ap->a_ioflag;
|
|
|
|
struct fusefs_node *ip;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct fusebuf *fbuf = NULL;
|
|
|
|
size_t len, diff;
|
|
|
|
int error=0;
|
|
|
|
|
|
|
|
ip = VTOI(vp);
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
|
|
|
|
if (!fmp->sess_init)
|
|
|
|
return (ENXIO);
|
|
|
|
if (uio->uio_resid == 0)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
if (ioflag & IO_APPEND) {
|
|
|
|
if ((error = VOP_GETATTR(vp, &vattr, cred, p)) != 0)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
uio->uio_offset = vattr.va_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (uio->uio_resid > 0) {
|
|
|
|
len = MIN(uio->uio_resid, fmp->max_read);
|
|
|
|
fbuf = fb_setup(len, ip->ufs_ino.i_number, FBT_WRITE, p);
|
|
|
|
|
|
|
|
fbuf->fb_io_fd = fusefs_fd_get(ip, FUFH_WRONLY);
|
|
|
|
fbuf->fb_io_off = uio->uio_offset;
|
|
|
|
fbuf->fb_io_len = len;
|
|
|
|
|
|
|
|
if ((error = uiomove(fbuf->fb_dat, len, uio))) {
|
|
|
|
printf("fusefs: uio error %i\n", error);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
|
|
|
|
diff = len - fbuf->fb_io_len;
|
|
|
|
if (fbuf->fb_io_len > len) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
uio->uio_resid += diff;
|
|
|
|
uio->uio_offset -= diff;
|
|
|
|
|
|
|
|
if (uio->uio_offset > ip->filesize) {
|
|
|
|
ip->filesize = uio->uio_offset;
|
|
|
|
uvm_vnp_setsize(vp, uio->uio_offset);
|
|
|
|
}
|
|
|
|
uvm_vnp_uncache(vp);
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
fbuf = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_rename(void *v)
|
|
|
|
{
|
|
|
|
struct vop_rename_args *ap = v;
|
|
|
|
struct vnode *tvp = ap->a_tvp;
|
|
|
|
struct vnode *tdvp = ap->a_tdvp;
|
|
|
|
struct vnode *fvp = ap->a_fvp;
|
|
|
|
struct vnode *fdvp = ap->a_fdvp;
|
|
|
|
struct componentname *tcnp = ap->a_tcnp;
|
|
|
|
struct componentname *fcnp = ap->a_fcnp;
|
|
|
|
struct proc *p = fcnp->cn_proc;
|
|
|
|
struct fusefs_node *ip, *dp;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct fusebuf *fbuf;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if ((tcnp->cn_flags & HASBUF) == 0 ||
|
|
|
|
(fcnp->cn_flags & HASBUF) == 0)
|
|
|
|
panic("fusefs_rename: no name");
|
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
* Check for cross-device rename.
|
|
|
|
*/
|
|
|
|
if ((fvp->v_mount != tdvp->v_mount) ||
|
|
|
|
(tvp && (fvp->v_mount != tvp->v_mount))) {
|
|
|
|
error = EXDEV;
|
|
|
|
abortit:
|
|
|
|
VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */
|
|
|
|
if (tdvp == tvp)
|
|
|
|
vrele(tdvp);
|
|
|
|
else
|
|
|
|
vput(tdvp);
|
|
|
|
if (tvp)
|
|
|
|
vput(tvp);
|
|
|
|
VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
|
|
|
|
vrele(fdvp);
|
|
|
|
vrele(fvp);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If source and dest are the same, do nothing.
|
|
|
|
*/
|
|
|
|
if (tvp == fvp) {
|
|
|
|
error = 0;
|
|
|
|
goto abortit;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY)) != 0)
|
|
|
|
goto abortit;
|
|
|
|
dp = VTOI(fdvp);
|
|
|
|
ip = VTOI(fvp);
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Be sure we are not renaming ".", "..", or an alias of ".". This
|
|
|
|
* leads to a crippled directory tree. It's pretty tough to do a
|
|
|
|
* "ls" or "pwd" with the "." directory entry missing, and "cd .."
|
|
|
|
* doesn't work if the ".." entry is missing.
|
|
|
|
*/
|
|
|
|
if (fvp->v_type == VDIR) {
|
|
|
|
/*
|
|
|
|
* Avoid ".", "..", and aliases of "." for obvious reasons.
|
|
|
|
*/
|
|
|
|
if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
|
|
|
|
dp == ip ||
|
|
|
|
(fcnp->cn_flags & ISDOTDOT) ||
|
|
|
|
(tcnp->cn_flags & ISDOTDOT)) {
|
|
|
|
VOP_UNLOCK(fvp);
|
|
|
|
error = EINVAL;
|
|
|
|
goto abortit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VN_KNOTE(fdvp, NOTE_WRITE); /* XXX right place? */
|
|
|
|
|
|
|
|
if (!fmp->sess_init) {
|
|
|
|
error = ENXIO;
|
|
|
|
VOP_UNLOCK(fvp);
|
|
|
|
goto abortit;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fmp->undef_op & UNDEF_RENAME) {
|
|
|
|
error = ENOSYS;
|
|
|
|
VOP_UNLOCK(fvp);
|
|
|
|
goto abortit;
|
|
|
|
}
|
|
|
|
|
|
|
|
fbuf = fb_setup(fcnp->cn_namelen + tcnp->cn_namelen + 2,
|
|
|
|
dp->ufs_ino.i_number, FBT_RENAME, p);
|
|
|
|
|
|
|
|
memcpy(fbuf->fb_dat, fcnp->cn_nameptr, fcnp->cn_namelen);
|
|
|
|
fbuf->fb_dat[fcnp->cn_namelen] = '\0';
|
|
|
|
memcpy(fbuf->fb_dat + fcnp->cn_namelen + 1, tcnp->cn_nameptr,
|
|
|
|
tcnp->cn_namelen);
|
|
|
|
fbuf->fb_dat[fcnp->cn_namelen + tcnp->cn_namelen + 1] = '\0';
|
|
|
|
fbuf->fb_io_ino = VTOI(tdvp)->ufs_ino.i_number;
|
|
|
|
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
if (error == ENOSYS) {
|
|
|
|
fmp->undef_op |= UNDEF_RENAME;
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
VOP_UNLOCK(fvp);
|
|
|
|
goto abortit;
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
VN_KNOTE(fvp, NOTE_RENAME);
|
|
|
|
|
|
|
|
VOP_UNLOCK(fvp);
|
|
|
|
if (tdvp == tvp)
|
|
|
|
vrele(tdvp);
|
|
|
|
else
|
|
|
|
vput(tdvp);
|
|
|
|
if (tvp)
|
|
|
|
vput(tvp);
|
|
|
|
vrele(fdvp);
|
|
|
|
vrele(fvp);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_mkdir(void *v)
|
|
|
|
{
|
|
|
|
struct vop_mkdir_args *ap = v;
|
|
|
|
struct vnode *dvp = ap->a_dvp;
|
|
|
|
struct vnode **vpp = ap->a_vpp;
|
|
|
|
struct componentname *cnp = ap->a_cnp;
|
|
|
|
struct vattr *vap = ap->a_vap;
|
|
|
|
struct proc *p = cnp->cn_proc;
|
|
|
|
struct vnode *tdp = NULL;
|
|
|
|
struct fusefs_node *ip;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct fusebuf *fbuf;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
ip = VTOI(dvp);
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
|
|
|
|
|
|
|
|
if (!fmp->sess_init) {
|
|
|
|
error = ENXIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fmp->undef_op & UNDEF_MKDIR) {
|
|
|
|
error = ENOSYS;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
fbuf = fb_setup(cnp->cn_namelen + 1, ip->ufs_ino.i_number,
|
|
|
|
FBT_MKDIR, p);
|
|
|
|
|
|
|
|
fbuf->fb_io_mode = MAKEIMODE(vap->va_type, vap->va_mode);
|
|
|
|
memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
|
|
|
|
fbuf->fb_dat[cnp->cn_namelen] = '\0';
|
|
|
|
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
if (error) {
|
|
|
|
if (error == ENOSYS)
|
|
|
|
fmp->undef_op |= UNDEF_MKDIR;
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = VFS_VGET(fmp->mp, fbuf->fb_ino, &tdp))) {
|
|
|
|
fb_delete(fbuf);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
tdp->v_type = IFTOVT(fbuf->fb_io_mode);
|
|
|
|
|
|
|
|
*vpp = tdp;
|
|
|
|
VN_KNOTE(ap->a_dvp, NOTE_WRITE | NOTE_LINK);
|
|
|
|
fb_delete(fbuf);
|
|
|
|
out:
|
|
|
|
pool_put(&namei_pool, cnp->cn_pnbuf);
|
|
|
|
vput(dvp);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_rmdir(void *v)
|
|
|
|
{
|
|
|
|
struct vop_rmdir_args *ap = v;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct vnode *dvp = ap->a_dvp;
|
|
|
|
struct componentname *cnp = ap->a_cnp;
|
|
|
|
struct proc *p = cnp->cn_proc;
|
|
|
|
struct fusefs_node *ip, *dp;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct fusebuf *fbuf;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
ip = VTOI(vp);
|
|
|
|
dp = VTOI(dvp);
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
|
|
|
|
if (!fmp->sess_init) {
|
|
|
|
error = ENXIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fmp->undef_op & UNDEF_RMDIR) {
|
|
|
|
error = ENOSYS;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
|
|
|
|
|
|
|
|
fbuf = fb_setup(cnp->cn_namelen + 1, dp->ufs_ino.i_number,
|
|
|
|
FBT_RMDIR, p);
|
|
|
|
memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
|
|
|
|
fbuf->fb_dat[cnp->cn_namelen] = '\0';
|
|
|
|
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
if (error == ENOSYS)
|
|
|
|
fmp->undef_op |= UNDEF_RMDIR;
|
|
|
|
if (error != ENOTEMPTY)
|
|
|
|
VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
vput(dvp);
|
|
|
|
dvp = NULL;
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
out:
|
|
|
|
if (dvp)
|
|
|
|
vput(dvp);
|
|
|
|
VN_KNOTE(vp, NOTE_DELETE);
|
|
|
|
pool_put(&namei_pool, cnp->cn_pnbuf);
|
|
|
|
vput(vp);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_remove(void *v)
|
|
|
|
{
|
|
|
|
struct vop_remove_args *ap = v;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct vnode *dvp = ap->a_dvp;
|
|
|
|
struct componentname *cnp = ap->a_cnp;
|
|
|
|
struct proc *p = cnp->cn_proc;
|
|
|
|
struct fusefs_node *ip;
|
|
|
|
struct fusefs_node *dp;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct fusebuf *fbuf;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
ip = VTOI(vp);
|
|
|
|
dp = VTOI(dvp);
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
|
|
|
|
if (!fmp->sess_init) {
|
|
|
|
error = ENXIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fmp->undef_op & UNDEF_REMOVE) {
|
|
|
|
error = ENOSYS;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
fbuf = fb_setup(cnp->cn_namelen + 1, dp->ufs_ino.i_number,
|
|
|
|
FBT_UNLINK, p);
|
|
|
|
memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
|
|
|
|
fbuf->fb_dat[cnp->cn_namelen] = '\0';
|
|
|
|
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
if (error) {
|
|
|
|
if (error == ENOSYS)
|
|
|
|
fmp->undef_op |= UNDEF_REMOVE;
|
|
|
|
|
|
|
|
fb_delete(fbuf);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
VN_KNOTE(vp, NOTE_DELETE);
|
|
|
|
VN_KNOTE(dvp, NOTE_WRITE);
|
|
|
|
fb_delete(fbuf);
|
|
|
|
out:
|
|
|
|
pool_put(&namei_pool, cnp->cn_pnbuf);
|
|
|
|
if (dvp == vp)
|
|
|
|
vrele(vp);
|
|
|
|
else
|
|
|
|
vput(vp);
|
|
|
|
vput(dvp);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_strategy(void *v)
|
|
|
|
{
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_lock(void *v)
|
|
|
|
{
|
|
|
|
struct vop_lock_args *ap = v;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
|
|
|
|
return rrw_enter(&VTOI(vp)->ufs_ino.i_lock, ap->a_flags & LK_RWFLAGS);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_unlock(void *v)
|
|
|
|
{
|
|
|
|
struct vop_unlock_args *ap = v;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
|
|
|
|
rrw_exit(&VTOI(vp)->ufs_ino.i_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_islocked(void *v)
|
|
|
|
{
|
|
|
|
struct vop_islocked_args *ap = v;
|
|
|
|
|
|
|
|
return rrw_status(&VTOI(ap->a_vp)->ufs_ino.i_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_advlock(void *v)
|
|
|
|
{
|
|
|
|
struct vop_advlock_args *ap = v;
|
|
|
|
struct fusefs_node *ip = VTOI(ap->a_vp);
|
|
|
|
|
|
|
|
return (lf_advlock(&ip->ufs_ino.i_lockf, ip->filesize, ap->a_id,
|
|
|
|
ap->a_op, ap->a_fl, ap->a_flags));
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fusefs_fsync(void *v)
|
|
|
|
{
|
|
|
|
struct vop_fsync_args *ap = v;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct proc *p = ap->a_p;
|
|
|
|
struct fusefs_node *ip;
|
|
|
|
struct fusefs_mnt *fmp;
|
|
|
|
struct fusefs_filehandle *fufh;
|
|
|
|
struct fusebuf *fbuf;
|
|
|
|
int type, error = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Can't write to directory file handles so no need to fsync.
|
|
|
|
* FUSE has fsyncdir but it doesn't make sense on OpenBSD.
|
|
|
|
*/
|
|
|
|
if (vp->v_type == VDIR)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
ip = VTOI(vp);
|
|
|
|
fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;
|
|
|
|
|
|
|
|
if (!fmp->sess_init)
|
|
|
|
return (ENXIO);
|
|
|
|
|
|
|
|
/* Implementing fsync is optional so don't error. */
|
|
|
|
if (fmp->undef_op & UNDEF_FSYNC)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
/* Sync all writeable file descriptors. */
|
|
|
|
for (type = 0; type < FUFH_MAXTYPE; type++) {
|
|
|
|
fufh = &(ip->fufh[type]);
|
|
|
|
if (fufh->fh_type == FUFH_WRONLY ||
|
|
|
|
fufh->fh_type == FUFH_RDWR) {
|
|
|
|
|
|
|
|
fbuf = fb_setup(0, ip->ufs_ino.i_number, FBT_FSYNC, p);
|
|
|
|
fbuf->fb_io_fd = fufh->fh_id;
|
|
|
|
|
|
|
|
/* Always behave as if ap->a_waitfor = MNT_WAIT. */
|
|
|
|
error = fb_queue(fmp->dev, fbuf);
|
|
|
|
fb_delete(fbuf);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error == ENOSYS) {
|
|
|
|
fmp->undef_op |= UNDEF_FSYNC;
|
|
|
|
|
|
|
|
/* Implementing fsync is optional so don't error. */
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|