#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/kernel.h>
#include <sys/fcntl.h>
#include <sys/conf.h>
#include <sys/disklabel.h>
#include <sys/lock.h>
#include <sys/stat.h>
#include <sys/mount_internal.h>
#include <sys/proc.h>
#include <sys/kauth.h>
#include <sys/time.h>
#include <sys/vnode_internal.h>
#include <miscfs/specfs/specdev.h>
#include <sys/dirent.h>
#include <sys/vmmeter.h>
#include <sys/vm.h>
#include <sys/uio_internal.h>
#if CONFIG_MACF
#include <security/mac_framework.h>
#endif
#include "devfsdefs.h"
static int devfs_update(struct vnode *vp, struct timeval *access,
struct timeval *modify);
static int
devfs_lookup(struct vnop_lookup_args *ap)
{
struct componentname *cnp = ap->a_cnp;
vfs_context_t ctx = cnp->cn_context;
struct proc *p = vfs_context_proc(ctx);
struct vnode *dir_vnode = ap->a_dvp;
struct vnode **result_vnode = ap->a_vpp;
devnode_t * dir_node;
devnode_t * node = NULL;
devdirent_t * nodename;
int flags = cnp->cn_flags;
int op = cnp->cn_nameiop;
int wantparent = flags & (LOCKPARENT|WANTPARENT);
int error = 0;
char heldchar;
retry:
*result_vnode = NULL;
dir_node = VTODN(dir_vnode);
if (dir_node->dn_type != DEV_DIR) {
return (ENOTDIR);
}
DEVFS_LOCK();
heldchar = cnp->cn_nameptr[cnp->cn_namelen];
cnp->cn_nameptr[cnp->cn_namelen] = '\0';
nodename = dev_findname(dir_node, cnp->cn_nameptr);
cnp->cn_nameptr[cnp->cn_namelen] = heldchar;
if (nodename) {
node = nodename->de_dnp;
error = devfs_dntovn(node, result_vnode, p);
}
DEVFS_UNLOCK();
if (error) {
if (error == EAGAIN)
goto retry;
return error;
}
if (!nodename) {
if (!(flags & ISLASTCN) || !(op == CREATE || op == RENAME)) {
return ENOENT;
}
return (EJUSTRETURN);
}
if (op == DELETE && (flags & ISLASTCN)) {
if (dir_node == node) {
if (*result_vnode) {
vnode_put(*result_vnode);
*result_vnode = NULL;
}
if ( ((error = vnode_get(dir_vnode)) == 0) ) {
*result_vnode = dir_vnode;
}
return (error);
}
return (0);
}
if (op == RENAME && wantparent && (flags & ISLASTCN)) {
if (dir_node == node) {
error = EISDIR;
goto drop_ref;
}
return (0);
}
if ((flags & ISDOTDOT) == 0 && dir_node == node) {
if (*result_vnode) {
vnode_put(*result_vnode);
*result_vnode = NULL;
}
if ( (error = vnode_get(dir_vnode)) ) {
return (error);
}
*result_vnode = dir_vnode;
}
return (0);
drop_ref:
if (*result_vnode) {
vnode_put(*result_vnode);
*result_vnode = NULL;
}
return (error);
}
static int
devfs_getattr(struct vnop_getattr_args *ap)
{
struct vnode *vp = ap->a_vp;
struct vnode_attr *vap = ap->a_vap;
devnode_t * file_node;
struct timeval now;
DEVFS_LOCK();
file_node = VTODN(vp);
microtime(&now);
dn_times(file_node, &now, &now, &now);
VATTR_RETURN(vap, va_mode, file_node->dn_mode);
switch (file_node->dn_type)
{
case DEV_DIR:
VATTR_RETURN(vap, va_rdev, (dev_t)file_node->dn_dvm);
vap->va_mode |= (S_IFDIR);
break;
case DEV_CDEV:
VATTR_RETURN(vap, va_rdev, vp->v_rdev);
vap->va_mode |= (S_IFCHR);
break;
case DEV_BDEV:
VATTR_RETURN(vap, va_rdev, vp->v_rdev);
vap->va_mode |= (S_IFBLK);
break;
case DEV_SLNK:
VATTR_RETURN(vap, va_rdev, 0);
vap->va_mode |= (S_IFLNK);
break;
default:
VATTR_RETURN(vap, va_rdev, 0);
}
VATTR_RETURN(vap, va_type, vp->v_type);
VATTR_RETURN(vap, va_nlink, file_node->dn_links);
VATTR_RETURN(vap, va_uid, file_node->dn_uid);
VATTR_RETURN(vap, va_gid, file_node->dn_gid);
VATTR_RETURN(vap, va_fsid, (uintptr_t)file_node->dn_dvm);
VATTR_RETURN(vap, va_fileid, (uintptr_t)file_node);
VATTR_RETURN(vap, va_data_size, file_node->dn_len);
if (vp->v_type == VBLK)
VATTR_RETURN(vap, va_iosize, BLKDEV_IOSIZE);
else if (vp->v_type == VCHR)
VATTR_RETURN(vap, va_iosize, MAXPHYSIO);
else
VATTR_RETURN(vap, va_iosize, vp->v_mount->mnt_vfsstat.f_iosize);
if (file_node->dn_ctime.tv_sec == 0) {
file_node->dn_ctime.tv_sec = boottime_sec();
file_node->dn_ctime.tv_nsec = 0;
}
if (file_node->dn_mtime.tv_sec == 0)
file_node->dn_mtime = file_node->dn_ctime;
if (file_node->dn_atime.tv_sec == 0)
file_node->dn_atime = file_node->dn_ctime;
VATTR_RETURN(vap, va_change_time, file_node->dn_ctime);
VATTR_RETURN(vap, va_modify_time, file_node->dn_mtime);
VATTR_RETURN(vap, va_access_time, file_node->dn_atime);
VATTR_RETURN(vap, va_gen, 0);
VATTR_RETURN(vap, va_flags, 0);
VATTR_RETURN(vap, va_filerev, 0);
VATTR_RETURN(vap, va_acl, NULL);
DEVFS_UNLOCK();
return 0;
}
static int
devfs_setattr(struct vnop_setattr_args *ap)
{
struct vnode *vp = ap->a_vp;
struct vnode_attr *vap = ap->a_vap;
int error = 0;
devnode_t * file_node;
struct timeval atimeval, mtimeval;
DEVFS_LOCK();
file_node = VTODN(vp);
if (VATTR_IS_ACTIVE(vap, va_access_time) || VATTR_IS_ACTIVE(vap, va_modify_time)) {
if (VATTR_IS_ACTIVE(vap, va_access_time))
file_node->dn_access = 1;
if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
file_node->dn_change = 1;
file_node->dn_update = 1;
}
atimeval.tv_sec = vap->va_access_time.tv_sec;
atimeval.tv_usec = vap->va_access_time.tv_nsec / 1000;
mtimeval.tv_sec = vap->va_modify_time.tv_sec;
mtimeval.tv_usec = vap->va_modify_time.tv_nsec / 1000;
if ( (error = devfs_update(vp, &atimeval, &mtimeval)) )
goto exit;
}
VATTR_SET_SUPPORTED(vap, va_access_time);
VATTR_SET_SUPPORTED(vap, va_change_time);
if (VATTR_IS_ACTIVE(vap, va_mode)) {
file_node->dn_mode &= ~07777;
file_node->dn_mode |= vap->va_mode & 07777;
}
VATTR_SET_SUPPORTED(vap, va_mode);
if (VATTR_IS_ACTIVE(vap, va_uid))
file_node->dn_uid = vap->va_uid;
VATTR_SET_SUPPORTED(vap, va_uid);
if (VATTR_IS_ACTIVE(vap, va_gid))
file_node->dn_gid = vap->va_gid;
VATTR_SET_SUPPORTED(vap, va_gid);
exit:
DEVFS_UNLOCK();
return error;
}
#if CONFIG_MACF
static int
devfs_setlabel(struct vnop_setlabel_args *ap)
{
struct vnode *vp;
struct devnode *de;
vp = ap->a_vp;
de = VTODN(vp);
mac_vnode_label_update(ap->a_context, vp, ap->a_vl);
mac_devfs_label_update(vp->v_mount, de, vp);
return (0);
}
#endif
static int
devfs_read(struct vnop_read_args *ap)
{
devnode_t * dn_p = VTODN(ap->a_vp);
switch (ap->a_vp->v_type) {
case VDIR: {
dn_p->dn_access = 1;
return VNOP_READDIR(ap->a_vp, ap->a_uio, 0, NULL, NULL, ap->a_context);
}
default: {
printf("devfs_read(): bad file type %d", ap->a_vp->v_type);
return(EINVAL);
break;
}
}
return (0);
}
static int
devfs_close(struct vnop_close_args *ap)
{
struct vnode * vp = ap->a_vp;
register devnode_t * dnp;
struct timeval now;
if (vnode_isinuse(vp, 1)) {
DEVFS_LOCK();
dnp = VTODN(vp);
microtime(&now);
dn_times(dnp, &now, &now, &now);
DEVFS_UNLOCK();
}
return (0);
}
static int
devfsspec_close(struct vnop_close_args *ap)
{
struct vnode * vp = ap->a_vp;
register devnode_t * dnp;
struct timeval now;
if (vnode_isinuse(vp, 1)) {
DEVFS_LOCK();
microtime(&now);
dnp = VTODN(vp);
dn_times(dnp, &now, &now, &now);
DEVFS_UNLOCK();
}
return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_close), ap));
}
static int
devfsspec_read(struct vnop_read_args *ap)
{
register devnode_t * dnp = VTODN(ap->a_vp);
dnp->dn_access = 1;
return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_read), ap));
}
static int
devfsspec_write(struct vnop_write_args *ap)
{
register devnode_t * dnp = VTODN(ap->a_vp);
dnp->dn_change = 1;
dnp->dn_update = 1;
return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_write), ap));
}
static int
devfs_write(struct vnop_write_args *ap)
{
switch (ap->a_vp->v_type) {
case VDIR:
return(EISDIR);
default:
printf("devfs_write(): bad file type %d", ap->a_vp->v_type);
return (EINVAL);
}
return 0;
}
static int
devfs_remove(struct vnop_remove_args *ap)
{
struct vnode *vp = ap->a_vp;
struct vnode *dvp = ap->a_dvp;
struct componentname *cnp = ap->a_cnp;
devnode_t * tp;
devnode_t * tdp;
devdirent_t * tnp;
int doingdirectory = 0;
int error = 0;
DEVFS_LOCK();
tp = VTODN(vp);
tdp = VTODN(dvp);
tnp = dev_findname(tdp, cnp->cn_nameptr);
if (tnp == NULL) {
error = ENOENT;
goto abort;
}
if ((tp->dn_type) == DEV_DIR) {
if ( (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')
|| (cnp->cn_flags&ISDOTDOT) ) {
error = EINVAL;
goto abort;
}
doingdirectory++;
}
tdp->dn_change = 1;
tdp->dn_update = 1;
if (( doingdirectory) && (tp->dn_links > 2)) {
error = ENOTEMPTY;
goto abort;
}
dev_free_name(tnp);
abort:
DEVFS_UNLOCK();
return (error);
}
static int
devfs_link(struct vnop_link_args *ap)
{
struct vnode *vp = ap->a_vp;
struct vnode *tdvp = ap->a_tdvp;
struct componentname *cnp = ap->a_cnp;
devnode_t * fp;
devnode_t * tdp;
devdirent_t * tnp;
int error = 0;
struct timeval now;
if (cnp->cn_namelen > DEVMAXNAMESIZE) {
error = ENAMETOOLONG;
goto out1;
}
tdp = VTODN(tdvp);
if (tdvp->v_mount != vp->v_mount) {
return (EXDEV);
}
DEVFS_LOCK();
fp = VTODN(vp);
fp->dn_change = 1;
microtime(&now);
error = devfs_update(vp, &now, &now);
if (!error) {
error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp);
}
out1:
DEVFS_UNLOCK();
return (error);
}
static int
devfs_rename(struct vnop_rename_args *ap)
{
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;
devnode_t *fp, *fdp, *tp, *tdp;
devdirent_t *fnp,*tnp;
int doingdirectory = 0;
int error = 0;
struct timeval now;
DEVFS_LOCK();
if (tcnp->cn_namelen > DEVMAXNAMESIZE) {
error = ENAMETOOLONG;
goto out;
}
tdp = VTODN(tdvp);
fdp = VTODN(fdvp);
fp = VTODN(fvp);
fnp = dev_findname(fdp, fcnp->cn_nameptr);
if (fnp == NULL) {
error = ENOENT;
goto out;
}
tp = NULL;
tnp = NULL;
if (tvp) {
tnp = dev_findname(tdp, tcnp->cn_nameptr);
if (tnp == NULL) {
error = ENOENT;
goto out;
}
tp = VTODN(tvp);
}
if ((fp->dn_type) == DEV_DIR) {
if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.')
|| (fcnp->cn_flags&ISDOTDOT)
|| (tcnp->cn_namelen == 1 && tcnp->cn_nameptr[0] == '.')
|| (tcnp->cn_flags&ISDOTDOT)
|| (tdp == fp )) {
error = EINVAL;
goto out;
}
doingdirectory++;
}
if (doingdirectory && (tdp != fdp)) {
devnode_t * tmp, *ntmp;
tmp = tdp;
do {
if(tmp == fp) {
error = EINVAL;
goto out;
}
ntmp = tmp;
} while ((tmp = tmp->dn_typeinfo.Dir.parent) != ntmp);
}
fp->dn_change = 1;
microtime(&now);
if ( (error = devfs_update(fvp, &now, &now)) ) {
goto out;
}
if (fvp == tvp) {
if (fvp->v_type == VDIR) {
error = EINVAL;
goto out;
}
dev_free_name(fnp);
DEVFS_UNLOCK();
return 0;
}
fp->dn_links++;
if (tp) {
if (( doingdirectory) && (tp->dn_links > 2)) {
error = ENOTEMPTY;
goto bad;
}
dev_free_name(tnp);
tp = NULL;
}
dev_add_name(tcnp->cn_nameptr,tdp,NULL,fp,&tnp);
fnp->de_dnp = NULL;
fp->dn_links--;
dev_free_name(fnp);
bad:
fp->dn_links--;
out:
DEVFS_UNLOCK();
return (error);
}
static int
devfs_mkdir(struct vnop_mkdir_args *ap)
{
struct componentname * cnp = ap->a_cnp;
vfs_context_t ctx = cnp->cn_context;
struct proc *p = vfs_context_proc(ctx);
int error = 0;
devnode_t * dir_p;
devdirent_t * nm_p;
devnode_t * dev_p;
struct vnode_attr * vap = ap->a_vap;
struct vnode * * vpp = ap->a_vpp;
DEVFS_LOCK();
dir_p = VTODN(ap->a_dvp);
error = dev_add_entry(cnp->cn_nameptr, dir_p, DEV_DIR,
NULL, NULL, NULL, &nm_p);
if (error) {
goto failure;
}
dev_p = nm_p->de_dnp;
dev_p->dn_uid = dir_p->dn_uid;
dev_p->dn_gid = dir_p->dn_gid;
dev_p->dn_mode = vap->va_mode;
dn_copy_times(dev_p, dir_p);
error = devfs_dntovn(dev_p, vpp, p);
failure:
DEVFS_UNLOCK();
return error;
}
static int
devfs_rmdir(struct vnop_rmdir_args *ap)
{
struct vnop_remove_args ra;
ra.a_dvp = ap->a_dvp;
ra.a_vp = ap->a_vp;
ra.a_cnp = ap->a_cnp;
ra.a_flags = 0;
ra.a_context = ap->a_context;
return devfs_remove(&ra);
}
static int
devfs_symlink(struct vnop_symlink_args *ap)
{
struct componentname * cnp = ap->a_cnp;
vfs_context_t ctx = cnp->cn_context;
struct proc *p = vfs_context_proc(ctx);
int error = 0;
devnode_t * dir_p;
devnode_type_t typeinfo;
devdirent_t * nm_p;
devnode_t * dev_p;
struct vnode_attr * vap = ap->a_vap;
struct vnode * * vpp = ap->a_vpp;
typeinfo.Slnk.name = ap->a_target;
typeinfo.Slnk.namelen = strlen(ap->a_target);
DEVFS_LOCK();
dir_p = VTODN(ap->a_dvp);
error = dev_add_entry(cnp->cn_nameptr, dir_p, DEV_SLNK,
&typeinfo, NULL, NULL, &nm_p);
if (error) {
goto failure;
}
dev_p = nm_p->de_dnp;
dev_p->dn_uid = dir_p->dn_uid;
dev_p->dn_gid = dir_p->dn_gid;
dev_p->dn_mode = vap->va_mode;
dn_copy_times(dev_p, dir_p);
error = devfs_dntovn(dev_p, vpp, p);
failure:
DEVFS_UNLOCK();
return error;
}
static int
devfs_mknod(struct vnop_mknod_args *ap)
{
struct componentname * cnp = ap->a_cnp;
vfs_context_t ctx = cnp->cn_context;
struct proc *p = vfs_context_proc(ctx);
devnode_t * dev_p;
devdirent_t * devent;
devnode_t * dir_p;
struct vnode * dvp = ap->a_dvp;
int error = 0;
devnode_type_t typeinfo;
struct vnode_attr * vap = ap->a_vap;
struct vnode ** vpp = ap->a_vpp;
*vpp = NULL;
if (!(vap->va_type == VBLK) && !(vap->va_type == VCHR)) {
return (EINVAL);
}
typeinfo.dev = vap->va_rdev;
DEVFS_LOCK();
dir_p = VTODN(dvp);
error = dev_add_entry(cnp->cn_nameptr, dir_p,
(vap->va_type == VBLK) ? DEV_BDEV : DEV_CDEV,
&typeinfo, NULL, NULL, &devent);
if (error) {
goto failure;
}
dev_p = devent->de_dnp;
error = devfs_dntovn(dev_p, vpp, p);
if (error)
goto failure;
dev_p->dn_uid = vap->va_uid;
dev_p->dn_gid = vap->va_gid;
dev_p->dn_mode = vap->va_mode;
VATTR_SET_SUPPORTED(vap, va_uid);
VATTR_SET_SUPPORTED(vap, va_gid);
VATTR_SET_SUPPORTED(vap, va_mode);
failure:
DEVFS_UNLOCK();
return (error);
}
static int
devfs_readdir(struct vnop_readdir_args *ap)
{
struct vnode *vp = ap->a_vp;
struct uio *uio = ap->a_uio;
struct dirent dirent;
devnode_t * dir_node;
devdirent_t * name_node;
const char *name;
int error = 0;
int reclen;
int nodenumber;
int startpos,pos;
if (ap->a_flags & (VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF))
return (EINVAL);
dir_node = VTODN(vp);
if (dir_node->dn_type != DEV_DIR)
return(ENOTDIR);
pos = 0;
startpos = uio->uio_offset;
DEVFS_LOCK();
name_node = dir_node->dn_typeinfo.Dir.dirlist;
nodenumber = 0;
dir_node->dn_access = 1;
while ((name_node || (nodenumber < 2)) && (uio_resid(uio) > 0))
{
switch(nodenumber)
{
case 0:
dirent.d_fileno = (int32_t)(void *)dir_node;
name = ".";
dirent.d_namlen = 1;
dirent.d_type = DT_DIR;
break;
case 1:
if(dir_node->dn_typeinfo.Dir.parent)
dirent.d_fileno
= (int32_t)dir_node->dn_typeinfo.Dir.parent;
else
dirent.d_fileno = (u_int32_t)dir_node;
name = "..";
dirent.d_namlen = 2;
dirent.d_type = DT_DIR;
break;
default:
dirent.d_fileno = (int32_t)(void *)name_node->de_dnp;
dirent.d_namlen = strlen(name_node->de_name);
name = name_node->de_name;
switch(name_node->de_dnp->dn_type) {
case DEV_BDEV:
dirent.d_type = DT_BLK;
break;
case DEV_CDEV:
dirent.d_type = DT_CHR;
break;
case DEV_DIR:
dirent.d_type = DT_DIR;
break;
case DEV_SLNK:
dirent.d_type = DT_LNK;
break;
default:
dirent.d_type = DT_UNKNOWN;
}
}
#define GENERIC_DIRSIZ(dp) \
((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
reclen = dirent.d_reclen = GENERIC_DIRSIZ(&dirent);
if(pos >= startpos)
{
if (uio_resid(uio) < reclen)
break;
strlcpy(dirent.d_name, name, DEVMAXNAMESIZE);
if ((error = uiomove ((caddr_t)&dirent,
dirent.d_reclen, uio)) != 0)
break;
}
pos += reclen;
if((nodenumber >1) && name_node)
name_node = name_node->de_next;
nodenumber++;
}
DEVFS_UNLOCK();
uio->uio_offset = pos;
return (error);
}
static int
devfs_readlink(struct vnop_readlink_args *ap)
{
struct vnode *vp = ap->a_vp;
struct uio *uio = ap->a_uio;
devnode_t * lnk_node;
int error = 0;
lnk_node = VTODN(vp);
if (lnk_node->dn_type != DEV_SLNK) {
error = EINVAL;
goto out;
}
error = uiomove(lnk_node->dn_typeinfo.Slnk.name,
lnk_node->dn_typeinfo.Slnk.namelen, uio);
out:
return error;
}
static int
devfs_reclaim(struct vnop_reclaim_args *ap)
{
struct vnode * vp = ap->a_vp;
devnode_t * dnp;
DEVFS_LOCK();
dnp = VTODN(vp);
if (dnp) {
dnp->dn_vn = NULL;
vp->v_data = NULL;
if (dnp->dn_delete) {
devnode_free(dnp);
}
}
DEVFS_UNLOCK();
return(0);
}
static int
devs_vnop_pathconf(
struct vnop_pathconf_args *ap)
{
switch (ap->a_name) {
case _PC_LINK_MAX:
*ap->a_retval = 32767;
break;
case _PC_NAME_MAX:
*ap->a_retval = DEVMAXNAMESIZE - 1;
break;
case _PC_PATH_MAX:
*ap->a_retval = DEVMAXPATHSIZE - 1;
break;
case _PC_CHOWN_RESTRICTED:
*ap->a_retval = 200112;
break;
case _PC_NO_TRUNC:
*ap->a_retval = 0;
break;
case _PC_CASE_SENSITIVE:
*ap->a_retval = 1;
break;
case _PC_CASE_PRESERVING:
*ap->a_retval = 1;
break;
default:
return (EINVAL);
}
return (0);
}
static int
devfs_inactive(__unused struct vnop_inactive_args *ap)
{
return (0);
}
static int
devfs_update(struct vnode *vp, struct timeval *access, struct timeval *modify)
{
devnode_t * ip;
struct timeval now;
ip = VTODN(vp);
if (vp->v_mount->mnt_flag & MNT_RDONLY) {
ip->dn_access = 0;
ip->dn_change = 0;
ip->dn_update = 0;
return (0);
}
microtime(&now);
dn_times(ip, access, modify, &now);
return (0);
}
#define VOPFUNC int (*)(void *)
int (**devfs_vnodeop_p)(void *);
static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = {
{ &vnop_default_desc, (VOPFUNC)vn_default_error },
{ &vnop_lookup_desc, (VOPFUNC)devfs_lookup },
{ &vnop_create_desc, (VOPFUNC)err_create },
{ &vnop_whiteout_desc, (VOPFUNC)err_whiteout },
{ &vnop_mknod_desc, (VOPFUNC)devfs_mknod },
{ &vnop_open_desc, (VOPFUNC)nop_open },
{ &vnop_close_desc, (VOPFUNC)devfs_close },
{ &vnop_getattr_desc, (VOPFUNC)devfs_getattr },
{ &vnop_setattr_desc, (VOPFUNC)devfs_setattr },
{ &vnop_read_desc, (VOPFUNC)devfs_read },
{ &vnop_write_desc, (VOPFUNC)devfs_write },
{ &vnop_ioctl_desc, (VOPFUNC)err_ioctl },
{ &vnop_select_desc, (VOPFUNC)err_select },
{ &vnop_revoke_desc, (VOPFUNC)err_revoke },
{ &vnop_mmap_desc, (VOPFUNC)err_mmap },
{ &vnop_fsync_desc, (VOPFUNC)nop_fsync },
{ &vnop_remove_desc, (VOPFUNC)devfs_remove },
{ &vnop_link_desc, (VOPFUNC)devfs_link },
{ &vnop_rename_desc, (VOPFUNC)devfs_rename },
{ &vnop_mkdir_desc, (VOPFUNC)devfs_mkdir },
{ &vnop_rmdir_desc, (VOPFUNC)devfs_rmdir },
{ &vnop_symlink_desc, (VOPFUNC)devfs_symlink },
{ &vnop_readdir_desc, (VOPFUNC)devfs_readdir },
{ &vnop_readlink_desc, (VOPFUNC)devfs_readlink },
{ &vnop_inactive_desc, (VOPFUNC)devfs_inactive },
{ &vnop_reclaim_desc, (VOPFUNC)devfs_reclaim },
{ &vnop_strategy_desc, (VOPFUNC)err_strategy },
{ &vnop_pathconf_desc, (VOPFUNC)devs_vnop_pathconf },
{ &vnop_advlock_desc, (VOPFUNC)err_advlock },
{ &vnop_bwrite_desc, (VOPFUNC)err_bwrite },
{ &vnop_pagein_desc, (VOPFUNC)err_pagein },
{ &vnop_pageout_desc, (VOPFUNC)err_pageout },
{ &vnop_copyfile_desc, (VOPFUNC)err_copyfile },
{ &vnop_blktooff_desc, (VOPFUNC)err_blktooff },
{ &vnop_offtoblk_desc, (VOPFUNC)err_offtoblk },
{ &vnop_blockmap_desc, (VOPFUNC)err_blockmap },
#if CONFIG_MACF
{ &vnop_setlabel_desc, (VOPFUNC)devfs_setlabel },
#endif
{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
};
struct vnodeopv_desc devfs_vnodeop_opv_desc =
{ &devfs_vnodeop_p, devfs_vnodeop_entries };
int (**devfs_spec_vnodeop_p)(void *);
static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries[] = {
{ &vnop_default_desc, (VOPFUNC)vn_default_error },
{ &vnop_lookup_desc, (VOPFUNC)spec_lookup },
{ &vnop_create_desc, (VOPFUNC)spec_create },
{ &vnop_mknod_desc, (VOPFUNC)spec_mknod },
{ &vnop_open_desc, (VOPFUNC)spec_open },
{ &vnop_close_desc, (VOPFUNC)devfsspec_close },
{ &vnop_getattr_desc, (VOPFUNC)devfs_getattr },
{ &vnop_setattr_desc, (VOPFUNC)devfs_setattr },
{ &vnop_read_desc, (VOPFUNC)devfsspec_read },
{ &vnop_write_desc, (VOPFUNC)devfsspec_write },
{ &vnop_ioctl_desc, (VOPFUNC)spec_ioctl },
{ &vnop_select_desc, (VOPFUNC)spec_select },
{ &vnop_revoke_desc, (VOPFUNC)spec_revoke },
{ &vnop_mmap_desc, (VOPFUNC)spec_mmap },
{ &vnop_fsync_desc, (VOPFUNC)spec_fsync },
{ &vnop_remove_desc, (VOPFUNC)devfs_remove },
{ &vnop_link_desc, (VOPFUNC)devfs_link },
{ &vnop_rename_desc, (VOPFUNC)spec_rename },
{ &vnop_mkdir_desc, (VOPFUNC)spec_mkdir },
{ &vnop_rmdir_desc, (VOPFUNC)spec_rmdir },
{ &vnop_symlink_desc, (VOPFUNC)spec_symlink },
{ &vnop_readdir_desc, (VOPFUNC)spec_readdir },
{ &vnop_readlink_desc, (VOPFUNC)spec_readlink },
{ &vnop_inactive_desc, (VOPFUNC)devfs_inactive },
{ &vnop_reclaim_desc, (VOPFUNC)devfs_reclaim },
{ &vnop_strategy_desc, (VOPFUNC)spec_strategy },
{ &vnop_pathconf_desc, (VOPFUNC)spec_pathconf },
{ &vnop_advlock_desc, (VOPFUNC)spec_advlock },
{ &vnop_bwrite_desc, (VOPFUNC)vn_bwrite },
{ &vnop_pagein_desc, (VOPFUNC)err_pagein },
{ &vnop_pageout_desc, (VOPFUNC)err_pageout },
{ &vnop_copyfile_desc, (VOPFUNC)err_copyfile },
{ &vnop_blktooff_desc, (VOPFUNC)spec_blktooff },
{ &vnop_blktooff_desc, (VOPFUNC)spec_offtoblk },
{ &vnop_blockmap_desc, (VOPFUNC)spec_blockmap },
#if CONFIG_MACF
{ &vnop_setlabel_desc, (VOPFUNC)devfs_setlabel },
#endif
{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
};
struct vnodeopv_desc devfs_spec_vnodeop_opv_desc =
{ &devfs_spec_vnodeop_p, devfs_spec_vnodeop_entries };