#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/mount_internal.h>
#include <sys/namei.h>
#include <sys/sysctl.h>
#include <sys/vnode.h>
#include <sys/xattr.h>
#include <sys/ubc.h>
#include <sys/types.h>
#include <sys/dirent.h>
#include "nullfs.h"
#define NULL_ROOT_INO 2
#define NULL_SECOND_INO 3
#define NULL_THIRD_INO 4
vop_t * nullfs_vnodeop_p = NULL;
static int
nullfs_isspecialvp(struct vnode * vp)
{
struct null_mount * null_mp;
null_mp = MOUNTTONULLMOUNT(vnode_mount(vp));
if (vp && (vp == null_mp->nullm_rootvp || vp == null_mp->nullm_secondvp)) {
return 1;
}
return 0;
}
static int
nullfs_checkspecialvp(struct vnode* vp)
{
int result = 0;
struct null_mount * null_mp;
null_mp = MOUNTTONULLMOUNT(vnode_mount(vp));
lck_mtx_lock(&null_mp->nullm_lock);
result = (nullfs_isspecialvp(vp));
lck_mtx_unlock(&null_mp->nullm_lock);
return result;
}
static int
nullfs_default(__unused struct vnop_generic_args * args)
{
NULLFSDEBUG("%s (default)\n", ((struct vnodeop_desc_fake *)args->a_desc)->vdesc_name);
return ENOTSUP;
}
static int
nullfs_special_getattr(struct vnop_getattr_args * args)
{
mount_t mp = vnode_mount(args->a_vp);
struct null_mount * null_mp = MOUNTTONULLMOUNT(mp);
ino_t ino = NULL_ROOT_INO;
struct vnode_attr covered_rootattr;
vnode_t checkvp = null_mp->nullm_lowerrootvp;
VATTR_INIT(&covered_rootattr);
VATTR_WANTED(&covered_rootattr, va_uid);
VATTR_WANTED(&covered_rootattr, va_gid);
VATTR_WANTED(&covered_rootattr, va_create_time);
VATTR_WANTED(&covered_rootattr, va_modify_time);
VATTR_WANTED(&covered_rootattr, va_access_time);
if (vnode_getwithvid(checkvp, null_mp->nullm_lowerrootvid)) {
checkvp = vfs_vnodecovered(mp);
if (checkvp == NULL) {
return EIO;
}
}
int error = vnode_getattr(checkvp, &covered_rootattr, args->a_context);
vnode_put(checkvp);
if (error) {
return error;
}
if (args->a_vp == null_mp->nullm_secondvp) {
ino = NULL_SECOND_INO;
}
VATTR_RETURN(args->a_vap, va_type, vnode_vtype(args->a_vp));
VATTR_RETURN(args->a_vap, va_rdev, 0);
VATTR_RETURN(args->a_vap, va_nlink, 3);
VATTR_RETURN(args->a_vap, va_total_size, 0);
VATTR_RETURN(args->a_vap, va_data_size, 0); VATTR_RETURN(args->a_vap, va_data_alloc, 0);
VATTR_RETURN(args->a_vap, va_iosize, vfs_statfs(mp)->f_iosize);
VATTR_RETURN(args->a_vap, va_fileid, ino);
VATTR_RETURN(args->a_vap, va_linkid, ino);
VATTR_RETURN(args->a_vap, va_fsid, vfs_statfs(mp)->f_fsid.val[0]); VATTR_RETURN(args->a_vap, va_filerev, 0);
VATTR_RETURN(args->a_vap, va_gen, 0);
VATTR_RETURN(args->a_vap, va_flags, UF_HIDDEN);
if (ino == NULL_SECOND_INO) {
VATTR_RETURN(args->a_vap, va_parentid, NULL_ROOT_INO);
}
if (VATTR_IS_ACTIVE(args->a_vap, va_mode)) {
VATTR_RETURN(args->a_vap, va_mode, S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
}
if (VATTR_IS_ACTIVE(args->a_vap, va_uid)) {
VATTR_RETURN(args->a_vap, va_uid, covered_rootattr.va_uid);
}
if (VATTR_IS_ACTIVE(args->a_vap, va_gid)) {
VATTR_RETURN(args->a_vap, va_gid, covered_rootattr.va_gid);
}
if (VATTR_IS_ACTIVE(args->a_vap, va_create_time)) {
VATTR_SET_SUPPORTED(args->a_vap, va_create_time);
args->a_vap->va_create_time.tv_sec = covered_rootattr.va_create_time.tv_sec;
args->a_vap->va_create_time.tv_nsec = covered_rootattr.va_create_time.tv_nsec;
}
if (VATTR_IS_ACTIVE(args->a_vap, va_modify_time)) {
VATTR_SET_SUPPORTED(args->a_vap, va_modify_time);
args->a_vap->va_modify_time.tv_sec = covered_rootattr.va_modify_time.tv_sec;
args->a_vap->va_modify_time.tv_nsec = covered_rootattr.va_modify_time.tv_nsec;
}
if (VATTR_IS_ACTIVE(args->a_vap, va_access_time)) {
VATTR_SET_SUPPORTED(args->a_vap, va_access_time);
args->a_vap->va_modify_time.tv_sec = covered_rootattr.va_access_time.tv_sec;
args->a_vap->va_modify_time.tv_nsec = covered_rootattr.va_access_time.tv_nsec;
}
return 0;
}
static int
nullfs_getattr(struct vnop_getattr_args * args)
{
int error;
struct null_mount * null_mp = MOUNTTONULLMOUNT(vnode_mount(args->a_vp));
NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
lck_mtx_lock(&null_mp->nullm_lock);
if (nullfs_isspecialvp(args->a_vp)) {
error = nullfs_special_getattr(args);
lck_mtx_unlock(&null_mp->nullm_lock);
return error;
}
lck_mtx_unlock(&null_mp->nullm_lock);
struct vnode * lowervp = NULLVPTOLOWERVP(args->a_vp);
error = vnode_getwithref(lowervp);
if (error == 0) {
error = VNOP_GETATTR(lowervp, args->a_vap, args->a_context);
vnode_put(lowervp);
if (error == 0) {
VATTR_RETURN(args->a_vap, va_fsid, vfs_statfs(vnode_mount(args->a_vp))->f_fsid.val[0]);
}
}
return error;
}
static int
nullfs_open(struct vnop_open_args * args)
{
int error;
struct vnode *vp, *lvp;
NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
if (nullfs_checkspecialvp(args->a_vp)) {
return 0;
}
vp = args->a_vp;
lvp = NULLVPTOLOWERVP(vp);
error = vnode_getwithref(lvp);
if (error == 0) {
error = VNOP_OPEN(lvp, args->a_mode, args->a_context);
vnode_put(lvp);
}
return error;
}
static int
nullfs_close(struct vnop_close_args * args)
{
int error;
struct vnode *vp, *lvp;
NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
if (nullfs_checkspecialvp(args->a_vp)) {
return 0;
}
vp = args->a_vp;
lvp = NULLVPTOLOWERVP(vp);
error = vnode_getwithref(lvp);
if (error == 0) {
error = VNOP_CLOSE(lvp, args->a_fflag, args->a_context);
vnode_put(lvp);
}
return error;
}
static int
null_get_lowerparent(vnode_t lvp, vnode_t * dvpp, vfs_context_t ctx)
{
int error = 0;
struct vnode_attr va;
mount_t mp = vnode_mount(lvp);
vnode_t dvp = vnode_parent(lvp);
if (dvp) {
error = vnode_get(dvp);
goto end;
}
error = ENOENT;
if (!(mp->mnt_kern_flag & MNTK_PATH_FROM_ID)) {
goto end;
}
VATTR_INIT(&va);
VATTR_WANTED(&va, va_parentid);
error = vnode_getattr(lvp, &va, ctx);
if (error || !VATTR_IS_SUPPORTED(&va, va_parentid)) {
goto end;
}
error = VFS_VGET(mp, (ino64_t)va.va_parentid, &dvp, ctx);
end:
if (error == 0) {
*dvpp = dvp;
}
return error;
}
static int
null_special_lookup(struct vnop_lookup_args * ap)
{
struct componentname * cnp = ap->a_cnp;
struct vnode * dvp = ap->a_dvp;
struct vnode * ldvp = NULL;
struct vnode * lvp = NULL;
struct vnode * vp = NULL;
struct mount * mp = vnode_mount(dvp);
struct null_mount * null_mp = MOUNTTONULLMOUNT(mp);
int error = ENOENT;
if (dvp == null_mp->nullm_rootvp) {
if (cnp->cn_nameptr[0] == '.') {
if (cnp->cn_namelen == 1 || (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.')) {
vp = dvp;
error = vnode_get(vp);
goto end;
}
}
if (cnp->cn_namelen == 1 &&
(cnp->cn_nameptr[0] == 'd' || (null_mp->nullm_flags & NULLM_CASEINSENSITIVE ? cnp->cn_nameptr[0] == 'D' : 0))) {
error = 0;
if (null_mp->nullm_secondvp == NULL) {
error = null_getnewvnode(mp, NULL, dvp, &vp, cnp, 0);
if (error) {
goto end;
}
null_mp->nullm_secondvp = vp;
} else {
vp = null_mp->nullm_secondvp;
error = vnode_get(vp);
}
}
} else if (dvp == null_mp->nullm_secondvp) {
if (cnp->cn_nameptr[0] == '.') {
if (cnp->cn_namelen == 1) {
vp = dvp;
error = vnode_get(vp);
goto end;
} else if (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.') {
vp = null_mp->nullm_rootvp;
error = vnode_get(vp);
goto end;
}
}
error = vnode_getwithvid(null_mp->nullm_lowerrootvp, null_mp->nullm_lowerrootvid);
if (error) {
goto end;
}
error = null_get_lowerparent(null_mp->nullm_lowerrootvp, &ldvp, ap->a_context);
if (error == 0) {
error = VNOP_LOOKUP(ldvp, &lvp, cnp, ap->a_context);
vnode_put(ldvp);
if (error == 0) {
if (lvp == null_mp->nullm_lowerrootvp) {
error = null_nodeget(mp, lvp, dvp, &vp, cnp, 0);
if (error == 0 && null_mp->nullm_thirdcovervp == NULL) {
null_mp->nullm_thirdcovervp = vp;
}
} else {
error = ENOENT;
}
vnode_put(lvp);
}
}
vnode_put(null_mp->nullm_lowerrootvp);
}
end:
if (error == 0) {
*ap->a_vpp = vp;
}
return error;
}
static int
null_lookup(struct vnop_lookup_args * ap)
{
struct componentname * cnp = ap->a_cnp;
struct vnode * dvp = ap->a_dvp;
struct vnode *vp, *ldvp, *lvp;
struct mount * mp;
struct null_mount * null_mp;
int error;
NULLFSDEBUG("%s parent: %p component: %.*s\n", __FUNCTION__, ap->a_dvp, cnp->cn_namelen, cnp->cn_nameptr);
mp = vnode_mount(dvp);
if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME || cnp->cn_nameiop == CREATE) {
return (EROFS);
}
null_mp = MOUNTTONULLMOUNT(mp);
lck_mtx_lock(&null_mp->nullm_lock);
if (nullfs_isspecialvp(dvp)) {
error = null_special_lookup(ap);
lck_mtx_unlock(&null_mp->nullm_lock);
return error;
}
lck_mtx_unlock(&null_mp->nullm_lock);
if (cnp->cn_nameptr[0] == '.') {
if (cnp->cn_namelen == 1) {
vp = dvp;
} else if (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.') {
vp = vnode_parent(dvp);
} else {
goto notdot;
}
error = vp ? vnode_get(vp) : ENOENT;
if (error == 0) {
*ap->a_vpp = vp;
}
return error;
}
notdot:
ldvp = NULLVPTOLOWERVP(dvp);
vp = lvp = NULL;
error = vnode_getwithref(ldvp);
if (error) {
return error;
}
error = VNOP_LOOKUP(ldvp, &lvp, cnp, ap->a_context);
vnode_put(ldvp);
if ((error == 0 || error == EJUSTRETURN) && lvp != NULL) {
if (ldvp == lvp) {
vp = dvp;
error = vnode_get(vp);
} else {
error = null_nodeget(mp, lvp, dvp, &vp, cnp, 0);
}
if (error == 0) {
*ap->a_vpp = vp;
}
}
if (lvp != NULL) {
vnode_put(lvp);
}
return (error);
}
static int
null_inactive(__unused struct vnop_inactive_args * ap)
{
NULLFSDEBUG("%s %p\n", __FUNCTION__, ap->a_vp);
return (0);
}
static int
null_reclaim(struct vnop_reclaim_args * ap)
{
struct vnode * vp;
struct null_node * xp;
struct vnode * lowervp;
struct null_mount * null_mp = MOUNTTONULLMOUNT(vnode_mount(ap->a_vp));
NULLFSDEBUG("%s %p\n", __FUNCTION__, ap->a_vp);
vp = ap->a_vp;
xp = VTONULL(vp);
lowervp = xp->null_lowervp;
lck_mtx_lock(&null_mp->nullm_lock);
vnode_removefsref(vp);
if (lowervp != NULL) {
if (xp->null_flags & NULL_FLAG_HASHED) {
null_hashrem(xp);
}
vnode_getwithref(lowervp);
vnode_rele(lowervp);
vnode_put(lowervp);
}
if (vp == null_mp->nullm_rootvp) {
null_mp->nullm_rootvp = NULL;
} else if (vp == null_mp->nullm_secondvp) {
null_mp->nullm_secondvp = NULL;
} else if (vp == null_mp->nullm_thirdcovervp) {
null_mp->nullm_thirdcovervp = NULL;
}
lck_mtx_unlock(&null_mp->nullm_lock);
cache_purge(vp);
vnode_clearfsnode(vp);
FREE(xp, M_TEMP);
return 0;
}
#define DIRENT_SZ(dp) ((sizeof(struct dirent) - NAME_MAX) + (((dp)->d_namlen + 1 + 3) & ~3))
static int
store_entry_special(ino_t ino, const char * name, struct uio * uio)
{
struct dirent e;
size_t namelen = strlen(name);
int error = EINVAL;
if (namelen + 1 <= NAME_MAX) {
memset(&e, 0, sizeof(e));
e.d_ino = ino;
e.d_type = DT_DIR;
e.d_namlen = namelen;
e.d_reclen = DIRENT_SZ(&e);
if (uio_resid(uio) >= e.d_reclen) {
strlcpy(e.d_name, name, NAME_MAX);
error = uiomove((caddr_t)&e, e.d_reclen, uio);
} else {
error = EMSGSIZE;
}
}
return error;
}
static int
nullfs_special_readdir(struct vnop_readdir_args * ap)
{
struct vnode * vp = ap->a_vp;
struct uio * uio = ap->a_uio;
struct null_mount * null_mp = MOUNTTONULLMOUNT(vnode_mount(vp));
off_t offset = uio_offset(uio);
int error = ERANGE;
int items = 0;
ino_t ino = 0;
const char * name = NULL;
if (ap->a_flags & (VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF))
return (EINVAL);
if (offset == 0) {
if (vp == null_mp->nullm_rootvp) {
ino = NULL_ROOT_INO;
} else
{
ino = NULL_SECOND_INO;
}
error = store_entry_special(ino, ".", uio);
if (error) {
goto out;
}
offset++;
items++;
}
if (offset == 1) {
ino = NULL_ROOT_INO;
error = store_entry_special(ino, "..", uio);
if (error) {
goto out;
}
offset++;
items++;
}
if (offset == 2) {
if (vp == null_mp->nullm_rootvp) {
ino = NULL_SECOND_INO;
name = "d";
} else
{
ino = NULL_THIRD_INO;
if (vnode_getwithvid(null_mp->nullm_lowerrootvp, null_mp->nullm_lowerrootvid)) {
error = 0;
goto out;
}
name = vnode_getname_printable(null_mp->nullm_lowerrootvp);
}
error = store_entry_special(ino, name, uio);
if (ino == NULL_THIRD_INO) {
vnode_putname_printable(name);
vnode_put(null_mp->nullm_lowerrootvp);
}
if (error) {
goto out;
}
offset++;
items++;
}
out:
if (error == EMSGSIZE) {
error = 0;
}
uio_setoffset(uio, offset);
if (ap->a_numdirent) {
*ap->a_numdirent = items;
}
return error;
}
static int
nullfs_readdir(struct vnop_readdir_args * ap)
{
struct vnode *vp, *lvp;
int error;
struct null_mount * null_mp = MOUNTTONULLMOUNT(vnode_mount(ap->a_vp));
NULLFSDEBUG("%s %p\n", __FUNCTION__, ap->a_vp);
lck_mtx_lock(&null_mp->nullm_lock);
if (nullfs_isspecialvp(ap->a_vp)) {
error = nullfs_special_readdir(ap);
lck_mtx_unlock(&null_mp->nullm_lock);
return error;
}
lck_mtx_unlock(&null_mp->nullm_lock);
vp = ap->a_vp;
lvp = NULLVPTOLOWERVP(vp);
error = vnode_getwithref(lvp);
if (error == 0) {
error = VNOP_READDIR(lvp, ap->a_uio, ap->a_flags, ap->a_eofflag, ap->a_numdirent, ap->a_context);
vnode_put(lvp);
}
return error;
}
static int
nullfs_readlink(struct vnop_readlink_args * ap)
{
NULLFSDEBUG("%s %p\n", __FUNCTION__, ap->a_vp);
int error;
struct vnode *vp, *lvp;
if (nullfs_checkspecialvp(ap->a_vp)) {
return ENOTSUP;
}
vp = ap->a_vp;
lvp = NULLVPTOLOWERVP(vp);
error = vnode_getwithref(lvp);
if (error == 0) {
error = VNOP_READLINK(lvp, ap->a_uio, ap->a_context);
vnode_put(lvp);
if (error) {
NULLFSDEBUG("readlink failed: %d\n", error);
}
}
return error;
}
static int
nullfs_pathconf(__unused struct vnop_pathconf_args * args)
{
NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
return EINVAL;
}
static int
nullfs_fsync(__unused struct vnop_fsync_args * args)
{
NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
return 0;
}
static int
nullfs_mmap(struct vnop_mmap_args * args)
{
int error;
struct vnode *vp, *lvp;
NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
if (nullfs_checkspecialvp(args->a_vp)) {
return 0;
}
vp = args->a_vp;
lvp = NULLVPTOLOWERVP(vp);
error = vnode_getwithref(lvp);
if (error == 0) {
error = VNOP_MMAP(lvp, args->a_fflags, args->a_context);
vnode_put(lvp);
}
return error;
}
static int
nullfs_mnomap(struct vnop_mnomap_args * args)
{
int error;
struct vnode *vp, *lvp;
NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
if (nullfs_checkspecialvp(args->a_vp)) {
return 0;
}
vp = args->a_vp;
lvp = NULLVPTOLOWERVP(vp);
error = vnode_getwithref(lvp);
if (error == 0) {
error = VNOP_MNOMAP(lvp, args->a_context);
vnode_put(lvp);
}
return error;
}
static int
nullfs_getxattr(struct vnop_getxattr_args * args)
{
int error;
struct vnode *vp, *lvp;
NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
if (nullfs_checkspecialvp(args->a_vp)) {
return 0;
}
vp = args->a_vp;
lvp = NULLVPTOLOWERVP(vp);
error = vnode_getwithref(lvp);
if (error == 0) {
error = VNOP_GETXATTR(lvp, args->a_name, args->a_uio, args->a_size, args->a_options, args->a_context);
vnode_put(lvp);
}
return error;
}
static int
nullfs_listxattr(struct vnop_listxattr_args * args)
{
int error;
struct vnode *vp, *lvp;
NULLFSDEBUG("%s %p\n", __FUNCTION__, args->a_vp);
if (nullfs_checkspecialvp(args->a_vp)) {
return 0;
}
vp = args->a_vp;
lvp = NULLVPTOLOWERVP(vp);
error = vnode_getwithref(lvp);
if (error == 0) {
error = VNOP_LISTXATTR(lvp, args->a_uio, args->a_size, args->a_options, args->a_context);
vnode_put(lvp);
}
return error;
}
static int
nullfs_pagein(struct vnop_pagein_args * ap)
{
int error = EIO;
struct vnode *vp, *lvp;
NULLFSDEBUG("%s %p\n", __FUNCTION__, ap->a_vp);
vp = ap->a_vp;
lvp = NULLVPTOLOWERVP(vp);
if (vnode_vtype(vp) != VREG) {
return ENOTSUP;
}
if (vnode_getwithvid(lvp, NULLVPTOLOWERVID(vp)) == 0) {
vm_offset_t ioaddr;
uio_t auio;
kern_return_t kret;
off_t bytes_to_commit;
off_t lowersize;
upl_t upl = ap->a_pl;
user_ssize_t bytes_remaining = 0;
auio = uio_create(1, ap->a_f_offset, UIO_SYSSPACE, UIO_READ);
if (auio == NULL) {
error = EIO;
goto exit_no_unmap;
}
kret = ubc_upl_map(upl, &ioaddr);
if (KERN_SUCCESS != kret) {
panic("nullfs_pagein: ubc_upl_map() failed with (%d)", kret);
}
ioaddr += ap->a_pl_offset;
error = uio_addiov(auio, (user_addr_t)ioaddr, ap->a_size);
if (error) {
goto exit;
}
lowersize = ubc_getsize(lvp);
if (lowersize != ubc_getsize(vp)) {
(void)ubc_setsize(vp, lowersize);
}
error = VNOP_READ(lvp, auio, ((ap->a_flags & UPL_IOSYNC) ? IO_SYNC : 0), ap->a_context);
bytes_remaining = uio_resid(auio);
if (bytes_remaining > 0 && bytes_remaining <= (user_ssize_t)ap->a_size)
{
bzero((void*)((uintptr_t)(ioaddr + ap->a_size - bytes_remaining)), (size_t) bytes_remaining);
}
exit:
kret = ubc_upl_unmap(upl);
if (KERN_SUCCESS != kret) {
panic("nullfs_pagein: ubc_upl_unmap() failed with (%d)", kret);
}
if (auio != NULL) {
uio_free(auio);
}
exit_no_unmap:
if ((ap->a_flags & UPL_NOCOMMIT) == 0) {
if (!error && (bytes_remaining >= 0) && (bytes_remaining <= (user_ssize_t)ap->a_size)) {
bytes_to_commit = ap->a_size - bytes_remaining;
if (bytes_to_commit)
{
if (bytes_to_commit & PAGE_MASK)
{
bytes_to_commit = (bytes_to_commit & (~PAGE_MASK)) + (PAGE_MASK + 1);
assert(bytes_to_commit <= (off_t)ap->a_size);
bytes_remaining = ap->a_size - bytes_to_commit;
}
ubc_upl_commit_range(upl, ap->a_pl_offset, (upl_size_t)bytes_to_commit, UPL_COMMIT_FREE_ON_EMPTY);
}
if (bytes_remaining) {
ubc_upl_abort_range(upl, ap->a_pl_offset + bytes_to_commit, (upl_size_t)bytes_remaining, UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
}
} else {
ubc_upl_abort_range(upl, ap->a_pl_offset, (upl_size_t)ap->a_size, UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
}
}
vnode_put(lvp);
} else if((ap->a_flags & UPL_NOCOMMIT) == 0) {
ubc_upl_abort_range(ap->a_pl, ap->a_pl_offset, (upl_size_t)ap->a_size, UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY);
}
return error;
}
static int
nullfs_read(struct vnop_read_args * ap)
{
int error = EIO;
struct vnode *vp, *lvp;
NULLFSDEBUG("%s %p\n", __FUNCTION__, ap->a_vp);
if (nullfs_checkspecialvp(ap->a_vp)) {
return ENOTSUP;
}
vp = ap->a_vp;
lvp = NULLVPTOLOWERVP(vp);
if (vnode_getwithvid(lvp, NULLVPTOLOWERVID(vp)) == 0) {
if (!vnode_isreg(lvp) && !vnode_islnk(lvp)) {
error = EPERM;
goto end;
}
if (uio_resid(ap->a_uio) == 0) {
error = 0;
goto end;
}
error = VNOP_READ(lvp, ap->a_uio, ap->a_ioflag, ap->a_context);
if (error) {
NULLFSDEBUG("VNOP_READ failed: %d\n", error);
}
end:
vnode_put(lvp);
}
return error;
}
static struct vnodeopv_entry_desc nullfs_vnodeop_entries[] = {
{&vnop_default_desc, (vop_t)nullfs_default}, {&vnop_getattr_desc, (vop_t)nullfs_getattr},
{&vnop_open_desc, (vop_t)nullfs_open}, {&vnop_close_desc, (vop_t)nullfs_close},
{&vnop_inactive_desc, (vop_t)null_inactive}, {&vnop_reclaim_desc, (vop_t)null_reclaim},
{&vnop_lookup_desc, (vop_t)null_lookup}, {&vnop_readdir_desc, (vop_t)nullfs_readdir},
{&vnop_readlink_desc, (vop_t)nullfs_readlink}, {&vnop_pathconf_desc, (vop_t)nullfs_pathconf},
{&vnop_fsync_desc, (vop_t)nullfs_fsync}, {&vnop_mmap_desc, (vop_t)nullfs_mmap},
{&vnop_mnomap_desc, (vop_t)nullfs_mnomap}, {&vnop_getxattr_desc, (vop_t)nullfs_getxattr},
{&vnop_pagein_desc, (vop_t)nullfs_pagein}, {&vnop_read_desc, (vop_t)nullfs_read},
{&vnop_listxattr_desc, (vop_t)nullfs_listxattr}, {NULL, NULL},
};
struct vnodeopv_desc nullfs_vnodeop_opv_desc = {&nullfs_vnodeop_p, nullfs_vnodeop_entries};