#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filio.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/unistd.h>
#include <sys/vnode.h>
#include <sys/namei.h>
#include <sys/mountctl.h>
#include <sys/vfs_quota.h>
#include <sys/uio.h>
#include <machine/limits.h>
#include <vm/vm.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_pager.h>
#include <vm/vnode_pager.h>
static int vop_nolookup (struct vop_old_lookup_args *);
static int vop_nostrategy (struct vop_strategy_args *);
struct vop_ops default_vnode_vops = {
.vop_default = vop_eopnotsupp,
.vop_advlock = (void *)vop_einval,
.vop_fsync = (void *)vop_null,
.vop_fdatasync = vop_stdfdatasync,
.vop_ioctl = vop_stdioctl,
.vop_mmap = (void *)vop_einval,
.vop_old_lookup = vop_nolookup,
.vop_open = vop_stdopen,
.vop_close = vop_stdclose,
.vop_getattr_lite = vop_stdgetattr_lite,
.vop_pathconf = vop_stdpathconf,
.vop_readlink = (void *)vop_einval,
.vop_reallocblks = (void *)vop_eopnotsupp,
.vop_strategy = vop_nostrategy,
.vop_getacl = (void *)vop_eopnotsupp,
.vop_setacl = (void *)vop_eopnotsupp,
.vop_aclcheck = (void *)vop_eopnotsupp,
.vop_getextattr = (void *)vop_eopnotsupp,
.vop_setextattr = (void *)vop_eopnotsupp,
.vop_markatime = vop_stdmarkatime,
.vop_allocate = vop_stdallocate,
.vop_nresolve = vop_compat_nresolve,
.vop_nlookupdotdot = vop_compat_nlookupdotdot,
.vop_ncreate = vop_compat_ncreate,
.vop_nmkdir = vop_compat_nmkdir,
.vop_nmknod = vop_compat_nmknod,
.vop_nlink = vop_compat_nlink,
.vop_nsymlink = vop_compat_nsymlink,
.vop_nwhiteout = vop_compat_nwhiteout,
.vop_nremove = vop_compat_nremove,
.vop_nrmdir = vop_compat_nrmdir,
.vop_nrename = vop_compat_nrename,
.vop_mountctl = vop_stdmountctl
};
VNODEOP_SET(default_vnode_vops);
int
vop_eopnotsupp(struct vop_generic_args *ap)
{
return (EOPNOTSUPP);
}
int
vop_ebadf(struct vop_generic_args *ap)
{
return (EBADF);
}
int
vop_enotty(struct vop_generic_args *ap)
{
return (ENOTTY);
}
int
vop_einval(struct vop_generic_args *ap)
{
return (EINVAL);
}
int
vop_stdmarkatime(struct vop_markatime_args *ap)
{
return (EOPNOTSUPP);
}
int
vop_null(struct vop_generic_args *ap)
{
return (0);
}
int
vop_defaultop(struct vop_generic_args *ap)
{
return (VOCALL(&default_vnode_vops, ap));
}
int
vop_compat_nresolve(struct vop_nresolve_args *ap)
{
int error;
struct vnode *dvp;
struct vnode *vp;
struct nchandle *nch;
struct namecache *ncp;
struct componentname cnp;
nch = ap->a_nch;
ncp = nch->ncp;
dvp = ap->a_dvp;
if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n",
ncp, ncp->nc_name);
return(EAGAIN);
}
bzero(&cnp, sizeof(cnp));
cnp.cn_nameiop = NAMEI_LOOKUP;
cnp.cn_flags = 0;
cnp.cn_nameptr = ncp->nc_name;
cnp.cn_namelen = ncp->nc_nlen;
cnp.cn_cred = ap->a_cred;
cnp.cn_td = curthread;
error = vop_old_lookup(ap->a_head.a_ops, dvp, &vp, &cnp);
if (error == 0)
vn_unlock(vp);
if ((cnp.cn_flags & CNP_PDIRUNLOCK) == 0)
vn_unlock(dvp);
if ((ncp->nc_flag & NCF_UNRESOLVED) == 0) {
if (error == 0)
vrele(vp);
} else if (error == 0) {
KKASSERT(vp != NULL);
cache_setvp(nch, vp);
vrele(vp);
} else if (error == ENOENT) {
KKASSERT(vp == NULL);
if (cnp.cn_flags & CNP_ISWHITEOUT)
ncp->nc_flag |= NCF_WHITEOUT;
cache_setvp(nch, NULL);
}
vrele(dvp);
return (error);
}
int
vop_compat_nlookupdotdot(struct vop_nlookupdotdot_args *ap)
{
struct componentname cnp;
int error;
*ap->a_vpp = NULL;
if ((error = vget(ap->a_dvp, LK_EXCLUSIVE)) != 0)
return (error);
if (ap->a_dvp->v_type != VDIR) {
vput(ap->a_dvp);
return (ENOTDIR);
}
bzero(&cnp, sizeof(cnp));
cnp.cn_nameiop = NAMEI_LOOKUP;
cnp.cn_flags = CNP_ISDOTDOT;
cnp.cn_nameptr = "..";
cnp.cn_namelen = 2;
cnp.cn_cred = ap->a_cred;
cnp.cn_td = curthread;
error = vop_old_lookup(ap->a_head.a_ops, ap->a_dvp, ap->a_vpp, &cnp);
if (cnp.cn_flags & CNP_PDIRUNLOCK)
vrele(ap->a_dvp);
else
vput(ap->a_dvp);
return (error);
}
int
vop_compat_ncreate(struct vop_ncreate_args *ap)
{
struct thread *td = curthread;
struct componentname cnp;
struct nchandle *nch;
struct namecache *ncp;
struct vnode *dvp;
int error;
nch = ap->a_nch;
dvp = ap->a_dvp;
ncp = nch->ncp;
if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n",
ncp, ncp->nc_name);
return(EAGAIN);
}
bzero(&cnp, sizeof(cnp));
cnp.cn_nameiop = NAMEI_CREATE;
cnp.cn_flags = CNP_LOCKPARENT;
cnp.cn_nameptr = ncp->nc_name;
cnp.cn_namelen = ncp->nc_nlen;
cnp.cn_cred = ap->a_cred;
cnp.cn_td = td;
*ap->a_vpp = NULL;
error = vop_old_lookup(ap->a_head.a_ops, dvp, ap->a_vpp, &cnp);
if (error == EJUSTRETURN) {
KKASSERT((cnp.cn_flags & CNP_PDIRUNLOCK) == 0);
error = VOP_OLD_CREATE(dvp, ap->a_vpp, &cnp, ap->a_vap);
if (error == 0) {
cache_setunresolved(nch);
cache_setvp(nch, *ap->a_vpp);
}
} else {
if (error == 0) {
vput(*ap->a_vpp);
*ap->a_vpp = NULL;
error = EEXIST;
}
KKASSERT(*ap->a_vpp == NULL);
}
if ((cnp.cn_flags & CNP_PDIRUNLOCK) == 0)
vn_unlock(dvp);
vrele(dvp);
return (error);
}
int
vop_compat_nmkdir(struct vop_nmkdir_args *ap)
{
struct thread *td = curthread;
struct componentname cnp;
struct nchandle *nch;
struct namecache *ncp;
struct vnode *dvp;
int error;
nch = ap->a_nch;
ncp = nch->ncp;
dvp = ap->a_dvp;
if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n",
ncp, ncp->nc_name);
return(EAGAIN);
}
bzero(&cnp, sizeof(cnp));
cnp.cn_nameiop = NAMEI_CREATE;
cnp.cn_flags = CNP_LOCKPARENT;
cnp.cn_nameptr = ncp->nc_name;
cnp.cn_namelen = ncp->nc_nlen;
cnp.cn_cred = ap->a_cred;
cnp.cn_td = td;
*ap->a_vpp = NULL;
error = vop_old_lookup(ap->a_head.a_ops, dvp, ap->a_vpp, &cnp);
if (error == EJUSTRETURN) {
KKASSERT((cnp.cn_flags & CNP_PDIRUNLOCK) == 0);
error = VOP_OLD_MKDIR(dvp, ap->a_vpp, &cnp, ap->a_vap);
if (error == 0) {
cache_setunresolved(nch);
cache_setvp(nch, *ap->a_vpp);
}
} else {
if (error == 0) {
vput(*ap->a_vpp);
*ap->a_vpp = NULL;
error = EEXIST;
}
KKASSERT(*ap->a_vpp == NULL);
}
if ((cnp.cn_flags & CNP_PDIRUNLOCK) == 0)
vn_unlock(dvp);
vrele(dvp);
return (error);
}
int
vop_compat_nmknod(struct vop_nmknod_args *ap)
{
struct thread *td = curthread;
struct componentname cnp;
struct nchandle *nch;
struct namecache *ncp;
struct vnode *dvp;
int error;
nch = ap->a_nch;
ncp = nch->ncp;
dvp = ap->a_dvp;
if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n",
ncp, ncp->nc_name);
return(EAGAIN);
}
bzero(&cnp, sizeof(cnp));
cnp.cn_nameiop = NAMEI_CREATE;
cnp.cn_flags = CNP_LOCKPARENT;
cnp.cn_nameptr = ncp->nc_name;
cnp.cn_namelen = ncp->nc_nlen;
cnp.cn_cred = ap->a_cred;
cnp.cn_td = td;
*ap->a_vpp = NULL;
error = vop_old_lookup(ap->a_head.a_ops, dvp, ap->a_vpp, &cnp);
if (error == EJUSTRETURN) {
KKASSERT((cnp.cn_flags & CNP_PDIRUNLOCK) == 0);
error = VOP_OLD_MKNOD(dvp, ap->a_vpp, &cnp, ap->a_vap);
if (error == 0) {
cache_setunresolved(nch);
cache_setvp(nch, *ap->a_vpp);
}
} else {
if (error == 0) {
vput(*ap->a_vpp);
*ap->a_vpp = NULL;
error = EEXIST;
}
KKASSERT(*ap->a_vpp == NULL);
}
if ((cnp.cn_flags & CNP_PDIRUNLOCK) == 0)
vn_unlock(dvp);
vrele(dvp);
return (error);
}
int
vop_compat_nlink(struct vop_nlink_args *ap)
{
struct thread *td = curthread;
struct componentname cnp;
struct nchandle *nch;
struct namecache *ncp;
struct vnode *dvp;
struct vnode *tvp;
int error;
nch = ap->a_nch;
ncp = nch->ncp;
dvp = ap->a_dvp;
if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n",
ncp, ncp->nc_name);
return(EAGAIN);
}
bzero(&cnp, sizeof(cnp));
cnp.cn_nameiop = NAMEI_CREATE;
cnp.cn_flags = CNP_LOCKPARENT | CNP_NOTVP;
cnp.cn_nameptr = ncp->nc_name;
cnp.cn_namelen = ncp->nc_nlen;
cnp.cn_cred = ap->a_cred;
cnp.cn_td = td;
cnp.cn_notvp = ap->a_vp;
tvp = NULL;
error = vop_old_lookup(ap->a_head.a_ops, dvp, &tvp, &cnp);
if (error == EJUSTRETURN) {
KKASSERT((cnp.cn_flags & CNP_PDIRUNLOCK) == 0);
error = VOP_OLD_LINK(dvp, ap->a_vp, &cnp);
if (error == 0) {
cache_setunresolved(nch);
cache_setvp(nch, ap->a_vp);
}
} else {
if (error == 0) {
vput(tvp);
error = EEXIST;
}
}
if ((cnp.cn_flags & CNP_PDIRUNLOCK) == 0)
vn_unlock(dvp);
vrele(dvp);
return (error);
}
int
vop_compat_nsymlink(struct vop_nsymlink_args *ap)
{
struct thread *td = curthread;
struct componentname cnp;
struct nchandle *nch;
struct namecache *ncp;
struct vnode *dvp;
struct vnode *vp;
int error;
*ap->a_vpp = NULL;
nch = ap->a_nch;
ncp = nch->ncp;
dvp = ap->a_dvp;
if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n",
ncp, ncp->nc_name);
return(EAGAIN);
}
bzero(&cnp, sizeof(cnp));
cnp.cn_nameiop = NAMEI_CREATE;
cnp.cn_flags = CNP_LOCKPARENT;
cnp.cn_nameptr = ncp->nc_name;
cnp.cn_namelen = ncp->nc_nlen;
cnp.cn_cred = ap->a_cred;
cnp.cn_td = td;
vp = NULL;
error = vop_old_lookup(ap->a_head.a_ops, dvp, &vp, &cnp);
if (error == EJUSTRETURN) {
KKASSERT((cnp.cn_flags & CNP_PDIRUNLOCK) == 0);
error = VOP_OLD_SYMLINK(dvp, &vp, &cnp, ap->a_vap, ap->a_target);
if (error == 0) {
cache_setunresolved(nch);
cache_setvp(nch, vp);
*ap->a_vpp = vp;
}
} else {
if (error == 0) {
vput(vp);
vp = NULL;
error = EEXIST;
}
KKASSERT(vp == NULL);
}
if ((cnp.cn_flags & CNP_PDIRUNLOCK) == 0)
vn_unlock(dvp);
vrele(dvp);
return (error);
}
int
vop_compat_nwhiteout(struct vop_nwhiteout_args *ap)
{
struct thread *td = curthread;
struct componentname cnp;
struct nchandle *nch;
struct namecache *ncp;
struct vnode *dvp;
struct vnode *vp;
int error;
nch = ap->a_nch;
ncp = nch->ncp;
dvp = ap->a_dvp;
if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n",
ncp, ncp->nc_name);
return(EAGAIN);
}
bzero(&cnp, sizeof(cnp));
cnp.cn_nameiop = ap->a_flags;
cnp.cn_flags = CNP_LOCKPARENT;
cnp.cn_nameptr = ncp->nc_name;
cnp.cn_namelen = ncp->nc_nlen;
cnp.cn_cred = ap->a_cred;
cnp.cn_td = td;
vp = NULL;
switch(ap->a_flags) {
case NAMEI_DELETE:
cnp.cn_flags |= CNP_DOWHITEOUT;
case NAMEI_CREATE:
error = vop_old_lookup(ap->a_head.a_ops, dvp, &vp, &cnp);
if (error == EJUSTRETURN) {
KKASSERT((cnp.cn_flags & CNP_PDIRUNLOCK) == 0);
error = VOP_OLD_WHITEOUT(dvp, &cnp, ap->a_flags);
if (error == 0)
cache_setunresolved(nch);
} else {
if (error == 0) {
vput(vp);
vp = NULL;
error = EEXIST;
}
KKASSERT(vp == NULL);
}
break;
case NAMEI_LOOKUP:
error = VOP_OLD_WHITEOUT(dvp, NULL, ap->a_flags);
break;
default:
error = EINVAL;
break;
}
if ((cnp.cn_flags & CNP_PDIRUNLOCK) == 0)
vn_unlock(dvp);
vrele(dvp);
return (error);
}
int
vop_compat_nremove(struct vop_nremove_args *ap)
{
struct thread *td = curthread;
struct componentname cnp;
struct nchandle *nch;
struct namecache *ncp;
struct vnode *dvp;
struct vnode *vp;
int error;
nch = ap->a_nch;
ncp = nch->ncp;
dvp = ap->a_dvp;
if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n",
ncp, ncp->nc_name);
return(EAGAIN);
}
bzero(&cnp, sizeof(cnp));
cnp.cn_nameiop = NAMEI_DELETE;
cnp.cn_flags = CNP_LOCKPARENT;
cnp.cn_nameptr = ncp->nc_name;
cnp.cn_namelen = ncp->nc_nlen;
cnp.cn_cred = ap->a_cred;
cnp.cn_td = td;
vp = NULL;
error = vop_old_lookup(ap->a_head.a_ops, dvp, &vp, &cnp);
if (error == 0 && vp->v_type == VDIR)
error = EPERM;
if (error == 0) {
KKASSERT((cnp.cn_flags & CNP_PDIRUNLOCK) == 0);
error = VOP_OLD_REMOVE(dvp, vp, &cnp);
if (error == 0)
cache_unlink(nch);
}
if (vp) {
if (dvp == vp)
vrele(vp);
else
vput(vp);
}
if ((cnp.cn_flags & CNP_PDIRUNLOCK) == 0)
vn_unlock(dvp);
vrele(dvp);
return (error);
}
int
vop_compat_nrmdir(struct vop_nrmdir_args *ap)
{
struct thread *td = curthread;
struct componentname cnp;
struct nchandle *nch;
struct namecache *ncp;
struct vnode *dvp;
struct vnode *vp;
int error;
nch = ap->a_nch;
ncp = nch->ncp;
dvp = ap->a_dvp;
if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) {
kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n",
ncp, ncp->nc_name);
return(EAGAIN);
}
bzero(&cnp, sizeof(cnp));
cnp.cn_nameiop = NAMEI_DELETE;
cnp.cn_flags = CNP_LOCKPARENT;
cnp.cn_nameptr = ncp->nc_name;
cnp.cn_namelen = ncp->nc_nlen;
cnp.cn_cred = ap->a_cred;
cnp.cn_td = td;
vp = NULL;
error = vop_old_lookup(ap->a_head.a_ops, dvp, &vp, &cnp);
if (error == 0 && vp->v_type != VDIR)
error = ENOTDIR;
if (error == 0 && vp == dvp)
error = EINVAL;
if (error == 0 && (vp->v_flag & VROOT))
error = EBUSY;
if (error == 0) {
KKASSERT((cnp.cn_flags & CNP_PDIRUNLOCK) == 0);
error = VOP_OLD_RMDIR(dvp, vp, &cnp);
if (error == 0) {
cache_inval(nch, CINV_DESTROY);
cache_inval_vp(vp, CINV_DESTROY);
}
}
if (vp) {
if (dvp == vp)
vrele(vp);
else
vput(vp);
}
if ((cnp.cn_flags & CNP_PDIRUNLOCK) == 0)
vn_unlock(dvp);
vrele(dvp);
return (error);
}
int
vop_compat_nrename(struct vop_nrename_args *ap)
{
struct thread *td = curthread;
struct componentname fcnp;
struct componentname tcnp;
struct nchandle *fnch;
struct nchandle *tnch;
struct namecache *fncp;
struct namecache *tncp;
struct vnode *fdvp, *fvp;
struct vnode *tdvp, *tvp;
int error;
fnch = ap->a_fnch;
fncp = fnch->ncp;
fdvp = ap->a_fdvp;
if ((error = vget(fdvp, LK_EXCLUSIVE)) != 0) {
kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n",
fncp, fncp->nc_name);
return(EAGAIN);
}
bzero(&fcnp, sizeof(fcnp));
fcnp.cn_nameiop = NAMEI_DELETE;
fcnp.cn_flags = CNP_LOCKPARENT;
fcnp.cn_nameptr = fncp->nc_name;
fcnp.cn_namelen = fncp->nc_nlen;
fcnp.cn_cred = ap->a_cred;
fcnp.cn_td = td;
fvp = NULL;
error = vop_old_lookup(ap->a_head.a_ops, fdvp, &fvp, &fcnp);
if (error == 0 && (fvp->v_flag & VROOT)) {
vput(fvp);
error = EBUSY;
}
if ((fcnp.cn_flags & CNP_PDIRUNLOCK) == 0) {
fcnp.cn_flags |= CNP_PDIRUNLOCK;
vn_unlock(fdvp);
}
if (error) {
vrele(fdvp);
return (error);
}
vn_unlock(fvp);
tnch = ap->a_tnch;
tncp = tnch->ncp;
tdvp = ap->a_tdvp;
if (error) {
vrele(fdvp);
vrele(fvp);
return (error);
}
if ((error = vget(tdvp, LK_EXCLUSIVE)) != 0) {
kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n",
tncp, tncp->nc_name);
vrele(fdvp);
vrele(fvp);
return(EAGAIN);
}
bzero(&tcnp, sizeof(tcnp));
tcnp.cn_nameiop = NAMEI_RENAME;
tcnp.cn_flags = CNP_LOCKPARENT;
tcnp.cn_nameptr = tncp->nc_name;
tcnp.cn_namelen = tncp->nc_nlen;
tcnp.cn_cred = ap->a_cred;
tcnp.cn_td = td;
tvp = NULL;
error = vop_old_lookup(ap->a_head.a_ops, tdvp, &tvp, &tcnp);
if (error == EJUSTRETURN) {
KKASSERT(tvp == NULL);
KKASSERT((tcnp.cn_flags & CNP_PDIRUNLOCK) == 0);
error = VOP_OLD_RENAME(fdvp, fvp, &fcnp, tdvp, tvp, &tcnp);
if (error == 0)
cache_rename(fnch, tnch);
} else if (error == 0) {
KKASSERT((tcnp.cn_flags & CNP_PDIRUNLOCK) == 0);
error = VOP_OLD_RENAME(fdvp, fvp, &fcnp, tdvp, tvp, &tcnp);
if (error == 0)
cache_rename(fnch, tnch);
} else {
vrele(fdvp);
vrele(fvp);
if (tcnp.cn_flags & CNP_PDIRUNLOCK)
vrele(tdvp);
else
vput(tdvp);
}
return (error);
}
static int
vop_nolookup(struct vop_old_lookup_args *ap)
{
*ap->a_vpp = NULL;
return (ENOTDIR);
}
static int
vop_nostrategy (struct vop_strategy_args *ap)
{
kprintf("No strategy for buffer at %p\n", ap->a_bio->bio_buf);
vprint("", ap->a_vp);
ap->a_bio->bio_buf->b_flags |= B_ERROR;
ap->a_bio->bio_buf->b_error = EOPNOTSUPP;
biodone(ap->a_bio);
return (EOPNOTSUPP);
}
int
vop_stdpathconf(struct vop_pathconf_args *ap)
{
int error = 0;
switch (ap->a_name) {
case _PC_CHOWN_RESTRICTED:
*ap->a_retval = _POSIX_CHOWN_RESTRICTED;
break;
case _PC_LINK_MAX:
*ap->a_retval = LINK_MAX;
break;
case _PC_MAX_CANON:
*ap->a_retval = MAX_CANON;
break;
case _PC_MAX_INPUT:
*ap->a_retval = MAX_INPUT;
break;
case _PC_NAME_MAX:
*ap->a_retval = NAME_MAX;
break;
case _PC_NO_TRUNC:
*ap->a_retval = _POSIX_NO_TRUNC;
break;
case _PC_PATH_MAX:
*ap->a_retval = PATH_MAX;
break;
case _PC_PIPE_BUF:
*ap->a_retval = PIPE_BUF;
break;
case _PC_VDISABLE:
*ap->a_retval = _POSIX_VDISABLE;
break;
default:
error = EINVAL;
break;
}
return (error);
}
int
vop_stdopen(struct vop_open_args *ap)
{
struct vnode *vp = ap->a_vp;
struct file *fp;
if (ap->a_fpp) {
fp = *ap->a_fpp;
switch(vp->v_type) {
case VFIFO:
fp->f_type = DTYPE_FIFO;
break;
default:
fp->f_type = DTYPE_VNODE;
break;
}
fp->f_flag = (fp->f_flag & ~FMASK) | (ap->a_mode & FMASK);
fp->f_ops = &vnode_fileops;
fp->f_data = vp;
vref(vp);
}
if (ap->a_mode & FWRITE)
atomic_add_int(&vp->v_writecount, 1);
KKASSERT(vp->v_opencount >= 0 && vp->v_opencount != INT_MAX);
atomic_add_int(&vp->v_opencount, 1);
return (0);
}
int
vop_stdclose(struct vop_close_args *ap)
{
struct vnode *vp = ap->a_vp;
KASSERT(vp->v_opencount > 0,
("VOP_STDCLOSE: BAD OPENCOUNT %p %d type=%d ops=%p flgs=%08x",
vp, vp->v_opencount, vp->v_type, *vp->v_ops, vp->v_flag));
if (ap->a_fflag & FWRITE) {
KASSERT(vp->v_writecount > 0,
("VOP_STDCLOSE: BAD WRITECOUNT %p %d",
vp, vp->v_writecount));
atomic_add_int(&vp->v_writecount, -1);
}
atomic_add_int(&vp->v_opencount, -1);
return (0);
}
int
vop_stdgetattr_lite(struct vop_getattr_lite_args *ap)
{
struct vattr va;
struct vattr_lite *lvap;
int error;
error = VOP_GETATTR(ap->a_vp, &va);
if (__predict_true(error == 0)) {
lvap = ap->a_lvap;
lvap->va_type = va.va_type;
lvap->va_nlink = va.va_nlink;
lvap->va_mode = va.va_mode;
lvap->va_uid = va.va_uid;
lvap->va_gid = va.va_gid;
lvap->va_size = va.va_size;
lvap->va_flags = va.va_flags;
}
return error;
}
int
vop_stdgetpages(struct vop_getpages_args *ap)
{
struct mount *mp;
int error;
if ((mp = ap->a_vp->v_mount) != NULL) {
error = vnode_pager_generic_getpages(
ap->a_vp, ap->a_m, ap->a_count,
ap->a_reqpage, ap->a_seqaccess);
} else {
error = VM_PAGER_BAD;
}
return (error);
}
int
vop_stdputpages(struct vop_putpages_args *ap)
{
struct mount *mp;
int error;
if ((mp = ap->a_vp->v_mount) != NULL) {
error = vnode_pager_generic_putpages(
ap->a_vp, ap->a_m, ap->a_count,
ap->a_flags, ap->a_rtvals);
} else {
error = VM_PAGER_BAD;
}
return (error);
}
int
vop_stdnoread(struct vop_read_args *ap)
{
return (EINVAL);
}
int
vop_stdnowrite(struct vop_write_args *ap)
{
return (EINVAL);
}
int
vop_stdmountctl(struct vop_mountctl_args *ap)
{
struct mount *mp;
int error = 0;
mp = ap->a_head.a_ops->head.vv_mount;
switch(ap->a_op) {
case MOUNTCTL_MOUNTFLAGS:
*ap->a_res = vfs_flagstostr(mp->mnt_flag & MNT_VISFLAGMASK, NULL,
ap->a_buf, ap->a_buflen, &error);
break;
case MOUNTCTL_INSTALL_VFS_JOURNAL:
case MOUNTCTL_RESTART_VFS_JOURNAL:
case MOUNTCTL_REMOVE_VFS_JOURNAL:
case MOUNTCTL_RESYNC_VFS_JOURNAL:
case MOUNTCTL_STATUS_VFS_JOURNAL:
error = journal_mountctl(ap);
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
int
vop_stdallocate(struct vop_allocate_args *ap)
{
struct thread *td;
struct vnode *vp;
struct vattr vattr, *vap;
struct uio auio;
struct iovec aiov;
uint8_t *buf;
off_t offset, len, fsize;
size_t iosize;
int error;
bzero(&auio, sizeof(auio));
td = curthread;
vap = &vattr;
buf = NULL;
vp = ap->a_vp;
offset = ap->a_offset;
len = ap->a_len;
error = VOP_GETATTR(vp, vap);
if (error != 0)
goto out;
fsize = vap->va_size;
iosize = vap->va_blocksize;
if (iosize == 0)
iosize = BLKDEV_IOSIZE;
if (iosize > vmaxiosize(vp))
iosize = vmaxiosize(vp);
buf = kmalloc(iosize, M_TEMP, M_WAITOK);
if (offset + len > vap->va_size) {
VATTR_NULL(&vattr);
vap->va_size = offset + len;
error = VOP_SETATTR(vp, vap, td->td_ucred);
if (error != 0)
goto out;
VATTR_NULL(&vattr);
vap->va_size = fsize;
error = VOP_SETATTR(vp, vap, td->td_ucred);
if (error != 0)
goto out;
}
for (;;) {
off_t cur = iosize;
if ((offset % iosize) != 0)
cur -= (offset % iosize);
if (cur > len)
cur = len;
if (offset < fsize) {
aiov.iov_base = buf;
aiov.iov_len = cur;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = offset;
auio.uio_resid = cur;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_rw = UIO_READ;
auio.uio_td = td;
error = VOP_READ(vp, &auio, 0, td->td_ucred);
if (error != 0)
break;
if (auio.uio_resid > 0) {
bzero(buf + cur - auio.uio_resid,
auio.uio_resid);
}
} else {
bzero(buf, cur);
}
aiov.iov_base = buf;
aiov.iov_len = cur;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = offset;
auio.uio_resid = cur;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_rw = UIO_WRITE;
auio.uio_td = td;
error = VOP_WRITE(vp, &auio, 0, td->td_ucred);
if (error != 0)
break;
len -= cur;
offset += cur;
if (len == 0)
break;
}
out:
ap->a_offset = offset;
ap->a_len = len;
kfree(buf, M_TEMP);
return (error);
}
int
vop_stdfdatasync(struct vop_fdatasync_args *ap)
{
return (VOP_FSYNC_FP(ap->a_vp, ap->a_waitfor, ap->a_flags, ap->a_fp));
}
int
vop_stdioctl(struct vop_ioctl_args *ap)
{
struct vnode *vp;
struct vattr va;
off_t *offp;
int error;
switch (ap->a_command) {
case FIOSEEKDATA:
case FIOSEEKHOLE:
vp = ap->a_vp;
error = vn_lock(vp, LK_SHARED);
if (error != 0)
return (EBADF);
if (vp->v_type == VREG)
error = VOP_GETATTR(vp, &va);
else
error = ENOTTY;
if (error == 0) {
offp = (void *)ap->a_data;
if (*offp < 0 || *offp >= va.va_size)
error = ENXIO;
else if (ap->a_command == FIOSEEKHOLE)
*offp = va.va_size;
}
vn_unlock(vp);
break;
default:
error = ENOTTY;
break;
}
return (error);
}
int
vfs_stdroot(struct mount *mp, struct vnode **vpp)
{
return (EOPNOTSUPP);
}
int
vfs_stdstatfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
{
return (EOPNOTSUPP);
}
int
vfs_stdstatvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred)
{
struct statfs *in;
int error;
in = &mp->mnt_stat;
error = VFS_STATFS(mp, in, cred);
if (error == 0) {
bzero(sbp, sizeof(*sbp));
sbp->f_bsize = in->f_bsize;
sbp->f_frsize = in->f_bsize;
sbp->f_blocks = in->f_blocks;
sbp->f_bfree = in->f_bfree;
sbp->f_bavail = in->f_bavail;
sbp->f_files = in->f_files;
sbp->f_ffree = in->f_ffree;
sbp->f_favail = in->f_ffree;
sbp->f_fsid = 0;
sbp->f_flag = 0;
if (in->f_flags & MNT_RDONLY)
sbp->f_flag |= ST_RDONLY;
if (in->f_flags & MNT_NOSUID)
sbp->f_flag |= ST_NOSUID;
sbp->f_namemax = 0;
sbp->f_owner = in->f_owner;
sbp->f_type = 0;
sbp->f_syncreads = in->f_syncreads;
sbp->f_syncwrites = in->f_syncwrites;
sbp->f_asyncreads = in->f_asyncreads;
sbp->f_asyncwrites = in->f_asyncwrites;
}
return (error);
}
int
vfs_stdvptofh(struct vnode *vp, struct fid *fhp)
{
return (EOPNOTSUPP);
}
int
vfs_stdstart(struct mount *mp, int flags)
{
return (0);
}
int
vfs_stdquotactl(struct mount *mp, int cmds, uid_t uid,
caddr_t arg, struct ucred *cred)
{
return (EOPNOTSUPP);
}
int
vfs_stdsync(struct mount *mp, int waitfor)
{
return (0);
}
int
vfs_stdnosync(struct mount *mp, int waitfor)
{
return (EOPNOTSUPP);
}
int
vfs_stdvget(struct mount *mp, struct vnode *dvp, ino_t ino, struct vnode **vpp)
{
return (EOPNOTSUPP);
}
int
vfs_stdfhtovp(struct mount *mp, struct vnode *rootvp,
struct fid *fhp, struct vnode **vpp)
{
return (EOPNOTSUPP);
}
int
vfs_stdcheckexp(struct mount *mp, struct sockaddr *nam, int *extflagsp,
struct ucred **credanonp)
{
return (EOPNOTSUPP);
}
int
vfs_stdinit(struct vfsconf *vfsp)
{
return (0);
}
int
vfs_stduninit(struct vfsconf *vfsp)
{
return(0);
}
int
vfs_stdextattrctl(struct mount *mp, int cmd, struct vnode *vp,
int attrnamespace, const char *attrname,
struct ucred *cred)
{
return(EOPNOTSUPP);
}
#define ACCOUNTING_NB_FSTYPES 7
static const char *accounting_fstypes[ACCOUNTING_NB_FSTYPES] = {
"ext2fs", "hammer", "mfs", "ntfs", "null", "tmpfs", "ufs" };
int
vfs_stdac_init(struct mount *mp)
{
const char* fs_type;
int i, fstype_ok = 0;
for (i=0; i<ACCOUNTING_NB_FSTYPES; i++) {
fs_type = accounting_fstypes[i];
if (strncmp(mp->mnt_stat.f_fstypename, fs_type,
sizeof(mp->mnt_stat)) == 0) {
fstype_ok = 1;
break;
}
}
if (fstype_ok == 0)
return (0);
vq_init(mp);
return (0);
}
void
vfs_stdac_done(struct mount *mp)
{
vq_done(mp);
}
void
vfs_stdncpgen_set(struct mount *mp, struct namecache *ncp)
{
}
int
vfs_stdncpgen_test(struct mount *mp, struct namecache *ncp)
{
return 0;
}
int
vfs_stdmodifying(struct mount *mp)
{
if (mp->mnt_flag & MNT_RDONLY)
return EROFS;
return 0;
}