<commit_before>/* $OpenBSD: fuse_vnops.c,v 1.11 2013/12/03 09:59:40 syl Exp $ */
/*
 * Copyright (c) 2012-2013 <NAME> <<EMAIL>>
 *
 * 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/dirent.h>
#include <sys/fcntl.h>
#include <sys/lockf.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/poll.h>
#include <sys/specdev.h>
#include <sys/statvfs.h>
#include <sys/vnode.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_poll(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 *);

/* Prototypes for fusefs kqfilter */
int	filt_fusefsreadwrite(struct knote *, long);
int	filt_fusefsvnode(struct knote *, long);
void	filt_fusefsdetach(struct knote *);

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_poll	= fusefs_poll,
	.vop_kqfilter	= fusefs_kqfilter,
	.vop_fsync	= nullop,
	.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,
};

struct filterops fusefsreadwrite_filtops =
	{ 1, NULL, filt_fusefsdetach, filt_fusefsreadwrite };
struct filterops fusefsvnode_filtops =
	{ 1, NULL, filt_fusefsdetach, 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 = &fusefsreadwrite_filtops;
		break;
	case EVFILT_WRITE:
		kn->kn_fop = &fusefsreadwrite_filtops;
		break;
	case EVFILT_VNODE:
		kn->kn_fop = &fusefsvnode_filtops;
		break;
	default:
		return (EINVAL);
	}

	kn->kn_hook = (caddr_t)vp;

	SLIST_INSERT_HEAD(&vp->v_selectinfo.si_note, kn, kn_selnext);

	return (0);
}

void
filt_fusefsdetach(struct knote *kn)
{
	struct vnode *vp = (struct vnode *)kn->kn_hook;

	SLIST_REMOVE(&vp->v_selectinfo.si_note, kn, knote, kn_selnext);
}

int
filt_fusefsreadwrite(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);
	}

	return (kn->kn_data != 0);
}

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);
}

void
update_vattr(struct mount *mp, struct vattr *v)
{
	v->va_fsid = mp->mnt_stat.f_fsid.val[0];
	v->va_type = IFTOVT(v->va_mode);
#if (S_BLKSIZE == 512)
	v->va_bytes = v->va_bytes << 9;
#else
	v->va_bytes = v->va_bytes * S_BLKSIZE;
#endif
	v->va_mode = v->va_mode & ~S_IFMT;
}

int
fusefs_open(void *v)
{
	struct vop_open_args *ap;
	struct fusefs_node *ip;
	struct fusefs_mnt *fmp;
	enum fufh_type fufh_type = FUFH_RDONLY;
	int flags = O_RDONLY;
	int error;
	int isdir;

	ap = v;
	ip = VTOI(ap->a_vp);
	fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;

	if (!fmp->sess_init)
		return (0);

	isdir = 0;
	if (ip->vtype == VDIR)
		isdir = 1;
	else {
		if ((ap->a_mode & FREAD) && (ap->a_mode & FWRITE)) {
			fufh_type = FUFH_RDWR;
			flags = O_RDWR;
		} else if (ap->a_mode  & (FWRITE)) {
			fufh_type = FUFH_WRONLY;
			flags = O_WRONLY;
		}
	}

	/* already open i think all is ok */
	if (ip->fufh[fufh_type].fh_type != FUFH_INVALID)
		return (0);

	error = fusefs_file_open(fmp, ip, fufh_type, flags, isdir, ap->a_p);
	if (error)
		return (error);

	return (error);
}

int
fusefs_close(void *v)
{
	struct vop_close_args *ap;
	struct fusefs_node *ip;
	struct fusefs_mnt *fmp;
	enum fufh_type fufh_type = FUFH_RDONLY;
	int isdir, i;

	ap = v;
	ip = VTOI(ap->a_vp);
	fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;

	if (!fmp->sess_init)
		return (0);

	if (ip->vtype == VDIR) {
		isdir = 1;

		if (ip->fufh[fufh_type].fh_type != FUFH_INVALID)
			return (fusefs_file_close(fmp, ip, fufh_type, O_RDONLY,
			    isdir, ap->a_p));
	} else {
		if (ap->a_fflag & IO_NDELAY)
			return (0);

		if ((ap->a_fflag & FREAD) && (ap->a_fflag & FWRITE))
			fufh_type = FUFH_RDWR;
		else if (ap->a_fflag  & (FWRITE))
			fufh_type = FUFH_WRONLY;
	}

	/*
	 * if fh not valid lookup for another valid fh in vnode.
	 * Do we need panic if there's not a valid fh ?
	 */
	if (ip->fufh[fufh_type].fh_type != FUFH_INVALID) {
		for (i = 0; i < FUFH_MAXTYPE; i++)
			if (ip->fufh[fufh_type].fh_type != FUFH_INVALID)
				break;
		return (0);
	}

	return (0);
}

int
fusefs_access(void *v)
{
	struct vop_access_args *ap;
	struct fusefs_node *ip;
	struct fusefs_mnt *fmp;
	struct fusebuf *fbuf;
	struct proc *p;
	uint32_t mask = 0;
	int error = 0;

	ap = v;
	p = ap->a_p;
	ip = VTOI(ap->a_vp);
	fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;

	if (!fmp->sess_init || (fmp->undef_op & UNDEF_ACCESS))
		goto system_check;

	if (ap->a_vp->v_type == VLNK)
		goto system_check;

	if (ap->a_vp->v_type == VREG && (ap->a_mode & VWRITE & VEXEC))
		goto system_check;

	if ((ap->a_mode & VWRITE) && (fmp->mp->mnt_flag & MNT_RDONLY))
		return (EACCES);

	if ((ap->a_mode & VWRITE) != 0)
		mask |= 0x2;

	if ((ap->a_mode & VREAD) != 0)
		mask |= 0x4;

	if ((ap->a_mode & VEXEC) != 0)
		mask |= 0x1;

	fbuf = fb_setup(0, ip->ufs_ino.i_number, FBT_ACCESS, p);
	fbuf->fb_io_mode = mask;

	error = fb_queue(fmp->dev, fbuf);
	if (error) {
		if (error == ENOSYS) {
			fmp->undef_op |= UNDEF_ACCESS;
			fb_delete(fbuf);
			goto system_check;
		}

		printf("fusefs: access error %i\n", error);
		fb_delete(fbuf);
		return (error);
	}

	fb_delete(fbuf);
	return (error);

system_check:
	return (vaccess(ap->a_vp->v_type, ip->cached_attrs.va_mode & ALLPERMS,
	    ip->cached_attrs.va_uid, ip->cached_attrs.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 fusefs_node *ip;
	struct fusebuf *fbuf;
	int error = 0;

	ip = VTOI(vp);
	fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;

	if (!fmp->sess_init)
		goto fake;

	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);
	}

	update_vattr(fmp->mp, &fbuf->fb_vattr);
	memcpy(vap, &fbuf->fb_vattr, sizeof(*vap));
	memcpy(&ip->cached_attrs, vap, sizeof(*vap));
	fb_delete(fbuf);
	return (error);
fake:
	bzero(vap, sizeof(*vap));
	vap->va_type = vp->v_type;
	return (0);
}

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 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 || (fmp->undef_op & UNDEF_SETATTR))
		return (ENXIO);

	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_vattr.va_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_vattr.va_gid = vap->va_gid;
		io->fi_flags |= FUSE_FATTR_GID;
	}

	if (vap->va_size != VNOVAL) {
		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;
			}
		default:
			break;
		}

		/*XXX to finish*/
	}

	if (vap->va_atime.tv_sec != VNOVAL) {
		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
			error = EROFS;
			goto out;
		}
		fbuf->fb_vattr.va_atime.tv_sec = vap->va_atime.tv_sec;
		fbuf->fb_vattr.va_atime.tv_nsec = vap->va_atime.tv_nsec;
		io->fi_flags |= FUSE_FATTR_ATIME;
	}<commit_msg>Add support for truncate in fuse.<commit_after>/* $OpenBSD: fuse_vnops.c,v 1.12 2013/12/20 22:03:26 syl Exp $ */
/*
 * Copyright (c) 2012-2013 <NAME> <<EMAIL>>
 *
 * 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/dirent.h>
#include <sys/fcntl.h>
#include <sys/lockf.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/poll.h>
#include <sys/specdev.h>
#include <sys/statvfs.h>
#include <sys/vnode.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_poll(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 *);

/* Prototypes for fusefs kqfilter */
int	filt_fusefsreadwrite(struct knote *, long);
int	filt_fusefsvnode(struct knote *, long);
void	filt_fusefsdetach(struct knote *);

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_poll	= fusefs_poll,
	.vop_kqfilter	= fusefs_kqfilter,
	.vop_fsync	= nullop,
	.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,
};

struct filterops fusefsreadwrite_filtops =
	{ 1, NULL, filt_fusefsdetach, filt_fusefsreadwrite };
struct filterops fusefsvnode_filtops =
	{ 1, NULL, filt_fusefsdetach, 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 = &fusefsreadwrite_filtops;
		break;
	case EVFILT_WRITE:
		kn->kn_fop = &fusefsreadwrite_filtops;
		break;
	case EVFILT_VNODE:
		kn->kn_fop = &fusefsvnode_filtops;
		break;
	default:
		return (EINVAL);
	}

	kn->kn_hook = (caddr_t)vp;

	SLIST_INSERT_HEAD(&vp->v_selectinfo.si_note, kn, kn_selnext);

	return (0);
}

void
filt_fusefsdetach(struct knote *kn)
{
	struct vnode *vp = (struct vnode *)kn->kn_hook;

	SLIST_REMOVE(&vp->v_selectinfo.si_note, kn, knote, kn_selnext);
}

int
filt_fusefsreadwrite(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);
	}

	return (kn->kn_data != 0);
}

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);
}

void
update_vattr(struct mount *mp, struct vattr *v)
{
	v->va_fsid = mp->mnt_stat.f_fsid.val[0];
	v->va_type = IFTOVT(v->va_mode);
#if (S_BLKSIZE == 512)
	v->va_bytes = v->va_bytes << 9;
#else
	v->va_bytes = v->va_bytes * S_BLKSIZE;
#endif
	v->va_mode = v->va_mode & ~S_IFMT;
}

int
fusefs_open(void *v)
{
	struct vop_open_args *ap;
	struct fusefs_node *ip;
	struct fusefs_mnt *fmp;
	enum fufh_type fufh_type = FUFH_RDONLY;
	int flags = O_RDONLY;
	int error;
	int isdir;

	ap = v;
	ip = VTOI(ap->a_vp);
	fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;

	if (!fmp->sess_init)
		return (0);

	isdir = 0;
	if (ip->vtype == VDIR)
		isdir = 1;
	else {
		if ((ap->a_mode & FREAD) && (ap->a_mode & FWRITE)) {
			fufh_type = FUFH_RDWR;
			flags = O_RDWR;
		} else if (ap->a_mode  & (FWRITE)) {
			fufh_type = FUFH_WRONLY;
			flags = O_WRONLY;
		}
	}

	/* already open i think all is ok */
	if (ip->fufh[fufh_type].fh_type != FUFH_INVALID)
		return (0);

	error = fusefs_file_open(fmp, ip, fufh_type, flags, isdir, ap->a_p);
	if (error)
		return (error);

	return (error);
}

int
fusefs_close(void *v)
{
	struct vop_close_args *ap;
	struct fusefs_node *ip;
	struct fusefs_mnt *fmp;
	enum fufh_type fufh_type = FUFH_RDONLY;
	int isdir, i;

	ap = v;
	ip = VTOI(ap->a_vp);
	fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;

	if (!fmp->sess_init)
		return (0);

	if (ip->vtype == VDIR) {
		isdir = 1;

		if (ip->fufh[fufh_type].fh_type != FUFH_INVALID)
			return (fusefs_file_close(fmp, ip, fufh_type, O_RDONLY,
			    isdir, ap->a_p));
	} else {
		if (ap->a_fflag & IO_NDELAY)
			return (0);

		if ((ap->a_fflag & FREAD) && (ap->a_fflag & FWRITE))
			fufh_type = FUFH_RDWR;
		else if (ap->a_fflag  & (FWRITE))
			fufh_type = FUFH_WRONLY;
	}

	/*
	 * if fh not valid lookup for another valid fh in vnode.
	 * Do we need panic if there's not a valid fh ?
	 */
	if (ip->fufh[fufh_type].fh_type != FUFH_INVALID) {
		for (i = 0; i < FUFH_MAXTYPE; i++)
			if (ip->fufh[fufh_type].fh_type != FUFH_INVALID)
				break;
		return (0);
	}

	return (0);
}

int
fusefs_access(void *v)
{
	struct vop_access_args *ap;
	struct fusefs_node *ip;
	struct fusefs_mnt *fmp;
	struct fusebuf *fbuf;
	struct proc *p;
	uint32_t mask = 0;
	int error = 0;

	ap = v;
	p = ap->a_p;
	ip = VTOI(ap->a_vp);
	fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;

	if (!fmp->sess_init || (fmp->undef_op & UNDEF_ACCESS))
		goto system_check;

	if (ap->a_vp->v_type == VLNK)
		goto system_check;

	if (ap->a_vp->v_type == VREG && (ap->a_mode & VWRITE & VEXEC))
		goto system_check;

	if ((ap->a_mode & VWRITE) && (fmp->mp->mnt_flag & MNT_RDONLY))
		return (EACCES);

	if ((ap->a_mode & VWRITE) != 0)
		mask |= 0x2;

	if ((ap->a_mode & VREAD) != 0)
		mask |= 0x4;

	if ((ap->a_mode & VEXEC) != 0)
		mask |= 0x1;

	fbuf = fb_setup(0, ip->ufs_ino.i_number, FBT_ACCESS, p);
	fbuf->fb_io_mode = mask;

	error = fb_queue(fmp->dev, fbuf);
	if (error) {
		if (error == ENOSYS) {
			fmp->undef_op |= UNDEF_ACCESS;
			fb_delete(fbuf);
			goto system_check;
		}

		printf("fusefs: access error %i\n", error);
		fb_delete(fbuf);
		return (error);
	}

	fb_delete(fbuf);
	return (error);

system_check:
	return (vaccess(ap->a_vp->v_type, ip->cached_attrs.va_mode & ALLPERMS,
	    ip->cached_attrs.va_uid, ip->cached_attrs.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 fusefs_node *ip;
	struct fusebuf *fbuf;
	int error = 0;

	ip = VTOI(vp);
	fmp = (struct fusefs_mnt *)ip->ufs_ino.i_ump;

	if (!fmp->sess_init)
		goto fake;

	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);
	}

	update_vattr(fmp->mp, &fbuf->fb_vattr);
	memcpy(vap, &fbuf->fb_vattr, sizeof(*vap));
	memcpy(&ip->cached_attrs, vap, sizeof(*vap));
	fb_delete(fbuf);
	return (error);
fake:
	bzero(vap, sizeof(*vap));
	vap->va_type = vp->v_type;
	return (0);
}

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 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 || (fmp->undef_op & UNDEF_SETATTR))
		return (ENXIO);

	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_vattr.va_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_vattr.va_gid = vap->va_gid;
		io->fi_flags |= FUSE_FATTR_GID;
	}

	if (vap->va_size != VNOVAL) {
		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_vattr.va_size = vap->va_size;
		io->fi_flags |= FUSE_FATTR_SIZE;
	}

	if (vap->va_atime.tv_sec != VNOVAL) {
		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
			error = EROFS;
			goto out;
		}
		fbuf->fb_vattr.va_atime.tv_sec = vap->va_atime.tv_sec;
		fbuf->fb_vattr.va_atime.tv_nsec = vap->va_atime.tv_nsec;
		io->fi_flags |= FUSE_FATTR_ATIME;
	}
{'fraction_non_alphanumeric': 0.1479803335582763, 'fraction_numerical': 0.006362672322375398, 'mean_word_length': 4.055311890838206, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 290, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5361886', 'n_tokens_mistral': 10058, 'n_tokens_neox': 9350, 'n_words': 1957}