]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/miscfs/devfs/devfs_vnops.c
xnu-6153.81.5.tar.gz
[apple/xnu.git] / bsd / miscfs / devfs / devfs_vnops.c
index 6c25d8044731fabf33daffb4a72e445896731e41..b9de4b101f06b01a7f7cf33f0a5f6c2d161d495e 100644 (file)
@@ -1,28 +1,34 @@
 /*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
  *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
- * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 /*
  * Copyright 1997,1998 Julian Elischer.  All rights reserved.
  * julian@freebsd.org
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
  * met:
@@ -31,7 +37,7 @@
  *  2. Redistributions in binary form must reproduce the above copyright notice,
  *     this list of conditions and the following disclaimer in the documentation
  *     and/or other materials provided with the distribution.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS
  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -43,7 +49,7 @@
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- * 
+ *
  * devfs_vnops.c
  */
 
  *  Dieter Siegmund (dieter@apple.com) Fri Sep 17 09:58:38 PDT 1999
  *  - update the mod/access times
  */
+/*
+ * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
+ * support for mandatory and extensible security protections.  This notice
+ * is included in support of clause 2.2 (b) of the Apple Public License,
+ * Version 2.0.
+ */
 
 #include <sys/param.h>
 #include <sys/systm.h>
-#include <sys/buf.h>
 #include <sys/namei.h>
 #include <sys/kernel.h>
 #include <sys/fcntl.h>
 #include <sys/disklabel.h>
 #include <sys/lock.h>
 #include <sys/stat.h>
-#include <sys/mount.h>
+#include <sys/mount_internal.h>
 #include <sys/proc.h>
+#include <sys/kauth.h>
 #include <sys/time.h>
-#include <sys/vnode.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"
+#include "devfs.h"
+
+#if FDESC
+#include "fdesc.h"
+#endif /* FDESC */
+
+static int              devfs_update(struct vnode *vp, struct timeval *access,
+    struct timeval *modify);
+void                    devfs_rele_node(devnode_t *);
+static void             devfs_consider_time_update(devnode_t *dnp, uint32_t just_changed_flags);
+static boolean_t        devfs_update_needed(long now_s, long last_s);
+static boolean_t        devfs_is_name_protected(struct vnode *dvp, const char *name);
+void                    dn_times_locked(devnode_t * dnp, struct timeval *t1, struct timeval *t2, struct timeval *t3, uint32_t just_changed_flags);
+void                    dn_times_now(devnode_t *dnp, uint32_t just_changed_flags);
+void                    dn_mark_for_delayed_times_update(devnode_t *dnp, uint32_t just_changed_flags);
+
+void
+dn_times_locked(devnode_t * dnp, struct timeval *t1, struct timeval *t2, struct timeval *t3, uint32_t just_changed_flags)
+{
+       lck_mtx_assert(&devfs_attr_mutex, LCK_MTX_ASSERT_OWNED);
+
+       if (just_changed_flags & DEVFS_UPDATE_ACCESS) {
+               dnp->dn_atime.tv_sec = t1->tv_sec;
+               dnp->dn_atime.tv_nsec = t1->tv_usec * 1000;
+               dnp->dn_access = 0;
+       } else if (dnp->dn_access) {
+               dnp->dn_atime.tv_sec = MIN(t1->tv_sec, dnp->dn_atime.tv_sec + DEVFS_LAZY_UPDATE_SECONDS);
+               dnp->dn_atime.tv_nsec = t1->tv_usec * 1000;
+               dnp->dn_access = 0;
+       }
+
+       if (just_changed_flags & DEVFS_UPDATE_MOD) {
+               dnp->dn_mtime.tv_sec = t2->tv_sec;
+               dnp->dn_mtime.tv_nsec = t2->tv_usec * 1000;
+               dnp->dn_update = 0;
+       } else if (dnp->dn_update) {
+               dnp->dn_mtime.tv_sec = MIN(t2->tv_sec, dnp->dn_mtime.tv_sec + DEVFS_LAZY_UPDATE_SECONDS);
+               dnp->dn_mtime.tv_nsec = t2->tv_usec * 1000;
+               dnp->dn_update = 0;
+       }
+
+       if (just_changed_flags & DEVFS_UPDATE_CHANGE) {
+               dnp->dn_ctime.tv_sec = t3->tv_sec;
+               dnp->dn_ctime.tv_nsec = t3->tv_usec * 1000;
+               dnp->dn_change = 0;
+       } else if (dnp->dn_change) {
+               dnp->dn_ctime.tv_sec = MIN(t3->tv_sec, dnp->dn_ctime.tv_sec + DEVFS_LAZY_UPDATE_SECONDS);
+               dnp->dn_ctime.tv_nsec = t3->tv_usec * 1000;
+               dnp->dn_change = 0;
+       }
+}
+
+void
+dn_mark_for_delayed_times_update(devnode_t *dnp, uint32_t just_changed_flags)
+{
+       if (just_changed_flags & DEVFS_UPDATE_CHANGE) {
+               dnp->dn_change = 1;
+       }
+       if (just_changed_flags & DEVFS_UPDATE_ACCESS) {
+               dnp->dn_access = 1;
+       }
+       if (just_changed_flags & DEVFS_UPDATE_MOD) {
+               dnp->dn_update = 1;
+       }
+}
+
+/*
+ * Update times based on pending updates and optionally a set of new changes.
+ */
+void
+dn_times_now(devnode_t * dnp, uint32_t just_changed_flags)
+{
+       struct timeval now;
+
+       DEVFS_ATTR_LOCK_SPIN();
+       microtime(&now);
+       dn_times_locked(dnp, &now, &now, &now, just_changed_flags);
+       DEVFS_ATTR_UNLOCK();
+}
+
+/*
+ * Critical devfs devices cannot be renamed or removed.
+ * However, links to them may be moved/unlinked. So we block
+ * remove/rename on a per-name basis, rather than per-node.
+ */
+static boolean_t
+devfs_is_name_protected(struct vnode *dvp, const char *name)
+{
+       /*
+        * Only names in root are protected. E.g. /dev/null is protected,
+        * but /dev/foo/null isn't.
+        */
+       if (!vnode_isvroot(dvp)) {
+               return FALSE;
+       }
+
+       if ((strcmp("console", name) == 0) ||
+           (strcmp("tty", name) == 0) ||
+           (strcmp("null", name) == 0) ||
+           (strcmp("zero", name) == 0) ||
+           (strcmp("klog", name) == 0)) {
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
 
 /*
  * Convert a component of a pathname into a pointer to a locked node.
  * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent node unlocked.
  */
 static int
-devfs_lookup(struct vop_lookup_args *ap)
-        /*struct vop_lookup_args {
-                struct vnode * a_dvp; directory vnode ptr
-                struct vnode ** a_vpp; where to put the result
-                struct componentname * a_cnp; the name we want
-        };*/
+devfs_lookup(struct vnop_lookup_args *ap)
+/*struct vnop_lookup_args {
+ *       struct vnode * a_dvp; directory vnode ptr
+ *       struct vnode ** a_vpp; where to put the result
+ *       struct componentname * a_cnp; the name we want
+ *       vfs_context_t a_context;
+ *  };*/
 {
        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;       /* the directory we are searching */
@@ -141,68 +268,68 @@ devfs_lookup(struct vop_lookup_args *ap)
        devdirent_t * nodename;
        int flags = cnp->cn_flags;
        int op = cnp->cn_nameiop;       /* LOOKUP, CREATE, RENAME, or DELETE */
-       int lockparent = flags & LOCKPARENT;
-       int wantparent = flags & (LOCKPARENT|WANTPARENT);
+       int wantparent = flags & (LOCKPARENT | WANTPARENT);
        int error = 0;
-       struct proc *p = cnp->cn_proc;
-       char    heldchar;       /* the char at the end of the name componet */
+       char    heldchar;       /* the char at the end of the name componet */
+
+retry:
 
        *result_vnode = NULL; /* safe not sorry */ /*XXX*/
 
-       if (dir_vnode->v_usecount == 0)
-           printf("devfs_lookup: dir had no refs ");
+       /*  okay to look at directory vnodes ourside devfs lock as they are not aliased */
        dir_node = VTODN(dir_vnode);
 
        /*
-        * Check accessiblity of directory.
+        * Make sure that our node is a directory as well.
         */
        if (dir_node->dn_type != DEV_DIR) {
-               return (ENOTDIR);
-       }
-
-       if ((error = VOP_ACCESS(dir_vnode, VEXEC, cnp->cn_cred, p)) != 0) {
-               return (error);
+               return ENOTDIR;
        }
 
-       /* temporarily terminate string component */
+       DEVFS_LOCK();
+       /*
+        * temporarily terminate string component
+        */
        heldchar = cnp->cn_nameptr[cnp->cn_namelen];
        cnp->cn_nameptr[cnp->cn_namelen] = '\0';
-       DEVFS_LOCK(p);
-       nodename = dev_findname(dir_node,cnp->cn_nameptr);
-       if (nodename) {
-           /* entry exists */
-           node = nodename->de_dnp;
-           node->dn_last_lookup = nodename; /* for unlink */
-           /* Do potential vnode allocation here inside the lock 
-            * to make sure that our device node has a non-NULL dn_vn
-            * associated with it.  The device node might otherwise
-            * get deleted out from under us (see devfs_dn_free()).
-            */
-           error = devfs_dntovn(node, result_vnode, p);
-       }
-       DEVFS_UNLOCK(p);
-       /* restore saved character */
+
+       nodename = dev_findname(dir_node, cnp->cn_nameptr);
+       /*
+        * restore saved character
+        */
        cnp->cn_nameptr[cnp->cn_namelen] = heldchar;
 
-       if (error)
-           return (error);
+       if (nodename) {
+               /* entry exists */
+               node = nodename->de_dnp;
 
-       if (!nodename) { /* no entry */
-               /* If it doesn't exist and we're not the last component,
-                * or we're at the last component, but we're not creating
-                * or renaming, return ENOENT.
+               /* Do potential vnode allocation here inside the lock
+                * to make sure that our device node has a non-NULL dn_vn
+                * associated with it.  The device node might otherwise
+                * get deleted out from under us (see devfs_dn_free()).
                 */
-               if (!(flags & ISLASTCN) || !(op == CREATE || op == RENAME)) {
-                       return ENOENT;
+               error = devfs_dntovn(node, result_vnode, p);
+       }
+       DEVFS_UNLOCK();
+
+       if (error) {
+               if (error == EAGAIN) {
+                       goto retry;
                }
+               return error;
+       }
+       if (!nodename) {
                /*
-                * Access for write is interpreted as allowing
-                * creation of files in the directory.
+                * we haven't called devfs_dntovn if we get here
+                * we have not taken a reference on the node.. no
+                * vnode_put is necessary on these error returns
+                *
+                * If it doesn't exist and we're not the last component,
+                * or we're at the last component, but we're not creating
+                * or renaming, return ENOENT.
                 */
-               if ((error = VOP_ACCESS(dir_vnode, VWRITE,
-                               cnp->cn_cred, p)) != 0)
-               {
-                       return (error);
+               if (!(flags & ISLASTCN) || !(op == CREATE || op == RENAME)) {
+                       return ENOENT;
                }
                /*
                 * We return with the directory locked, so that
@@ -211,17 +338,16 @@ devfs_lookup(struct vop_lookup_args *ap)
                 * We return ni_vp == NULL to indicate that the entry
                 * does not currently exist; we leave a pointer to
                 * the (locked) directory vnode in namei_data->ni_dvp.
-                * The pathname buffer is saved so that the name
-                * can be obtained later.
                 *
                 * NB - if the directory is unlocked, then this
                 * information cannot be used.
                 */
-               cnp->cn_flags |= SAVENAME;
-               if (!lockparent)
-                       VOP_UNLOCK(dir_vnode, 0, p);
-               return (EJUSTRETURN);
+               return EJUSTRETURN;
        }
+       /*
+        * from this point forward, we need to vnode_put the reference
+        * picked up in devfs_dntovn if we decide to return an error
+        */
 
        /*
         * If deleting, and at end of pathname, return
@@ -231,38 +357,20 @@ devfs_lookup(struct vop_lookup_args *ap)
         * on and lock the node, being careful with ".".
         */
        if (op == DELETE && (flags & ISLASTCN)) {
-               /*
-                * Write access to directory required to delete files.
-                */
-               if ((error = VOP_ACCESS(dir_vnode, VWRITE,
-                               cnp->cn_cred, p)) != 0)
-                       return (error);
                /*
                 * we are trying to delete '.'.  What does this mean? XXX
                 */
                if (dir_node == node) {
-                       VREF(dir_vnode);
-                       *result_vnode = dir_vnode;
-                       return (0);
-               }
-#ifdef NOTYET
-               /*
-                * If directory is "sticky", then user must own
-                * the directory, or the file in it, else she
-                * may not delete it (unless she's root). This
-                * implements append-only directories.
-                */
-               if ((dir_node->mode & ISVTX) &&
-                   cnp->cn_cred->cr_uid != 0 &&
-                   cnp->cn_cred->cr_uid != dir_node->uid &&
-                   cnp->cn_cred->cr_uid != node->uid) {
-                       VOP_UNLOCK(*result_vnode, 0, p);
-                       return (EPERM);
+                       if (*result_vnode) {
+                               vnode_put(*result_vnode);
+                               *result_vnode = NULL;
+                       }
+                       if (((error = vnode_get(dir_vnode)) == 0)) {
+                               *result_vnode = dir_vnode;
+                       }
+                       return error;
                }
-#endif
-               if (!lockparent)
-                       VOP_UNLOCK(dir_vnode, 0, p);
-               return (0);
+               return 0;
        }
 
        /*
@@ -272,23 +380,15 @@ devfs_lookup(struct vop_lookup_args *ap)
         * regular file, or empty directory.
         */
        if (op == RENAME && wantparent && (flags & ISLASTCN)) {
-               /*
-                * Are we allowed to change the holding directory?
-                */
-               if ((error = VOP_ACCESS(dir_vnode, VWRITE,
-                               cnp->cn_cred, p)) != 0)
-                       return (error);
                /*
                 * Careful about locking second node.
                 * This can only occur if the target is ".".
                 */
-               if (dir_node == node)
-                       return (EISDIR);
-               /* hmm save the 'from' name (we need to delete it) */
-               cnp->cn_flags |= SAVENAME;
-               if (!lockparent)
-                       VOP_UNLOCK(dir_vnode, 0, p);
-               return (0);
+               if (dir_node == node) {
+                       error = EISDIR;
+                       goto drop_ref;
+               }
+               return 0;
        }
 
        /*
@@ -311,397 +411,407 @@ devfs_lookup(struct vop_lookup_args *ap)
         * work if the file system has any hard links other than ".."
         * that point backwards in the directory structure.
         */
-       if (flags & ISDOTDOT) {
-               VOP_UNLOCK(dir_vnode, 0, p);    /* race to get the node */
-               if (lockparent && (flags & ISLASTCN))
-                       vn_lock(dir_vnode, LK_EXCLUSIVE | LK_RETRY, p);
-       } else if (dir_node == node) {
-#if 0
-           /* 
-            * this next statement is wrong: we already did a vget in 
-            * devfs_dntovn(); DWS 4/16/1999
-            */
-                VREF(dir_vnode);        /* we want ourself, ie "." */
-#endif
+       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;
-       } else {
-               if (!lockparent || (flags & ISLASTCN))
-                       VOP_UNLOCK(dir_vnode, 0, p);
        }
+       return 0;
 
-       return (0);
+drop_ref:
+       if (*result_vnode) {
+               vnode_put(*result_vnode);
+               *result_vnode = NULL;
+       }
+       return error;
 }
 
 static int
-devfs_access(struct vop_access_args *ap)
-        /*struct vop_access_args  {
               struct vnode *a_vp;
               int  a_mode;
               struct ucred *a_cred;
               struct proc *a_p;
-        } */ 
+devfs_getattr(struct vnop_getattr_args *ap)
+/*struct vnop_getattr_args {
*       struct vnode *a_vp;
*       struct vnode_attr *a_vap;
*       kauth_cred_t a_cred;
*       struct proc *a_p;
+ *  } */
 {
-       /*
-        *  mode is filled with a combination of VREAD, VWRITE,
-        *  and/or VEXEC bits turned on.  In an octal number these
-        *  are the Y in 0Y00.
-        */
        struct vnode *vp = ap->a_vp;
-       int mode = ap->a_mode;
-       struct ucred *cred = ap->a_cred;
-       devnode_t *     file_node;
-       gid_t   *gp;
-       int     i;
-       struct proc *p = ap->a_p;
+       struct vnode_attr *vap = ap->a_vap;
+       devnode_t *     file_node;
+       struct timeval now;
 
+
+       DEVFS_LOCK();
        file_node = VTODN(vp);
-       /* 
-        * if we are not running as a process, we are in the 
-        * kernel and we DO have permission
-        */
-       if (p == NULL)
-           return 0;
+
+       VATTR_RETURN(vap, va_mode, file_node->dn_mode);
 
        /*
-        * Access check is based on only one of owner, group, public.
-        * If not owner, then check group. If not a member of the
-        * group, then check public access.
-        */
-       if (cred->cr_uid != file_node->dn_uid)
-       {
-               /* failing that.. try groups */
-               mode >>= 3;
-               gp = cred->cr_groups;
-               for (i = 0; i < cred->cr_ngroups; i++, gp++)
-               {
-                       if (file_node->dn_gid == *gp)
-                       {
-                               goto found;
-                       }
-               }
-               /* failing that.. try general access */
-               mode >>= 3;
-found:
-               ;
-       }
-       if ((file_node->dn_mode & mode) == mode)
-               return (0);
-       /*
-        *  Root gets to do anything.
-        * but only use suser prives as a last resort
-        * (Use of super powers is recorded in ap->a_p->p_acflag)
+        * Note: for DEV_CDEV and DEV_BDEV, we return the device from
+        * the vp, not the file_node; if we getting information on a
+        * cloning device, we want the cloned information, not the template.
         */
-       if( suser(cred, &ap->a_p->p_acflag) == 0) /* XXX what if no proc? */
-               return 0;
-       return (EACCES);
-}
-
-static int
-devfs_getattr(struct vop_getattr_args *ap)
-        /*struct vop_getattr_args {
-                struct vnode *a_vp;
-                struct vattr *a_vap;
-                struct ucred *a_cred;
-                struct proc *a_p;
-        } */ 
-{
-       struct vnode *vp = ap->a_vp;
-       struct vattr *vap = ap->a_vap;
-       devnode_t *     file_node;
-       struct timeval  tv;
-
-       file_node = VTODN(vp);
-       tv = time;
-       dn_times(file_node, tv, tv);
-       vap->va_rdev = 0;/* default value only */
-       vap->va_mode = file_node->dn_mode;
-       switch (file_node->dn_type)
-       {
-       case    DEV_DIR:
-               vap->va_rdev = (dev_t)file_node->dn_dvm;
+       switch (file_node->dn_type) {
+       case    DEV_DIR:
+#if FDESC
+       case    DEV_DEVFD:      /* Like a directory */
+#endif /* FDESC */
+               VATTR_RETURN(vap, va_rdev, 0);
                vap->va_mode |= (S_IFDIR);
                break;
-       case    DEV_CDEV:
-               vap->va_rdev = file_node->dn_typeinfo.dev;
+       case    DEV_CDEV:
+               VATTR_RETURN(vap, va_rdev, vp->v_rdev);
                vap->va_mode |= (S_IFCHR);
                break;
-       case    DEV_BDEV:
-               vap->va_rdev = file_node->dn_typeinfo.dev;
+       case    DEV_BDEV:
+               VATTR_RETURN(vap, va_rdev, vp->v_rdev);
                vap->va_mode |= (S_IFBLK);
                break;
-       case    DEV_SLNK:
+       case    DEV_SLNK:
+               VATTR_RETURN(vap, va_rdev, 0);
                vap->va_mode |= (S_IFLNK);
                break;
+       default:
+               VATTR_RETURN(vap, va_rdev, 0);  /* default value only */
        }
-       vap->va_type = vp->v_type;
-       vap->va_nlink = file_node->dn_links;
-       vap->va_uid = file_node->dn_uid;
-       vap->va_gid = file_node->dn_gid;
-       vap->va_fsid = (int32_t)(void *)file_node->dn_dvm;
-       vap->va_fileid = (int32_t)(void *)file_node;
-       vap->va_size = file_node->dn_len; /* now a u_quad_t */
-       /* this doesn't belong here */
-       if (vp->v_type == VBLK)
-               vap->va_blocksize = BLKDEV_IOSIZE;
-       else if (vp->v_type == VCHR)
-               vap->va_blocksize = MAXPHYSIO;
-       else
-               vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
+       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->dn_ino);
+       VATTR_RETURN(vap, va_data_size, file_node->dn_len);
+
+       /* return an override block size (advisory) */
+       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);
+       }
+
+
+       DEVFS_ATTR_LOCK_SPIN();
+
+       microtime(&now);
+       dn_times_locked(file_node, &now, &now, &now, 0);
+
        /* if the time is bogus, set it to the boot time */
-       if (file_node->dn_ctime.tv_sec == 0)
-           file_node->dn_ctime.tv_sec = boottime.tv_sec;
-       if (file_node->dn_mtime.tv_sec == 0)
-           file_node->dn_mtime.tv_sec = boottime.tv_sec;
-       if (file_node->dn_atime.tv_sec == 0)
-           file_node->dn_atime.tv_sec = boottime.tv_sec;
-       vap->va_ctime = file_node->dn_ctime;
-       vap->va_mtime = file_node->dn_mtime;
-       vap->va_atime = file_node->dn_atime;
-       vap->va_gen = 0;
-       vap->va_flags = 0;
-       vap->va_bytes = file_node->dn_len;              /* u_quad_t */
-       vap->va_filerev = 0; /* XXX */          /* u_quad_t */
-       vap->va_vaflags = 0; /* XXX */
+       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);
+
+       DEVFS_ATTR_UNLOCK();
+
+       VATTR_RETURN(vap, va_gen, 0);
+       VATTR_RETURN(vap, va_filerev, 0);
+       VATTR_RETURN(vap, va_acl, NULL);
+
+       /* Hide the root so Finder doesn't display it */
+       if (vnode_isvroot(vp)) {
+               VATTR_RETURN(vap, va_flags, UF_HIDDEN);
+       } else {
+               VATTR_RETURN(vap, va_flags, 0);
+       }
+
+       DEVFS_UNLOCK();
+
        return 0;
 }
 
 static int
-devfs_setattr(struct vop_setattr_args *ap)
-        /*struct vop_setattr_args  {
-                struct vnode *a_vp;
-                struct vattr *a_vap;
-                struct ucred *a_cred;
-                struct proc *a_p;
-        } */ 
+devfs_setattr(struct vnop_setattr_args *ap)
+/*struct vnop_setattr_args  {
+ *  struct vnode *a_vp;
+ *  struct vnode_attr *a_vap;
+ *  vfs_context_t a_context;
+ *  } */
 {
        struct vnode *vp = ap->a_vp;
-       struct vattr *vap = ap->a_vap;
-       struct ucred *cred = ap->a_cred;
-       struct proc *p = ap->a_p;
+       struct vnode_attr *vap = ap->a_vap;
        int error = 0;
-       gid_t *gp;
-       int i;
-       devnode_t *     file_node;
+       devnode_t *     file_node;
        struct timeval atimeval, mtimeval;
 
-       if (vap->va_flags != VNOVAL)    /* XXX needs to be implemented */
-               return (EOPNOTSUPP);
+       DEVFS_LOCK();
 
        file_node = VTODN(vp);
-
-       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)  ||
-           (vap->va_bytes != VNOVAL)  ||
-           (vap->va_gen != VNOVAL ))
-       {
-               return EINVAL;
-       }
-
        /*
-        * Go through the fields and update iff not VNOVAL.
+        * Go through the fields and update if set.
         */
-       if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
-           if (cred->cr_uid != file_node->dn_uid &&
-               (error = suser(cred, &p->p_acflag)) &&
-               ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || 
-                (error = VOP_ACCESS(vp, VWRITE, cred, p))))
-               return (error);
-           if (vap->va_atime.tv_sec != VNOVAL)
-               file_node->dn_flags |= DN_ACCESS;
-           if (vap->va_mtime.tv_sec != VNOVAL)
-               file_node->dn_flags |= DN_CHANGE | DN_UPDATE;
-           atimeval.tv_sec = vap->va_atime.tv_sec;
-           atimeval.tv_usec = vap->va_atime.tv_nsec / 1000;
-           mtimeval.tv_sec = vap->va_mtime.tv_sec;
-           mtimeval.tv_usec = vap->va_mtime.tv_nsec / 1000;
-           if (error = VOP_UPDATE(vp, &atimeval, &mtimeval, 1))
-               return (error);
+       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);
 
        /*
-        * Change the permissions.. must be root or owner to do this.
+        * Change the permissions.
         */
-       if (vap->va_mode != (u_short)VNOVAL) {
-               if ((cred->cr_uid != file_node->dn_uid)
-                && (error = suser(cred, &p->p_acflag)))
-                       return (error);
+       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);
 
        /*
-        * Change the owner.. must be root to do this.
+        * Change the owner.
         */
-       if (vap->va_uid != (uid_t)VNOVAL) {
-               if (error = suser(cred, &p->p_acflag))
-                       return (error);
+       if (VATTR_IS_ACTIVE(vap, va_uid)) {
                file_node->dn_uid = vap->va_uid;
        }
+       VATTR_SET_SUPPORTED(vap, va_uid);
 
        /*
-        * Change the group.. must be root or owner to do this.
-        * If we are the owner, we must be in the target group too.
-        * don't use suser() unless you have to as it reports
-        * whether you needed suser powers or not.
+        * Change the group.
         */
-       if (vap->va_gid != (gid_t)VNOVAL) {
-               if (cred->cr_uid == file_node->dn_uid){
-                       gp = cred->cr_groups;
-                       for (i = 0; i < cred->cr_ngroups; i++, gp++) {
-                               if (vap->va_gid == *gp)
-                                       goto cando; 
-                       }
-               }
-               /*
-                * we can't do it with normal privs,
-                * do we have an ace up our sleeve?
-                */
-               if (error = suser(cred, &p->p_acflag))
-                       return (error);
-cando:
+       if (VATTR_IS_ACTIVE(vap, va_gid)) {
                file_node->dn_gid = vap->va_gid;
        }
-#if 0
-       /*
-        * Copied from somewhere else
-        * but only kept as a marker and reminder of the fact that
-        * flags should be handled some day
-        */
-       if (vap->va_flags != VNOVAL) {
-               if (error = suser(cred, &p->p_acflag))
-                       return error;
-               if (cred->cr_uid == 0)
-               ;
-               else {
-               }
-       }
-#endif
+       VATTR_SET_SUPPORTED(vap, va_gid);
+exit:
+       DEVFS_UNLOCK();
+
        return error;
 }
 
+#if CONFIG_MACF
 static int
-devfs_read(struct vop_read_args *ap)
-        /*struct vop_read_args {
               struct vnode *a_vp;
               struct uio *a_uio;
               int  a_ioflag;
               struct ucred *a_cred;
-        } */
+devfs_setlabel(struct vnop_setlabel_args *ap)
+/* struct vnop_setlabel_args {
*               struct vnodeop_desc *a_desc;
*               struct vnode *a_vp;
*               struct label *a_vl;
*       vfs_context_t a_context;
*       } */
 {
-       devnode_t * dn_p = VTODN(ap->a_vp);
+       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)
+/* struct vnop_read_args {
+ *       struct vnode *a_vp;
+ *       struct uio *a_uio;
+ *       int  a_ioflag;
+ *       vfs_context_t a_context;
+ *  } */
+{
+       devnode_t * dn_p = VTODN(ap->a_vp);
 
        switch (ap->a_vp->v_type) {
-         case VDIR: {
-             dn_p->dn_flags |= DN_ACCESS;
-             return VOP_READDIR(ap->a_vp, ap->a_uio, ap->a_cred,
-                                NULL, NULL, NULL);
-         }
-         default: {
-             printf("devfs_read(): bad file type %d", ap->a_vp->v_type);
-             return(EINVAL);
-             break;
-         }
-       }
-       return (0); /* not reached */
+       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;
+       }
+       }
 }
 
 static int
-devfs_close(ap)
-       struct vop_close_args /* {
-               struct vnode *a_vp;
-               int  a_fflag;
-               struct ucred *a_cred;
-               struct proc *a_p;
-       } */ *ap;
+devfs_close(struct vnop_close_args *ap)
+/* struct vnop_close_args {
+ *       struct vnode *a_vp;
+ *       int  a_fflag;
+ *       vfs_context_t a_context;
+ *  } */
 {
-       struct vnode *          vp = ap->a_vp;
-       register devnode_t *    dnp = VTODN(vp);
-
-       simple_lock(&vp->v_interlock);
-       if (vp->v_usecount > 1)
-           dn_times(dnp, time, time);
-       simple_unlock(&vp->v_interlock);
-       return (0);
+       struct vnode *          vp = ap->a_vp;
+       devnode_t *     dnp;
+
+       if (vnode_isinuse(vp, 1)) {
+               DEVFS_LOCK();
+               dnp = VTODN(vp);
+               if (dnp) {
+                       dn_times_now(dnp, 0);
+               }
+               DEVFS_UNLOCK();
+       }
+       return 0;
 }
 
 static int
-devfsspec_close(ap)
-       struct vop_close_args /* {
-               struct vnode *a_vp;
-               int  a_fflag;
-               struct ucred *a_cred;
-               struct proc *a_p;
-       } */ *ap;
+devfsspec_close(struct vnop_close_args *ap)
+/* struct vnop_close_args {
+ *       struct vnode *a_vp;
+ *       int  a_fflag;
+ *       vfs_context_t a_context;
+ *  } */
 {
-       struct vnode *          vp = ap->a_vp;
-       register devnode_t *    dnp = VTODN(vp);
-
-       simple_lock(&vp->v_interlock);
-       if (vp->v_usecount > 1)
-           dn_times(dnp, time, time);
-       simple_unlock(&vp->v_interlock);
-       return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap));
+       struct vnode *          vp = ap->a_vp;
+       devnode_t *     dnp;
+
+       if (vnode_isinuse(vp, 0)) {
+               DEVFS_LOCK();
+               dnp = VTODN(vp);
+               if (dnp) {
+                       dn_times_now(dnp, 0);
+               }
+               DEVFS_UNLOCK();
+       }
+
+       return VOCALL(spec_vnodeop_p, VOFFSET(vnop_close), ap);
+}
+
+static boolean_t
+devfs_update_needed(long now_s, long last_s)
+{
+       if (now_s > last_s) {
+               if (now_s - last_s >= DEVFS_LAZY_UPDATE_SECONDS) {
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+/*
+ * Given a set of time updates required [to happen at some point], check
+ * either make those changes (and resolve other pending updates) or mark
+ * the devnode for a subsequent update.
+ */
+static void
+devfs_consider_time_update(devnode_t *dnp, uint32_t just_changed_flags)
+{
+       struct timeval          now;
+       long now_s;
+
+       microtime(&now);
+       now_s = now.tv_sec;
+
+       if (dnp->dn_change || (just_changed_flags & DEVFS_UPDATE_CHANGE)) {
+               if (devfs_update_needed(now_s, dnp->dn_ctime.tv_sec)) {
+                       dn_times_now(dnp, just_changed_flags);
+                       return;
+               }
+       }
+       if (dnp->dn_access || (just_changed_flags & DEVFS_UPDATE_ACCESS)) {
+               if (devfs_update_needed(now_s, dnp->dn_atime.tv_sec)) {
+                       dn_times_now(dnp, just_changed_flags);
+                       return;
+               }
+       }
+       if (dnp->dn_update || (just_changed_flags & DEVFS_UPDATE_MOD)) {
+               if (devfs_update_needed(now_s, dnp->dn_mtime.tv_sec)) {
+                       dn_times_now(dnp, just_changed_flags);
+                       return;
+               }
+       }
+
+       /* Not going to do anything now--mark for later update */
+       dn_mark_for_delayed_times_update(dnp, just_changed_flags);
+
+       return;
 }
 
 static int
-devfsspec_read(struct vop_read_args *ap)
-        /*struct vop_read_args {
               struct vnode *a_vp;
               struct uio *a_uio;
               int  a_ioflag;
               struct ucred *a_cred;
       } */
+devfsspec_read(struct vnop_read_args *ap)
+/* struct vnop_read_args {
*       struct vnode *a_vp;
*       struct uio *a_uio;
*       int  a_ioflag;
*       kauth_cred_t a_cred;
*  } */
 {
-    VTODN(ap->a_vp)->dn_flags |= DN_ACCESS;
-    return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap));
+       devnode_t *     dnp = VTODN(ap->a_vp);
+
+       devfs_consider_time_update(dnp, DEVFS_UPDATE_ACCESS);
+
+       return VOCALL(spec_vnodeop_p, VOFFSET(vnop_read), ap);
 }
 
 static int
-devfsspec_write(struct vop_write_args *ap)
-        /*struct vop_write_args  {
               struct vnode *a_vp;
               struct uio *a_uio;
               int  a_ioflag;
               struct ucred *a_cred;
       } */
+devfsspec_write(struct vnop_write_args *ap)
+/* struct vnop_write_args  {
*       struct vnode *a_vp;
*       struct uio *a_uio;
*       int  a_ioflag;
*       vfs_context_t a_context;
*  } */
 {
-    VTODN(ap->a_vp)->dn_flags |= DN_CHANGE | DN_UPDATE;
-    return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap));
+       devnode_t *     dnp = VTODN(ap->a_vp);
+
+       devfs_consider_time_update(dnp, DEVFS_UPDATE_CHANGE | DEVFS_UPDATE_MOD);
+
+       return VOCALL(spec_vnodeop_p, VOFFSET(vnop_write), ap);
 }
 
 /*
  *  Write data to a file or directory.
  */
 static int
-devfs_write(struct vop_write_args *ap)
-        /*struct vop_write_args  {
               struct vnode *a_vp;
               struct uio *a_uio;
               int  a_ioflag;
               struct ucred *a_cred;
       } */
+devfs_write(struct vnop_write_args *ap)
+/* struct vnop_write_args  {
*       struct vnode *a_vp;
*       struct uio *a_uio;
*       int  a_ioflag;
*       kauth_cred_t a_cred;
*  } */
 {
        switch (ap->a_vp->v_type) {
        case VDIR:
-               return(EISDIR);
+               return EISDIR;
        default:
                printf("devfs_write(): bad file type %d", ap->a_vp->v_type);
-               return (EINVAL);
+               return EINVAL;
        }
-       return 0; /* not reached */
 }
 
+/*
+ * Deviates from UFS naming convention because there is a KPI function
+ * called devfs_remove().
+ */
 static int
-devfs_remove(struct vop_remove_args *ap)
-        /*struct vop_remove_args  {
               struct vnode *a_dvp;
               struct vnode *a_vp;
               struct componentname *a_cnp;
-        } */ 
+devfs_vnop_remove(struct vnop_remove_args *ap)
+/* struct vnop_remove_args  {
*       struct vnode *a_dvp;
*       struct vnode *a_vp;
*       struct componentname *a_cnp;
+ *  } */
 {
        struct vnode *vp = ap->a_vp;
        struct vnode *dvp = ap->a_dvp;
@@ -711,29 +821,32 @@ devfs_remove(struct vop_remove_args *ap)
        devdirent_t * tnp;
        int doingdirectory = 0;
        int error = 0;
-       uid_t ouruid = cnp->cn_cred->cr_uid;
-       struct proc *p = cnp->cn_proc;
 
        /*
-        * Lock our directories and get our name pointers
-        * assume that the names are null terminated as they
+        * assume that the name is null terminated as they
         * are the end of the path. Get pointers to all our
         * devfs structures.
         */
+
+       DEVFS_LOCK();
+
        tp = VTODN(vp);
        tdp = VTODN(dvp);
-       /*
-        * Assuming we are atomic, dev_lookup left this for us
-        */
-       tnp = tp->dn_last_lookup;
+
+
+       tnp = dev_findname(tdp, cnp->cn_nameptr);
+
+       if (tnp == NULL) {
+               error = ENOENT;
+               goto abort;
+       }
 
        /*
-        * Check we are doing legal things WRT the new flags
+        * Don't allow removing critical devfs devices
         */
-       if ((tp->dn_flags & (IMMUTABLE | APPEND))
-         || (tdp->dn_flags & APPEND) /*XXX eh?*/ ) {
-           error = EPERM;
-           goto abort;
+       if (devfs_is_name_protected(dvp, cnp->cn_nameptr)) {
+               error = EINVAL;
+               goto abort;
        }
 
        /*
@@ -743,8 +856,8 @@ devfs_remove(struct vop_remove_args *ap)
                /*
                 * Avoid ".", "..", and aliases of "." for obvious reasons.
                 */
-               if ( (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') 
-                   || (cnp->cn_flags&ISDOTDOT) ) {
+               if ((cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')
+                   || (cnp->cn_flags & ISDOTDOT)) {
                        error = EINVAL;
                        goto abort;
                }
@@ -752,63 +865,44 @@ devfs_remove(struct vop_remove_args *ap)
        }
 
        /***********************************
-        * Start actually doing things.... *
-        ***********************************/
-       tdp->dn_flags |= DN_CHANGE | DN_UPDATE;
+       * Start actually doing things.... *
+       ***********************************/
+       devfs_consider_time_update(tdp, DEVFS_UPDATE_CHANGE | DEVFS_UPDATE_MOD);
 
-       /*
-        * own the parent directory, or the destination of the rename,
-        * otherwise the destination may not be changed (except by
-        * root). This implements append-only directories.
-        * XXX shoudn't this be in generic code? 
-        */
-       if ((tdp->dn_mode & S_ISTXT)
-         && ouruid != 0
-         && ouruid != tdp->dn_uid
-         && ouruid != tp->dn_uid ) {
-           error = EPERM;
-           goto abort;
-       }
        /*
         * Target must be empty if a directory and have no links
         * to it. Also, ensure source and target are compatible
         * (both directories, or both not directories).
         */
-       if (( doingdirectory) && (tp->dn_links > 2)) {
-           error = ENOTEMPTY;
-           goto abort;
+       if ((doingdirectory) && (tp->dn_links > 2)) {
+               error = ENOTEMPTY;
+               goto abort;
        }
-       DEVFS_LOCK(p);
        dev_free_name(tnp);
-       DEVFS_UNLOCK(p);
- abort:
-       if (dvp == vp)
-           vrele(vp);
-       else
-           vput(vp);
-       vput(dvp);
-       return (error);
+abort:
+       DEVFS_UNLOCK();
+
+       return error;
 }
 
 /*
  */
 static int
-devfs_link(struct vop_link_args *ap)
-        /*struct vop_link_args  {
-                struct vnode *a_tdvp;
-                struct vnode *a_vp;
-                struct componentname *a_cnp;
-        } */ 
+devfs_link(struct vnop_link_args *ap)
+/*struct vnop_link_args  {
+ *       struct vnode *a_tdvp;
+ *       struct vnode *a_vp;
+ *       struct componentname *a_cnp;
+ *       vfs_context_t a_context;
+ *  } */
 {
        struct vnode *vp = ap->a_vp;
        struct vnode *tdvp = ap->a_tdvp;
        struct componentname *cnp = ap->a_cnp;
-       struct proc *p = cnp->cn_proc;
        devnode_t * fp;
        devnode_t * tdp;
        devdirent_t * tnp;
        int error = 0;
-       struct timeval tv;
 
        /*
         * First catch an arbitrary restriction for this FS
@@ -824,80 +918,33 @@ devfs_link(struct vop_link_args *ap)
         * are the end of the path. Get pointers to all our
         * devfs structures.
         */
+       /* can lookup dnode safely for tdvp outside of devfs lock as it is not aliased */
        tdp = VTODN(tdvp);
-       fp = VTODN(vp);
-       
+
        if (tdvp->v_mount != vp->v_mount) {
-               error = EXDEV;
-               VOP_ABORTOP(tdvp, cnp); 
-               goto out2;
-       }
-       if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
-               VOP_ABORTOP(tdvp, cnp);
-               goto out2;
+               return EXDEV;
        }
+       DEVFS_LOCK();
 
-       /*
-        * Check we are doing legal things WRT the new flags
-        */
-       if (fp->dn_flags & (IMMUTABLE | APPEND)) {
-               VOP_ABORTOP(tdvp, cnp);
-               error = EPERM;
-               goto out1;
-       }
+       fp = VTODN(vp);
 
        /***********************************
-        * Start actually doing things.... *
-        ***********************************/
-       fp->dn_flags |= DN_CHANGE;
-       tv = time;
-       error = VOP_UPDATE(vp, &tv, &tv, 1);
+       * Start actually doing things.... *
+       ***********************************/
+       dn_times_now(fp, DEVFS_UPDATE_CHANGE);
+
        if (!error) {
-           DEVFS_LOCK(p);
-           error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp);
-           DEVFS_UNLOCK(p);
+               error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp);
        }
 out1:
-       if (tdvp != vp)
-               VOP_UNLOCK(vp, 0, p);
-out2:
-       vput(tdvp);
-       return (error);
-}
-
-/*
- * Check if source directory is in the path of the target directory.
- * Target is supplied locked, source is unlocked.
- * The target is always vput before returning.
- */
-int
-devfs_checkpath(source, target)
-       devnode_t *source, *target;
-{
-    int error = 0;
-    devnode_t * ntmp;
-    devnode_t * tmp;
-    struct vnode *vp;
-
-    vp = target->dn_vn;
-    tmp = target;
-
-    do {
-       if (tmp == source) {
-           error = EINVAL;
-           break;
-       }
-       ntmp = tmp;
-    } while ((tmp = tmp->dn_typeinfo.Dir.parent) != ntmp);
+       DEVFS_UNLOCK();
 
-    if (vp != NULL)
-       vput(vp);
-    return (error);
+       return error;
 }
 
 /*
  * Rename system call. Seems overly complicated to me...
- *     rename("foo", "bar");
+ *      rename("foo", "bar");
  * is essentially
  *     unlink("bar");
  *     link("foo", "bar");
@@ -923,15 +970,16 @@ devfs_checkpath(source, target)
  *    directory.
  */
 static int
-devfs_rename(struct vop_rename_args *ap)
-        /*struct vop_rename_args  {
-                struct vnode *a_fdvp; 
-                struct vnode *a_fvp;  
-                struct componentname *a_fcnp;
-                struct vnode *a_tdvp;
-                struct vnode *a_tvp;
-                struct componentname *a_tcnp;
-        } */
+devfs_rename(struct vnop_rename_args *ap)
+/*struct vnop_rename_args  {
+ *       struct vnode *a_fdvp;
+ *       struct vnode *a_fvp;
+ *       struct componentname *a_fcnp;
+ *       struct vnode *a_tdvp;
+ *       struct vnode *a_tvp;
+ *       struct componentname *a_tcnp;
+ *       vfs_context_t a_context;
+ *  } */
 {
        struct vnode *tvp = ap->a_tvp;
        struct vnode *tdvp = ap->a_tdvp;
@@ -939,23 +987,21 @@ devfs_rename(struct vop_rename_args *ap)
        struct vnode *fdvp = ap->a_fdvp;
        struct componentname *tcnp = ap->a_tcnp;
        struct componentname *fcnp = ap->a_fcnp;
-       struct proc *p = fcnp->cn_proc;
        devnode_t *fp, *fdp, *tp, *tdp;
-       devdirent_t *fnp,*tnp;
+       devdirent_t *fnp, *tnp;
        int doingdirectory = 0;
        int error = 0;
-       struct timeval tv;
 
+       DEVFS_LOCK();
        /*
         * First catch an arbitrary restriction for this FS
         */
-       if(tcnp->cn_namelen > DEVMAXNAMESIZE) {
+       if (tcnp->cn_namelen > DEVMAXNAMESIZE) {
                error = ENAMETOOLONG;
-               goto abortit;
+               goto out;
        }
 
        /*
-        * Lock our directories and get our name pointers
         * assume that the names are null terminated as they
         * are the end of the path. Get pointers to all our
         * devfs structures.
@@ -963,45 +1009,24 @@ devfs_rename(struct vop_rename_args *ap)
        tdp = VTODN(tdvp);
        fdp = VTODN(fdvp);
        fp = VTODN(fvp);
-       fnp = fp->dn_last_lookup;
+
+       fnp = dev_findname(fdp, fcnp->cn_nameptr);
+
+       if (fnp == NULL) {
+               error = ENOENT;
+               goto out;
+       }
        tp = NULL;
        tnp = NULL;
+
        if (tvp) {
-           tp = VTODN(tvp);
-           tnp = tp->dn_last_lookup;
-       }
-       
-       /*
-        * trying to move it out of devfs?
-         * if we move a dir across mnt points. we need to fix all
-        * the mountpoint pointers! XXX
-        * so for now keep dirs within the same mount
-        */
-       if ((fvp->v_mount != tdvp->v_mount) ||
-           (tvp && (fvp->v_mount != tvp->v_mount))) {
-               error = EXDEV;
-abortit:
-               VOP_ABORTOP(tdvp, tcnp); 
-               if (tdvp == tvp) /* eh? */
-                       vrele(tdvp);
-               else
-                       vput(tdvp);
-               if (tvp)
-                       vput(tvp);
-               VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
-               vrele(fdvp);
-               vrele(fvp);
-               return (error);
-       }
+               tnp = dev_findname(tdp, tcnp->cn_nameptr);
 
-       /*
-        * Check we are doing legal things WRT the new flags
-        */
-       if ((tp && (tp->dn_flags & (IMMUTABLE | APPEND)))
-         || (fp->dn_flags & (IMMUTABLE | APPEND))
-         || (fdp->dn_flags & APPEND)) {
-               error = EPERM;
-               goto abortit;
+               if (tnp == NULL) {
+                       error = ENOENT;
+                       goto out;
+               }
+               tp = VTODN(tvp);
        }
 
        /*
@@ -1011,31 +1036,39 @@ abortit:
                /*
                 * Avoid ".", "..", and aliases of "." for obvious reasons.
                 */
-               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 )) {
+               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 abortit;
+                       goto out;
                }
                doingdirectory++;
        }
 
+       /*
+        * Don't allow renaming critical devfs devices
+        */
+       if (devfs_is_name_protected(fdvp, fcnp->cn_nameptr) ||
+           devfs_is_name_protected(tdvp, tcnp->cn_nameptr)) {
+               error = EINVAL;
+               goto out;
+       }
+
        /*
         * If ".." must be changed (ie the directory gets a new
         * parent) then the source directory must not be in the
         * directory hierarchy above the target, as this would
         * orphan everything below the source directory. Also
         * the user must have write permission in the source so
-        * as to be able to change "..". 
+        * as to be able to change "..".
         */
        if (doingdirectory && (tdp != fdp)) {
                devnode_t * tmp, *ntmp;
-               error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
                tmp = tdp;
                do {
-                       if(tmp == fp) {
+                       if (tmp == fp) {
                                /* XXX unlock stuff here probably */
                                error = EINVAL;
                                goto out;
@@ -1045,38 +1078,24 @@ abortit:
        }
 
        /***********************************
-        * Start actually doing things.... *
-        ***********************************/
-       fp->dn_flags |= DN_CHANGE;
-       tv = time;
-       if (error = VOP_UPDATE(fvp, &tv, &tv, 1)) {
-           VOP_UNLOCK(fvp, 0, p);
-           goto bad;
-       }
+       * Start actually doing things.... *
+       ***********************************/
+       dn_times_now(fp, DEVFS_UPDATE_CHANGE);
+
        /*
         * Check if just deleting a link name.
         */
        if (fvp == tvp) {
                if (fvp->v_type == VDIR) {
                        error = EINVAL;
-                       goto abortit;
+                       goto out;
                }
-
                /* Release destination completely. */
-               VOP_ABORTOP(tdvp, tcnp);
-               vput(tdvp);
-               vput(tvp);
-
-               /* Delete source. */
-               VOP_ABORTOP(fdvp, fcnp); /*XXX*/
-               vrele(fdvp);
-               vrele(fvp);
                dev_free_name(fnp);
+
+               DEVFS_UNLOCK();
                return 0;
        }
-
-       vrele(fdvp);
-
        /*
         * 1) Bump link count while we're moving stuff
         *    around.  If we crash somewhere before
@@ -1086,230 +1105,280 @@ abortit:
        /*
         * If the target exists zap it (unless it's a non-empty directory)
         * We could do that as well but won't
-        */
+        */
        if (tp) {
-               int ouruid = tcnp->cn_cred->cr_uid;
-               /*
-                * If the parent directory is "sticky", then the user must
-                * own the parent directory, or the destination of the rename,
-                * otherwise the destination may not be changed (except by
-                * root). This implements append-only directories.
-                * XXX shoudn't this be in generic code? 
-                */
-               if ((tdp->dn_mode & S_ISTXT)
-                 && ouruid != 0
-                 && ouruid != tdp->dn_uid
-                 && ouruid != tp->dn_uid ) {
-                       error = EPERM;
-                       goto bad;
-               }
                /*
                 * Target must be empty if a directory and have no links
                 * to it. Also, ensure source and target are compatible
                 * (both directories, or both not directories).
                 */
-               if (( doingdirectory) && (tp->dn_links > 2)) {
-                               error = ENOTEMPTY;
-                               goto bad;
+               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);
+       dev_add_name(tcnp->cn_nameptr, tdp, NULL, fp, &tnp);
        fnp->de_dnp = NULL;
        fp->dn_links--; /* one less link to it.. */
-       dev_free_name(fnp);
-       fp->dn_links--; /* we added one earlier*/
-       if (tdp)
-               vput(tdvp);
-       if (tp)
-               vput(fvp);
-       vrele(fvp);
-       return (error);
 
+       dev_free_name(fnp);
 bad:
-       if (tp)
-               vput(tvp);
-       vput(tdvp);
+       fp->dn_links--; /* we added one earlier*/
 out:
-       if (vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p) == 0) {
-               fp->dn_links--; /* we added one earlier*/
-               vput(fvp);
-       } else
-               vrele(fvp);
-       return (error);
+       DEVFS_UNLOCK();
+       return error;
 }
 
 static int
-devfs_symlink(struct vop_symlink_args *ap)
-        /*struct vop_symlink_args {
               struct vnode *a_dvp;
               struct vnode **a_vpp;
               struct componentname *a_cnp;
               struct vattr *a_vap;
               char *a_target;
       } */
+devfs_mkdir(struct vnop_mkdir_args *ap)
+/*struct vnop_mkdir_args {
*       struct vnode *a_dvp;
*       struct vnode **a_vpp;
*       struct componentname *a_cnp;
*       struct vnode_attr *a_vap;
*       vfs_context_t a_context;
*  } */
 {
-       struct componentname * cnp = ap->a_cnp;
-       struct vnode *vp = NULL;
+       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 vattr *  vap = ap->a_vap;
+       struct vnode_attr *     vap = ap->a_vap;
        struct vnode * * vpp = ap->a_vpp;
-       struct proc *p = cnp->cn_proc;
-       struct timeval tv;
+
+       DEVFS_LOCK();
 
        dir_p = VTODN(ap->a_dvp);
-       typeinfo.Slnk.name = ap->a_target;
-       typeinfo.Slnk.namelen = strlen(ap->a_target);
-       DEVFS_LOCK(p);
-       error = dev_add_entry(cnp->cn_nameptr, dir_p, DEV_SLNK, 
-                             &typeinfo, NULL, NULL, &nm_p);
-       DEVFS_UNLOCK(p);
+       error = dev_add_entry(cnp->cn_nameptr, dir_p, DEV_DIR,
+           NULL, NULL, NULL, &nm_p);
        if (error) {
-           goto failure;
+               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);
-       if (error)
-           goto failure;
-       vp = *vpp;
-       vput(vp);
- failure:
-       if ((cnp->cn_flags & SAVESTART) == 0)
-           FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
-       vput(ap->a_dvp);
+failure:
+       DEVFS_UNLOCK();
+
        return error;
 }
 
 /*
- * Mknod vnode call
+ * An rmdir is a special type of remove, which we already support; we wrap
+ * and reexpress the arguments to call devfs_remove directly.  The only
+ * different argument is flags, which we do not set, since it's ignored.
  */
-/* ARGSUSED */
+static int
+devfs_rmdir(struct vnop_rmdir_args *ap)
+/* struct vnop_rmdir_args {
+ *       struct vnode *a_dvp;
+ *       struct vnode *a_vp;
+ *       struct componentname *a_cnp;
+ *       vfs_context_t a_context;
+ *  } */
+{
+       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;         /* XXX */
+       ra.a_context = ap->a_context;
+
+       return devfs_vnop_remove(&ra);
+}
+
+
+static int
+devfs_symlink(struct vnop_symlink_args *ap)
+/*struct vnop_symlink_args {
+ *       struct vnode *a_dvp;
+ *       struct vnode **a_vpp;
+ *       struct componentname *a_cnp;
+ *       struct vnode_attr *a_vap;
+ *       char *a_target;
+ *       vfs_context_t a_context;
+ *  } */
+{
+       int error;
+       devdirent_t *newent;
+
+       DEVFS_LOCK();
+       error = devfs_make_symlink(VTODN(ap->a_dvp), ap->a_cnp->cn_nameptr, ap->a_vap->va_mode, ap->a_target, &newent);
+
+       if (error == 0) {
+               error = devfs_dntovn(newent->de_dnp, ap->a_vpp, vfs_context_proc(ap->a_context));
+       }
+
+       DEVFS_UNLOCK();
+
+       return error;
+}
+
+/* Called with devfs locked */
 int
-devfs_mknod(ap)
-       struct vop_mknod_args /* {
-               struct vnode *a_dvp;
-               struct vnode **a_vpp;
-               struct componentname *a_cnp;
-               struct vattr *a_vap;
-       } */ *ap;
+devfs_make_symlink(devnode_t *dir_p, char *name, int mode, char *target, devdirent_t **newent)
 {
-       struct componentname * cnp = ap->a_cnp;
-       devnode_t *     dev_p;
-       devdirent_t *   devent;
-       devnode_t *     dir_p;  /* devnode for parent directory */
-       struct vnode *  dvp = ap->a_dvp;
-       int             error = 0;
-       devnode_type_t  typeinfo;
-       struct vattr *  vap = ap->a_vap;
+       int error = 0;
+       devnode_type_t typeinfo;
+       devdirent_t * nm_p;
+       devnode_t * dev_p;
+
+       typeinfo.Slnk.name = target;
+       typeinfo.Slnk.namelen = strlen(target);
+
+       error = dev_add_entry(name, 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 = mode;
+       dn_copy_times(dev_p, dir_p);
+
+       if (newent) {
+               *newent = nm_p;
+       }
+
+failure:
+
+       return error;
+}
+
+/*
+ * Mknod vnode call
+ */
+static int
+devfs_mknod(struct vnop_mknod_args *ap)
+/* struct vnop_mknod_args {
+ *       struct vnode *a_dvp;
+ *       struct vnode **a_vpp;
+ *       struct componentname *a_cnp;
+ *       struct vnode_attr *a_vap;
+ *       vfs_context_t a_context;
+ *  } */
+{
+       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;  /* devnode for parent directory */
+       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;
-       struct proc *   p = cnp->cn_proc;
 
        *vpp = NULL;
-       if (!vap->va_type == VBLK && !vap->va_type == VCHR) {
-           error = EINVAL; /* only support mknod of special files */
-           goto failure;
+       if (!(vap->va_type == VBLK) && !(vap->va_type == VCHR)) {
+               return EINVAL; /* only support mknod of special files */
        }
-       dir_p = VTODN(dvp);
        typeinfo.dev = vap->va_rdev;
-       DEVFS_LOCK(p);
-       error = dev_add_entry(cnp->cn_nameptr, dir_p, 
-                             (vap->va_type == VBLK) ? DEV_BDEV : DEV_CDEV,
-                             &typeinfo, NULL, NULL, &devent);
-       DEVFS_UNLOCK(p);
+
+       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;
+               goto failure;
        }
        dev_p = devent->de_dnp;
        error = devfs_dntovn(dev_p, vpp, p);
-       if (error)
-           goto failure;
-       dev_p->dn_uid = cnp->cn_cred->cr_uid;
-       dev_p->dn_gid = dir_p->dn_gid;
+       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;
- failure:
-       if (*vpp) {
-           vput(*vpp);
-           *vpp = 0;
-       }
-       if ((cnp->cn_flags & SAVESTART) == 0)
-           FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
-       vput(dvp);
-       return (error);
+       VATTR_SET_SUPPORTED(vap, va_uid);
+       VATTR_SET_SUPPORTED(vap, va_gid);
+       VATTR_SET_SUPPORTED(vap, va_mode);
+failure:
+       DEVFS_UNLOCK();
+
+       return error;
 }
 
 /*
  * Vnode op for readdir
  */
 static int
-devfs_readdir(struct vop_readdir_args *ap)
-        /*struct vop_readdir_args {
               struct vnode *a_vp;
               struct uio *a_uio;
               struct ucred *a_cred;
              int *eofflag;
              int *ncookies;
              u_int **cookies;
       } */
+devfs_readdir(struct vnop_readdir_args *ap)
+/*struct vnop_readdir_args {
*       struct vnode *a_vp;
*       struct uio *a_uio;
*       int a_flags;
*       int *a_eofflag;
*       int *a_numdirent;
*       vfs_context_t a_context;
*  } */
 {
        struct vnode *vp = ap->a_vp;
        struct uio *uio = ap->a_uio;
        struct dirent dirent;
        devnode_t * dir_node;
-       devdirent_t *   name_node;
-       char    *name;
+       devdirent_t *   name_node;
+       const char *name;
        int error = 0;
        int reclen;
        int nodenumber;
-       int     startpos,pos;
-       struct proc *   p = uio->uio_procp;
+       int     startpos, pos;
+
+       if (ap->a_flags & (VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF)) {
+               return EINVAL;
+       }
 
        /*  set up refs to dir */
        dir_node = VTODN(vp);
-       if(dir_node->dn_type != DEV_DIR)
-               return(ENOTDIR);
-
+       if (dir_node->dn_type != DEV_DIR) {
+               return ENOTDIR;
+       }
        pos = 0;
        startpos = uio->uio_offset;
-       DEVFS_LOCK(p);
+
+       DEVFS_LOCK();
+
        name_node = dir_node->dn_typeinfo.Dir.dirlist;
        nodenumber = 0;
-       dir_node->dn_flags |= DN_ACCESS;
-
-       while ((name_node || (nodenumber < 2)) && (uio->uio_resid > 0))
-       {
-               switch(nodenumber)
-               {
-               case    0:
-                       dirent.d_fileno = (int32_t)(void *)dir_node;
+
+       while ((name_node || (nodenumber < 2)) && (uio_resid(uio) > 0)) {
+               switch (nodenumber) {
+               case    0:
+                       dirent.d_fileno = dir_node->dn_ino;
                        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;
+               case    1:
+                       if (dir_node->dn_typeinfo.Dir.parent) {
+                               dirent.d_fileno = dir_node->dn_typeinfo.Dir.parent->dn_ino;
+                       } else {
+                               dirent.d_fileno = dir_node->dn_ino;
+                       }
                        name = "..";
                        dirent.d_namlen = 2;
                        dirent.d_type = DT_DIR;
                        break;
                default:
-                       dirent.d_fileno = (int32_t)(void *)name_node->de_dnp;
+                       dirent.d_fileno = name_node->de_dnp->dn_ino;
                        dirent.d_namlen = strlen(name_node->de_name);
                        name = name_node->de_name;
-                       switch(name_node->de_dnp->dn_type) {
+                       switch (name_node->de_dnp->dn_type) {
                        case DEV_BDEV:
                                dirent.d_type = DT_BLK;
                                break;
@@ -1326,41 +1395,45 @@ devfs_readdir(struct vop_readdir_args *ap)
                                dirent.d_type = DT_UNKNOWN;
                        }
                }
-#define        GENERIC_DIRSIZ(dp) \
+#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)     /* made it to the offset yet? */
-               {
-                       if (uio->uio_resid < reclen) /* will it fit? */
+               if (pos >= startpos) {   /* made it to the offset yet? */
+                       if (uio_resid(uio) < reclen) { /* will it fit? */
                                break;
-                       strcpy( dirent.d_name,name);
-                       if ((error = uiomove ((caddr_t)&dirent,
-                                       dirent.d_reclen, uio)) != 0)
+                       }
+                       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)
+               if ((nodenumber > 1) && name_node) {
                        name_node = name_node->de_next;
+               }
                nodenumber++;
        }
-       DEVFS_UNLOCK(p);
+       DEVFS_UNLOCK();
        uio->uio_offset = pos;
 
-       return (error);
+       devfs_consider_time_update(dir_node, DEVFS_UPDATE_ACCESS);
+
+       return error;
 }
 
 
 /*
  */
 static int
-devfs_readlink(struct vop_readlink_args *ap)
-        /*struct vop_readlink_args {
               struct vnode *a_vp;
               struct uio *a_uio;
               struct ucred *a_cred;
       } */
+devfs_readlink(struct vnop_readlink_args *ap)
+/*struct vnop_readlink_args {
*       struct vnode *a_vp;
*       struct uio *a_uio;
*       vfs_context_t a_context;
*  } */
 {
        struct vnode *vp = ap->a_vp;
        struct uio *uio = ap->a_uio;
@@ -1369,238 +1442,259 @@ devfs_readlink(struct vop_readlink_args *ap)
 
        /*  set up refs to dir */
        lnk_node = VTODN(vp);
-       if(lnk_node->dn_type != DEV_SLNK)
-               return(EINVAL);
-       if ((error = VOP_ACCESS(vp, VREAD, ap->a_cred, NULL)) != 0) { /* XXX */
-               return error;
+
+       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);
+       error = uiomove(lnk_node->dn_typeinfo.Slnk.name,
+           lnk_node->dn_typeinfo.Slnk.namelen, uio);
+out:
        return error;
 }
 
 static int
-devfs_abortop(struct vop_abortop_args *ap)
-        /*struct vop_abortop_args {
-                struct vnode *a_dvp;
-                struct componentname *a_cnp;
-        } */
+devfs_reclaim(struct vnop_reclaim_args *ap)
+/*struct vnop_reclaim_args {
+ *       struct vnode *a_vp;
+ *  } */
 {
-       if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
-               FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI);
-       }
-       return 0;
-}
+       struct vnode *      vp = ap->a_vp;
+       devnode_t *         dnp;
 
+       DEVFS_LOCK();
 
-static int
-devfs_reclaim(struct vop_reclaim_args *ap)
-        /*struct vop_reclaim_args {
-               struct vnode *a_vp;
-        } */
-{
-    struct vnode *     vp = ap->a_vp;
-    devnode_t *        dnp = VTODN(vp);
-    
-    if (dnp) {
-       /* 
-        * do the same as devfs_inactive in case it is not called
-        * before us (can that ever happen?)
-        */
-       dnp->dn_vn = NULL;
-       vp->v_data = NULL;
-       if (dnp->dn_delete) {
-           devnode_free(dnp);
+       dnp = VTODN(vp);
+
+       if (dnp) {
+               /* If this is a cloning device, it didn't have a dn_vn anyway */
+               dnp->dn_vn = NULL;
+               vnode_clearfsnode(vp);
+
+               /* This could delete the node, if we are the last vnode */
+               devfs_rele_node(dnp);
        }
-    }
-    return(0);
+       DEVFS_UNLOCK();
+
+       return 0;
 }
 
+
 /*
- * Print out the contents of a /devfs vnode.
+ * Get configurable pathname variables.
  */
 static int
-devfs_print(struct vop_print_args *ap)
-       /*struct vop_print_args {
-               struct vnode *a_vp;
-       } */
+devs_vnop_pathconf(
+       struct vnop_pathconf_args /* {
+                                   *  struct vnode *a_vp;
+                                   *  int a_name;
+                                   *  int *a_retval;
+                                   *  vfs_context_t a_context;
+                                   *  } */*ap)
 {
+       switch (ap->a_name) {
+       case _PC_LINK_MAX:
+               /* arbitrary limit matching HFS; devfs has no hard limit */
+               *ap->a_retval = 32767;
+               break;
+       case _PC_NAME_MAX:
+               *ap->a_retval = DEVMAXNAMESIZE - 1;     /* includes NUL */
+               break;
+       case _PC_PATH_MAX:
+               *ap->a_retval = DEVMAXPATHSIZE - 1;     /* XXX nonconformant */
+               break;
+       case _PC_CHOWN_RESTRICTED:
+               *ap->a_retval = 200112;         /* _POSIX_CHOWN_RESTRICTED */
+               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);
+       return 0;
 }
 
+
+
 /**************************************************************************\
 * pseudo ops *
 \**************************************************************************/
 
 /*
  *
- *     struct vop_inactive_args {
+ *     struct vnop_inactive_args {
  *             struct vnode *a_vp;
- *             struct proc *a_p;
- *     } 
+ *             vfs_context_t a_context;
+ *     }
  */
 
 static int
-devfs_inactive(struct vop_inactive_args *ap)
+devfs_inactive(__unused struct vnop_inactive_args *ap)
 {
-    struct vnode *     vp = ap->a_vp;
-    devnode_t *        dnp = VTODN(vp);
-    
-    if (dnp) {
-       dnp->dn_vn = NULL;
-       vp->v_data = NULL;
-       if (dnp->dn_delete) {
-           devnode_free(dnp);
-       }
-    }
-    VOP_UNLOCK(vp, 0, ap->a_p);
-    return (0);
+       vnode_t vp = ap->a_vp;
+       devnode_t *dnp = VTODN(vp);
+
+       /*
+        * Cloned vnodes are not linked in anywhere, so they
+        * can just be recycled.
+        */
+       if (dnp->dn_clone != NULL) {
+               vnode_recycle(vp);
+       }
+
+       return 0;
 }
 
-int
-devfs_update(ap)
-       struct vop_update_args /* {
-               struct vnode *a_vp;
-               struct timeval *a_access;
-               struct timeval *a_modify;
-               int a_waitfor;
-       } */ *ap;
+/*
+ * called with DEVFS_LOCK held
+ */
+static int
+devfs_update(struct vnode *vp, struct timeval *access, struct timeval *modify)
 {
-       register struct fs *fs;
-       int error;
        devnode_t * ip;
+       struct timeval now;
 
-       ip = VTODN(ap->a_vp);
-       if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) {
-               ip->dn_flags &=
-                   ~(DN_ACCESS | DN_CHANGE | DN_MODIFIED | DN_UPDATE);
-               return (0);
-       }
-       if ((ip->dn_flags &
-           (DN_ACCESS | DN_CHANGE | DN_MODIFIED | DN_UPDATE)) == 0)
-               return (0);
-       dn_times(ip, time, time);
-       return (0);
+       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;
+       }
+
+       DEVFS_ATTR_LOCK_SPIN();
+       microtime(&now);
+       dn_times_locked(ip, access, modify, &now, DEVFS_UPDATE_ACCESS | DEVFS_UPDATE_MOD);
+       DEVFS_ATTR_UNLOCK();
+
+       return 0;
 }
 
 #define VOPFUNC int (*)(void *)
 
 /* The following ops are used by directories and symlinks */
-int (**devfs_vnodeop_p)(void *);
-static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = {
-       { &vop_default_desc, (VOPFUNC)vn_default_error },
-       { &vop_lookup_desc, (VOPFUNC)devfs_lookup },            /* lookup */
-       { &vop_create_desc, (VOPFUNC)err_create },              /* create */
-       { &vop_whiteout_desc, (VOPFUNC)err_whiteout },          /* whiteout */
-       { &vop_mknod_desc, (VOPFUNC)devfs_mknod },              /* mknod */
-       { &vop_open_desc, (VOPFUNC)nop_open },                  /* open */
-       { &vop_close_desc, (VOPFUNC)devfs_close },              /* close */
-       { &vop_access_desc, (VOPFUNC)devfs_access },            /* access */
-       { &vop_getattr_desc, (VOPFUNC)devfs_getattr },          /* getattr */
-       { &vop_setattr_desc, (VOPFUNC)devfs_setattr },          /* setattr */
-       { &vop_read_desc, (VOPFUNC)devfs_read },                /* read */
-       { &vop_write_desc, (VOPFUNC)devfs_write },              /* write */
-       { &vop_lease_desc, (VOPFUNC)nop_lease },                /* lease */
-       { &vop_ioctl_desc, (VOPFUNC)err_ioctl },                /* ioctl */
-       { &vop_select_desc, (VOPFUNC)err_select },              /* select */
-       { &vop_revoke_desc, (VOPFUNC)err_revoke },              /* revoke */
-       { &vop_mmap_desc, (VOPFUNC)err_mmap },                  /* mmap */
-       { &vop_fsync_desc, (VOPFUNC)nop_fsync },                /* fsync */
-       { &vop_seek_desc, (VOPFUNC)err_seek },                  /* seek */
-       { &vop_remove_desc, (VOPFUNC)devfs_remove },            /* remove */
-       { &vop_link_desc, (VOPFUNC)devfs_link },                /* link */
-       { &vop_rename_desc, (VOPFUNC)devfs_rename },            /* rename */
-       { &vop_mkdir_desc, (VOPFUNC)err_mkdir },                /* mkdir */
-       { &vop_rmdir_desc, (VOPFUNC)err_rmdir },                /* rmdir */
-       { &vop_symlink_desc, (VOPFUNC)devfs_symlink },          /* symlink */
-       { &vop_readdir_desc, (VOPFUNC)devfs_readdir },          /* readdir */
-       { &vop_readlink_desc, (VOPFUNC)devfs_readlink },        /* readlink */
-       { &vop_abortop_desc, (VOPFUNC)devfs_abortop },          /* abortop */
-       { &vop_inactive_desc, (VOPFUNC)devfs_inactive },        /* inactive */
-       { &vop_reclaim_desc, (VOPFUNC)devfs_reclaim },          /* reclaim */
-       { &vop_lock_desc, (VOPFUNC)nop_lock },                  /* lock */
-       { &vop_unlock_desc, (VOPFUNC)nop_unlock },              /* unlock */
-       { &vop_bmap_desc, (VOPFUNC)err_bmap },                  /* bmap */
-       { &vop_strategy_desc, (VOPFUNC)err_strategy },          /* strategy */
-       { &vop_print_desc, (VOPFUNC)err_print },                /* print */
-       { &vop_islocked_desc, (VOPFUNC)nop_islocked },          /* islocked */
-       { &vop_pathconf_desc, (VOPFUNC)err_pathconf },          /* pathconf */
-       { &vop_advlock_desc, (VOPFUNC)err_advlock },            /* advlock */
-       { &vop_blkatoff_desc, (VOPFUNC)err_blkatoff },          /* blkatoff */
-       { &vop_valloc_desc, (VOPFUNC)err_valloc },              /* valloc */
-       { &vop_reallocblks_desc, (VOPFUNC)err_reallocblks },    /* reallocblks */
-       { &vop_vfree_desc, (VOPFUNC)err_vfree },                /* vfree */
-       { &vop_truncate_desc, (VOPFUNC)err_truncate },          /* truncate */
-       { &vop_update_desc, (VOPFUNC)devfs_update },            /* update */
-       { &vop_bwrite_desc, (VOPFUNC)err_bwrite },
-       { &vop_pagein_desc, (VOPFUNC)err_pagein },              /* Pagein */
-       { &vop_pageout_desc, (VOPFUNC)err_pageout },            /* Pageout */
-       { &vop_copyfile_desc, (VOPFUNC)err_copyfile },          /* Copyfile */
-       { &vop_blktooff_desc, (VOPFUNC)err_blktooff },          /* blktooff */
-       { &vop_offtoblk_desc, (VOPFUNC)err_offtoblk },          /* offtoblk */
-       { &vop_cmap_desc, (VOPFUNC)err_cmap },          /* cmap */
-       { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+int(**devfs_vnodeop_p)(void *);
+const static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = {
+       { .opve_op = &vnop_default_desc, .opve_impl = (VOPFUNC)vn_default_error },
+       { .opve_op = &vnop_lookup_desc, .opve_impl = (VOPFUNC)devfs_lookup },           /* lookup */
+       { .opve_op = &vnop_create_desc, .opve_impl = (VOPFUNC)err_create },             /* create */
+       { .opve_op = &vnop_whiteout_desc, .opve_impl = (VOPFUNC)err_whiteout },         /* whiteout */
+       { .opve_op = &vnop_mknod_desc, .opve_impl = (VOPFUNC)devfs_mknod },             /* mknod */
+       { .opve_op = &vnop_open_desc, .opve_impl = (VOPFUNC)nop_open },                 /* open */
+       { .opve_op = &vnop_close_desc, .opve_impl = (VOPFUNC)devfs_close },             /* close */
+       { .opve_op = &vnop_getattr_desc, .opve_impl = (VOPFUNC)devfs_getattr },         /* getattr */
+       { .opve_op = &vnop_setattr_desc, .opve_impl = (VOPFUNC)devfs_setattr },         /* setattr */
+       { .opve_op = &vnop_read_desc, .opve_impl = (VOPFUNC)devfs_read },               /* read */
+       { .opve_op = &vnop_write_desc, .opve_impl = (VOPFUNC)devfs_write },             /* write */
+       { .opve_op = &vnop_ioctl_desc, .opve_impl = (VOPFUNC)err_ioctl },               /* ioctl */
+       { .opve_op = &vnop_select_desc, .opve_impl = (VOPFUNC)err_select },             /* select */
+       { .opve_op = &vnop_revoke_desc, .opve_impl = (VOPFUNC)err_revoke },             /* revoke */
+       { .opve_op = &vnop_mmap_desc, .opve_impl = (VOPFUNC)err_mmap },                 /* mmap */
+       { .opve_op = &vnop_fsync_desc, .opve_impl = (VOPFUNC)nop_fsync },               /* fsync */
+       { .opve_op = &vnop_remove_desc, .opve_impl = (VOPFUNC)devfs_vnop_remove },      /* remove */
+       { .opve_op = &vnop_link_desc, .opve_impl = (VOPFUNC)devfs_link },               /* link */
+       { .opve_op = &vnop_rename_desc, .opve_impl = (VOPFUNC)devfs_rename },           /* rename */
+       { .opve_op = &vnop_mkdir_desc, .opve_impl = (VOPFUNC)devfs_mkdir },             /* mkdir */
+       { .opve_op = &vnop_rmdir_desc, .opve_impl = (VOPFUNC)devfs_rmdir },             /* rmdir */
+       { .opve_op = &vnop_symlink_desc, .opve_impl = (VOPFUNC)devfs_symlink },         /* symlink */
+       { .opve_op = &vnop_readdir_desc, .opve_impl = (VOPFUNC)devfs_readdir },         /* readdir */
+       { .opve_op = &vnop_readlink_desc, .opve_impl = (VOPFUNC)devfs_readlink },       /* readlink */
+       { .opve_op = &vnop_inactive_desc, .opve_impl = (VOPFUNC)devfs_inactive },       /* inactive */
+       { .opve_op = &vnop_reclaim_desc, .opve_impl = (VOPFUNC)devfs_reclaim },         /* reclaim */
+       { .opve_op = &vnop_strategy_desc, .opve_impl = (VOPFUNC)err_strategy },         /* strategy */
+       { .opve_op = &vnop_pathconf_desc, .opve_impl = (VOPFUNC)devs_vnop_pathconf },   /* pathconf */
+       { .opve_op = &vnop_advlock_desc, .opve_impl = (VOPFUNC)err_advlock },           /* advlock */
+       { .opve_op = &vnop_bwrite_desc, .opve_impl = (VOPFUNC)err_bwrite },
+       { .opve_op = &vnop_pagein_desc, .opve_impl = (VOPFUNC)err_pagein },             /* Pagein */
+       { .opve_op = &vnop_pageout_desc, .opve_impl = (VOPFUNC)err_pageout },           /* Pageout */
+       { .opve_op = &vnop_copyfile_desc, .opve_impl = (VOPFUNC)err_copyfile },         /* Copyfile */
+       { .opve_op = &vnop_blktooff_desc, .opve_impl = (VOPFUNC)err_blktooff },         /* blktooff */
+       { .opve_op = &vnop_offtoblk_desc, .opve_impl = (VOPFUNC)err_offtoblk },         /* offtoblk */
+       { .opve_op = &vnop_blockmap_desc, .opve_impl = (VOPFUNC)err_blockmap },         /* blockmap */
+#if CONFIG_MACF
+       { .opve_op = &vnop_setlabel_desc, .opve_impl = (VOPFUNC)devfs_setlabel },       /* setlabel */
+#endif
+       { .opve_op = (struct vnodeop_desc*)NULL, .opve_impl = (int (*)(void *))NULL }
 };
-struct vnodeopv_desc devfs_vnodeop_opv_desc =
-       { &devfs_vnodeop_p, devfs_vnodeop_entries };
+const struct vnodeopv_desc devfs_vnodeop_opv_desc =
+{ .opv_desc_vector_p = &devfs_vnodeop_p, .opv_desc_ops = devfs_vnodeop_entries };
 
 /* The following ops are used by the device nodes */
-int (**devfs_spec_vnodeop_p)(void *);
-static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries[] = {
-       { &vop_default_desc, (VOPFUNC)vn_default_error },
-       { &vop_lookup_desc, (VOPFUNC)spec_lookup },             /* lookup */
-       { &vop_create_desc, (VOPFUNC)spec_create },             /* create */
-       { &vop_mknod_desc, (VOPFUNC)spec_mknod },               /* mknod */
-       { &vop_open_desc, (VOPFUNC)spec_open },                 /* open */
-       { &vop_close_desc, (VOPFUNC)devfsspec_close },          /* close */
-       { &vop_access_desc, (VOPFUNC)devfs_access },            /* access */
-       { &vop_getattr_desc, (VOPFUNC)devfs_getattr },          /* getattr */
-       { &vop_setattr_desc, (VOPFUNC)devfs_setattr },          /* setattr */
-       { &vop_read_desc, (VOPFUNC)devfsspec_read },            /* read */
-       { &vop_write_desc, (VOPFUNC)devfsspec_write },          /* write */
-       { &vop_lease_desc, (VOPFUNC)spec_lease_check },         /* lease */
-       { &vop_ioctl_desc, (VOPFUNC)spec_ioctl },               /* ioctl */
-       { &vop_select_desc, (VOPFUNC)spec_select },             /* select */
-       { &vop_revoke_desc, (VOPFUNC)spec_revoke },             /* revoke */
-       { &vop_mmap_desc, (VOPFUNC)spec_mmap },                 /* mmap */
-       { &vop_fsync_desc, (VOPFUNC)spec_fsync },               /* fsync */
-       { &vop_seek_desc, (VOPFUNC)spec_seek },                 /* seek */
-       { &vop_remove_desc, (VOPFUNC)devfs_remove },            /* remove */
-       { &vop_link_desc, (VOPFUNC)devfs_link },                /* link */
-       { &vop_rename_desc, (VOPFUNC)spec_rename },             /* rename */
-       { &vop_mkdir_desc, (VOPFUNC)spec_mkdir },               /* mkdir */
-       { &vop_rmdir_desc, (VOPFUNC)spec_rmdir },               /* rmdir */
-       { &vop_symlink_desc, (VOPFUNC)spec_symlink },           /* symlink */
-       { &vop_readdir_desc, (VOPFUNC)spec_readdir },           /* readdir */
-       { &vop_readlink_desc, (VOPFUNC)spec_readlink },         /* readlink */
-       { &vop_abortop_desc, (VOPFUNC)spec_abortop },           /* abortop */
-       { &vop_inactive_desc, (VOPFUNC)devfs_inactive },        /* inactive */
-       { &vop_reclaim_desc, (VOPFUNC)devfs_reclaim },          /* reclaim */
-       { &vop_lock_desc, (VOPFUNC)nop_lock },                  /* lock */
-       { &vop_unlock_desc, (VOPFUNC)nop_unlock },              /* unlock */
-       { &vop_bmap_desc, (VOPFUNC)spec_bmap },                 /* bmap */
-       { &vop_strategy_desc, (VOPFUNC)spec_strategy },         /* strategy */
-       { &vop_print_desc, (VOPFUNC)devfs_print },              /* print */
-       { &vop_islocked_desc, (VOPFUNC)nop_islocked },          /* islocked */
-       { &vop_pathconf_desc, (VOPFUNC)spec_pathconf },         /* pathconf */
-       { &vop_advlock_desc, (VOPFUNC)spec_advlock },           /* advlock */
-       { &vop_blkatoff_desc, (VOPFUNC)spec_blkatoff },         /* blkatoff */
-       { &vop_valloc_desc, (VOPFUNC)spec_valloc },             /* valloc */
-       { &vop_reallocblks_desc, (VOPFUNC)spec_reallocblks },   /* reallocblks */
-       { &vop_vfree_desc, (VOPFUNC)nop_vfree },                /* vfree */
-       { &vop_truncate_desc, (VOPFUNC)spec_truncate },         /* truncate */
-       { &vop_update_desc, (VOPFUNC)devfs_update },            /* update */
-       { &vop_bwrite_desc, (VOPFUNC)vn_bwrite },
-       { &vop_devblocksize_desc, (VOPFUNC)spec_devblocksize }, /* devblocksize */
-       { &vop_pagein_desc, (VOPFUNC)err_pagein },              /* Pagein */
-       { &vop_pageout_desc, (VOPFUNC)err_pageout },            /* Pageout */
-       { &vop_copyfile_desc, (VOPFUNC)err_copyfile },          /* Copyfile */
-       { &vop_blktooff_desc, (VOPFUNC)spec_blktooff }, /* blktooff */
-       { &vop_blktooff_desc, (VOPFUNC)spec_offtoblk  },        /* blkofftoblk */
-       { &vop_cmap_desc, (VOPFUNC)spec_cmap }, /* cmap */
-       { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+int(**devfs_spec_vnodeop_p)(void *);
+const static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries[] = {
+       { .opve_op = &vnop_default_desc, .opve_impl = (VOPFUNC)vn_default_error },
+       { .opve_op = &vnop_lookup_desc, .opve_impl = (VOPFUNC)spec_lookup },            /* lookup */
+       { .opve_op = &vnop_create_desc, .opve_impl = (VOPFUNC)spec_create },            /* create */
+       { .opve_op = &vnop_mknod_desc, .opve_impl = (VOPFUNC)spec_mknod },              /* mknod */
+       { .opve_op = &vnop_open_desc, .opve_impl = (VOPFUNC)spec_open },                        /* open */
+       { .opve_op = &vnop_close_desc, .opve_impl = (VOPFUNC)devfsspec_close },         /* close */
+       { .opve_op = &vnop_getattr_desc, .opve_impl = (VOPFUNC)devfs_getattr },         /* getattr */
+       { .opve_op = &vnop_setattr_desc, .opve_impl = (VOPFUNC)devfs_setattr },         /* setattr */
+       { .opve_op = &vnop_read_desc, .opve_impl = (VOPFUNC)devfsspec_read },           /* read */
+       { .opve_op = &vnop_write_desc, .opve_impl = (VOPFUNC)devfsspec_write },         /* write */
+       { .opve_op = &vnop_ioctl_desc, .opve_impl = (VOPFUNC)spec_ioctl },              /* ioctl */
+       { .opve_op = &vnop_select_desc, .opve_impl = (VOPFUNC)spec_select },            /* select */
+       { .opve_op = &vnop_revoke_desc, .opve_impl = (VOPFUNC)spec_revoke },            /* revoke */
+       { .opve_op = &vnop_mmap_desc, .opve_impl = (VOPFUNC)spec_mmap },                        /* mmap */
+       { .opve_op = &vnop_fsync_desc, .opve_impl = (VOPFUNC)spec_fsync },              /* fsync */
+       { .opve_op = &vnop_remove_desc, .opve_impl = (VOPFUNC)devfs_vnop_remove },      /* remove */
+       { .opve_op = &vnop_link_desc, .opve_impl = (VOPFUNC)devfs_link },               /* link */
+       { .opve_op = &vnop_rename_desc, .opve_impl = (VOPFUNC)spec_rename },            /* rename */
+       { .opve_op = &vnop_mkdir_desc, .opve_impl = (VOPFUNC)spec_mkdir },              /* mkdir */
+       { .opve_op = &vnop_rmdir_desc, .opve_impl = (VOPFUNC)spec_rmdir },              /* rmdir */
+       { .opve_op = &vnop_symlink_desc, .opve_impl = (VOPFUNC)spec_symlink },          /* symlink */
+       { .opve_op = &vnop_readdir_desc, .opve_impl = (VOPFUNC)spec_readdir },          /* readdir */
+       { .opve_op = &vnop_readlink_desc, .opve_impl = (VOPFUNC)spec_readlink },                /* readlink */
+       { .opve_op = &vnop_inactive_desc, .opve_impl = (VOPFUNC)devfs_inactive },       /* inactive */
+       { .opve_op = &vnop_reclaim_desc, .opve_impl = (VOPFUNC)devfs_reclaim },         /* reclaim */
+       { .opve_op = &vnop_strategy_desc, .opve_impl = (VOPFUNC)spec_strategy },                /* strategy */
+       { .opve_op = &vnop_pathconf_desc, .opve_impl = (VOPFUNC)spec_pathconf },                /* pathconf */
+       { .opve_op = &vnop_advlock_desc, .opve_impl = (VOPFUNC)spec_advlock },          /* advlock */
+       { .opve_op = &vnop_bwrite_desc, .opve_impl = (VOPFUNC)vn_bwrite },
+       { .opve_op = &vnop_pagein_desc, .opve_impl = (VOPFUNC)err_pagein },             /* Pagein */
+       { .opve_op = &vnop_pageout_desc, .opve_impl = (VOPFUNC)err_pageout },           /* Pageout */
+       { .opve_op = &vnop_copyfile_desc, .opve_impl = (VOPFUNC)err_copyfile },         /* Copyfile */
+       { .opve_op = &vnop_blktooff_desc, .opve_impl = (VOPFUNC)spec_blktooff },        /* blktooff */
+       { .opve_op = &vnop_blktooff_desc, .opve_impl = (VOPFUNC)spec_offtoblk  },       /* blkofftoblk */
+       { .opve_op = &vnop_blockmap_desc, .opve_impl = (VOPFUNC)spec_blockmap },        /* blockmap */
+#if CONFIG_MACF
+       { .opve_op = &vnop_setlabel_desc, .opve_impl = (VOPFUNC)devfs_setlabel },       /* setlabel */
+#endif
+       { .opve_op = (struct vnodeop_desc*)NULL, .opve_impl = (int (*)(void *))NULL }
 };
-struct vnodeopv_desc devfs_spec_vnodeop_opv_desc =
-       { &devfs_spec_vnodeop_p, devfs_spec_vnodeop_entries };
-
+const struct vnodeopv_desc devfs_spec_vnodeop_opv_desc =
+{ .opv_desc_vector_p = &devfs_spec_vnodeop_p, .opv_desc_ops = devfs_spec_vnodeop_entries };
+
+
+#if FDESC
+int(**devfs_devfd_vnodeop_p)(void*);
+const static struct vnodeopv_entry_desc devfs_devfd_vnodeop_entries[] = {
+       { .opve_op = &vnop_default_desc, .opve_impl = (VOPFUNC)vn_default_error },
+       { .opve_op = &vnop_lookup_desc, .opve_impl = (VOPFUNC)devfs_devfd_lookup},      /* lookup */
+       { .opve_op = &vnop_open_desc, .opve_impl = (VOPFUNC)nop_open },                 /* open */
+       { .opve_op = &vnop_close_desc, .opve_impl = (VOPFUNC)devfs_close },             /* close */
+       { .opve_op = &vnop_getattr_desc, .opve_impl = (VOPFUNC)devfs_getattr },         /* getattr */
+       { .opve_op = &vnop_setattr_desc, .opve_impl = (VOPFUNC)devfs_setattr },         /* setattr */
+       { .opve_op = &vnop_revoke_desc, .opve_impl = (VOPFUNC)err_revoke },             /* revoke */
+       { .opve_op = &vnop_fsync_desc, .opve_impl = (VOPFUNC)nop_fsync },               /* fsync */
+       { .opve_op = &vnop_readdir_desc, .opve_impl = (VOPFUNC)devfs_devfd_readdir},            /* readdir */
+       { .opve_op = &vnop_inactive_desc, .opve_impl = (VOPFUNC)devfs_inactive },       /* inactive */
+       { .opve_op = &vnop_reclaim_desc, .opve_impl = (VOPFUNC)devfs_reclaim },         /* reclaim */
+       { .opve_op = &vnop_pathconf_desc, .opve_impl = (VOPFUNC)devs_vnop_pathconf },   /* pathconf */
+#if CONFIG_MACF
+       { .opve_op = &vnop_setlabel_desc, .opve_impl = (VOPFUNC)devfs_setlabel },       /* setlabel */
+#endif
+       { .opve_op = (struct vnodeop_desc*)NULL, .opve_impl = (int (*)(void *))NULL }
+};
+const struct vnodeopv_desc devfs_devfd_vnodeop_opv_desc =
+{ .opv_desc_vector_p = &devfs_devfd_vnodeop_p, .opv_desc_ops = devfs_devfd_vnodeop_entries};
+#endif /* FDESC */