]> 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 8bb4cbbde21e928d497d4089f6d4513613a1b297..b9de4b101f06b01a7f7cf33f0a5f6c2d161d495e 100644 (file)
@@ -1,36 +1,34 @@
 /*
- * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
  *
- * @APPLE_LICENSE_OSREFERENCE_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.
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
- * Please obtain a copy of the License at 
- * http://www.opensource.apple.com/apsl/ and read it before using this 
- * file.
+ * 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.
  *
- * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
- * Please see the License for the specific language governing rights and 
+ * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
  * limitations under the License.
  *
- * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
+ * @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:
@@ -39,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
@@ -51,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/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;
+       }
 
-static int devfs_update(struct vnode *vp, struct timeval *access,
-                        struct timeval *modify);
+       return FALSE;
+}
 
 
 /*
@@ -140,12 +251,12 @@ static int devfs_update(struct vnode *vp, struct timeval *access,
  */
 static int
 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 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;
@@ -157,23 +268,22 @@ devfs_lookup(struct vnop_lookup_args *ap)
        devdirent_t * nodename;
        int flags = cnp->cn_flags;
        int op = cnp->cn_nameiop;       /* LOOKUP, CREATE, RENAME, or DELETE */
-       int wantparent = flags & (LOCKPARENT|WANTPARENT);
+       int wantparent = flags & (LOCKPARENT | WANTPARENT);
        int error = 0;
-       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);
 
        /*
         * Make sure that our node is a directory as well.
         */
        if (dir_node->dn_type != DEV_DIR) {
-               return (ENOTDIR);
+               return ENOTDIR;
        }
 
        DEVFS_LOCK();
@@ -190,10 +300,10 @@ retry:
        cnp->cn_nameptr[cnp->cn_namelen] = heldchar;
 
        if (nodename) {
-               /* entry exists */
-               node = nodename->de_dnp;
+               /* entry exists */
+               node = nodename->de_dnp;
 
-               /* Do potential vnode allocation here inside the lock 
+               /* 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()).
@@ -203,8 +313,9 @@ retry:
        DEVFS_UNLOCK();
 
        if (error) {
-               if (error == EAGAIN)
-                       goto retry;
+               if (error == EAGAIN) {
+                       goto retry;
+               }
                return error;
        }
        if (!nodename) {
@@ -217,7 +328,7 @@ retry:
                 * or we're at the last component, but we're not creating
                 * or renaming, return ENOENT.
                 */
-               if (!(flags & ISLASTCN) || !(op == CREATE || op == RENAME)) {
+               if (!(flags & ISLASTCN) || !(op == CREATE || op == RENAME)) {
                        return ENOENT;
                }
                /*
@@ -231,7 +342,7 @@ retry:
                 * NB - if the directory is unlocked, then this
                 * information cannot be used.
                 */
-               return (EJUSTRETURN);
+               return EJUSTRETURN;
        }
        /*
         * from this point forward, we need to vnode_put the reference
@@ -246,21 +357,20 @@ retry:
         * on and lock the node, being careful with ".".
         */
        if (op == DELETE && (flags & ISLASTCN)) {
-
                /*
                 * we are trying to delete '.'.  What does this mean? XXX
                 */
                if (dir_node == node) {
-                       if (*result_vnode) {
-                               vnode_put(*result_vnode);
-                               *result_vnode = NULL;
-                       }                               
-                       if ( ((error = vnode_get(dir_vnode)) == 0) ) {
-                               *result_vnode = dir_vnode;
+                       if (*result_vnode) {
+                               vnode_put(*result_vnode);
+                               *result_vnode = NULL;
                        }
-                       return (error);
+                       if (((error = vnode_get(dir_vnode)) == 0)) {
+                               *result_vnode = dir_vnode;
+                       }
+                       return error;
                }
-               return (0);
+               return 0;
        }
 
        /*
@@ -270,16 +380,15 @@ retry:
         * regular file, or empty directory.
         */
        if (op == RENAME && wantparent && (flags & ISLASTCN)) {
-
                /*
                 * Careful about locking second node.
                 * This can only occur if the target is ".".
                 */
                if (dir_node == node) {
-                       error = EISDIR;
+                       error = EISDIR;
                        goto drop_ref;
                }
-               return (0);
+               return 0;
        }
 
        /*
@@ -303,101 +412,124 @@ retry:
         * that point backwards in the directory structure.
         */
        if ((flags & ISDOTDOT) == 0 && dir_node == node) {
-               if (*result_vnode) {
-                       vnode_put(*result_vnode);
-                       *result_vnode = NULL;
+               if (*result_vnode) {
+                       vnode_put(*result_vnode);
+                       *result_vnode = NULL;
                }
-               if ( (error = vnode_get(dir_vnode)) ) {
-                       return (error);
+               if ((error = vnode_get(dir_vnode))) {
+                       return error;
                }
                *result_vnode = dir_vnode;
        }
-       return (0);
+       return 0;
 
 drop_ref:
        if (*result_vnode) {
-               vnode_put(*result_vnode);
+               vnode_put(*result_vnode);
                *result_vnode = NULL;
        }
-       return (error);
+       return error;
 }
 
 static int
 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;
-        } */ 
+/*struct vnop_getattr_args {
*       struct vnode *a_vp;
*       struct vnode_attr *a_vap;
*       kauth_cred_t a_cred;
*       struct proc *a_p;
+ *  } */
 {
        struct vnode *vp = ap->a_vp;
        struct vnode_attr *vap = ap->a_vap;
-       devnode_t *     file_node;
+       devnode_t *     file_node;
        struct timeval now;
 
-       file_node = VTODN(vp);
 
        DEVFS_LOCK();
-
-       microtime(&now);
-       dn_times(file_node, &now, &now, &now);
+       file_node = VTODN(vp);
 
        VATTR_RETURN(vap, va_mode, file_node->dn_mode);
 
-       switch (file_node->dn_type)
-       {
-       case    DEV_DIR:
-               VATTR_RETURN(vap, va_rdev,  (dev_t)file_node->dn_dvm);
+       /*
+        * 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.
+        */
+       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:
-               VATTR_RETURN(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:
-               VATTR_RETURN(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 */
+               VATTR_RETURN(vap, va_rdev, 0);  /* default value only */
        }
        VATTR_RETURN(vap, va_type, vp->v_type);
        VATTR_RETURN(vap, va_nlink, file_node->dn_links);
        VATTR_RETURN(vap, va_uid, file_node->dn_uid);
        VATTR_RETURN(vap, va_gid, file_node->dn_gid);
        VATTR_RETURN(vap, va_fsid, (uintptr_t)file_node->dn_dvm);
-       VATTR_RETURN(vap, va_fileid, (uintptr_t)file_node);
+       VATTR_RETURN(vap, va_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)
+       if (vp->v_type == VBLK) {
                VATTR_RETURN(vap, va_iosize, BLKDEV_IOSIZE);
-       else if (vp->v_type == VCHR)
+       } else if (vp->v_type == VCHR) {
                VATTR_RETURN(vap, va_iosize, MAXPHYSIO);
-       else
+       } 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_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;
+       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_flags, 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;
@@ -405,31 +537,28 @@ devfs_getattr(struct vnop_getattr_args *ap)
 
 static int
 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 vnop_setattr_args  {
*  struct vnode *a_vp;
*  struct vnode_attr *a_vap;
*  vfs_context_t a_context;
+ *  } */
 {
-       struct vnode *vp = ap->a_vp;
-       struct vnode_attr *vap = ap->a_vap;
-       kauth_cred_t cred = vfs_context_ucred(ap->a_context);
-       struct proc *p = vfs_context_proc(ap->a_context);
-       int error = 0;
-       devnode_t *     file_node;
-       struct timeval atimeval, mtimeval;
-  
-       file_node = VTODN(vp);
-  
-       DEVFS_LOCK();
-       /*
-        * Go through the fields and update if set.
-        */
-       if (VATTR_IS_ACTIVE(vap, va_access_time) || VATTR_IS_ACTIVE(vap, va_modify_time)) {
-  
-  
-               if (VATTR_IS_ACTIVE(vap, va_access_time))
+       struct vnode *vp = ap->a_vp;
+       struct vnode_attr *vap = ap->a_vap;
+       int error = 0;
+       devnode_t *     file_node;
+       struct timeval atimeval, mtimeval;
+
+       DEVFS_LOCK();
+
+       file_node = VTODN(vp);
+       /*
+        * Go through the fields and update if set.
+        */
+       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;
@@ -438,140 +567,217 @@ devfs_setattr(struct vnop_setattr_args *ap)
                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)) )
+
+               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.
-        */
-       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.
-        */
-       if (VATTR_IS_ACTIVE(vap, va_uid))
-               file_node->dn_uid = vap->va_uid;
-       VATTR_SET_SUPPORTED(vap, va_uid);
-  
-       /*
-        * Change the group.
-        */
-       if (VATTR_IS_ACTIVE(vap, va_gid))
-               file_node->dn_gid = vap->va_gid;
-       VATTR_SET_SUPPORTED(vap, va_gid);
-       exit:
+               }
+       }
+       VATTR_SET_SUPPORTED(vap, va_access_time);
+       VATTR_SET_SUPPORTED(vap, va_change_time);
+
+       /*
+        * Change the permissions.
+        */
+       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.
+        */
+       if (VATTR_IS_ACTIVE(vap, va_uid)) {
+               file_node->dn_uid = vap->va_uid;
+       }
+       VATTR_SET_SUPPORTED(vap, va_uid);
+
+       /*
+        * Change the group.
+        */
+       if (VATTR_IS_ACTIVE(vap, va_gid)) {
+               file_node->dn_gid = vap->va_gid;
+       }
+       VATTR_SET_SUPPORTED(vap, va_gid);
+exit:
        DEVFS_UNLOCK();
 
        return error;
 }
 
+#if CONFIG_MACF
+static int
+devfs_setlabel(struct vnop_setlabel_args *ap)
+/* struct vnop_setlabel_args {
+ *               struct vnodeop_desc *a_desc;
+ *               struct vnode *a_vp;
+ *               struct label *a_vl;
+ *       vfs_context_t a_context;
+ *       } */
+{
+       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;
       } */
+/* 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);
+       devnode_t * dn_p = VTODN(ap->a_vp);
 
        switch (ap->a_vp->v_type) {
-         case VDIR: {
-             dn_p->dn_access = 1;
-
-             return VNOP_READDIR(ap->a_vp, ap->a_uio, 0, NULL, NULL, ap->a_context);
-         }
-         default: {
-             printf("devfs_read(): bad file type %d", ap->a_vp->v_type);
-             return(EINVAL);
-             break;
-         }
-       }
-       return (0); /* 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(struct vnop_close_args *ap)
-        /* struct vnop_close_args {
              struct vnode *a_vp;
              int  a_fflag;
              vfs_context_t a_context;
      } */
+/* 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);
-       struct timeval now;
+       struct vnode *          vp = ap->a_vp;
+       devnode_t *     dnp;
 
        if (vnode_isinuse(vp, 1)) {
-           DEVFS_LOCK();
-           microtime(&now);
-           dn_times(dnp, &now, &now, &now);
-           DEVFS_UNLOCK();
+               DEVFS_LOCK();
+               dnp = VTODN(vp);
+               if (dnp) {
+                       dn_times_now(dnp, 0);
+               }
+               DEVFS_UNLOCK();
        }
-       return (0);
+       return 0;
 }
 
 static int
 devfsspec_close(struct vnop_close_args *ap)
-        /* struct vnop_close_args {
              struct vnode *a_vp;
              int  a_fflag;
              vfs_context_t a_context;
      } */
+/* 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);
-       struct timeval now;
+       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();
+       }
 
-       if (vnode_isinuse(vp, 1)) {
-           DEVFS_LOCK();
-           microtime(&now);
-           dn_times(dnp, &now, &now, &now);
-           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 (VOCALL (spec_vnodeop_p, VOFFSET(vnop_close), ap));
+
+       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 vnop_read_args *ap)
-        /* struct vnop_read_args {
               struct vnode *a_vp;
               struct uio *a_uio;
               int  a_ioflag;
               kauth_cred_t a_cred;
       } */
+/* struct vnop_read_args {
*       struct vnode *a_vp;
*       struct uio *a_uio;
*       int  a_ioflag;
*       kauth_cred_t a_cred;
*  } */
 {
-       register devnode_t *    dnp = VTODN(ap->a_vp);
+       devnode_t *     dnp = VTODN(ap->a_vp);
 
-       dnp->dn_access = 1;
+       devfs_consider_time_update(dnp, DEVFS_UPDATE_ACCESS);
 
-       return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_read), ap));
+       return VOCALL(spec_vnodeop_p, VOFFSET(vnop_read), ap);
 }
 
 static int
 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;
       } */
+/* struct vnop_write_args  {
*       struct vnode *a_vp;
*       struct uio *a_uio;
*       int  a_ioflag;
*       vfs_context_t a_context;
*  } */
 {
-       register devnode_t *    dnp = VTODN(ap->a_vp);
+       devnode_t *     dnp = VTODN(ap->a_vp);
 
-       dnp->dn_change = 1;
-       dnp->dn_update = 1;
+       devfs_consider_time_update(dnp, DEVFS_UPDATE_CHANGE | DEVFS_UPDATE_MOD);
 
-       return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_write), ap));
+       return VOCALL(spec_vnodeop_p, VOFFSET(vnop_write), ap);
 }
 
 /*
@@ -579,56 +785,67 @@ devfsspec_write(struct vnop_write_args *ap)
  */
 static int
 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;
       } */
+/* 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 vnop_remove_args *ap)
-        /* struct vnop_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;
        struct componentname *cnp = ap->a_cnp;
-       vfs_context_t ctx = cnp->cn_context;
        devnode_t *  tp;
        devnode_t *  tdp;
        devdirent_t * tnp;
        int doingdirectory = 0;
        int error = 0;
-       uid_t ouruid = kauth_cred_getuid(vfs_context_ucred(ctx));
 
        /*
         * 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);
 
-       DEVFS_LOCK();
 
        tnp = dev_findname(tdp, cnp->cn_nameptr);
 
        if (tnp == NULL) {
-               error = ENOENT;
+               error = ENOENT;
+               goto abort;
+       }
+
+       /*
+        * Don't allow removing critical devfs devices
+        */
+       if (devfs_is_name_protected(dvp, cnp->cn_nameptr)) {
+               error = EINVAL;
                goto abort;
        }
 
@@ -639,8 +856,8 @@ devfs_remove(struct vnop_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;
                }
@@ -648,37 +865,36 @@ devfs_remove(struct vnop_remove_args *ap)
        }
 
        /***********************************
-        * Start actually doing things.... *
-        ***********************************/
-       tdp->dn_change = 1;
-       tdp->dn_update = 1;
+       * Start actually doing things.... *
+       ***********************************/
+       devfs_consider_time_update(tdp, DEVFS_UPDATE_CHANGE | DEVFS_UPDATE_MOD);
 
        /*
         * 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;
        }
        dev_free_name(tnp);
 abort:
        DEVFS_UNLOCK();
 
-       return (error);
+       return error;
 }
 
 /*
  */
 static int
 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 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;
@@ -687,7 +903,6 @@ devfs_link(struct vnop_link_args *ap)
        devnode_t * tdp;
        devdirent_t * tnp;
        int error = 0;
-       struct timeval now;
 
        /*
         * First catch an arbitrary restriction for this FS
@@ -703,35 +918,33 @@ devfs_link(struct vnop_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) {
-               return (EXDEV);
+               return EXDEV;
        }
        DEVFS_LOCK();
 
+       fp = VTODN(vp);
 
        /***********************************
-        * Start actually doing things.... *
-        ***********************************/
-       fp->dn_change = 1;
-
-       microtime(&now);
-       error = devfs_update(vp, &now, &now);
+       * Start actually doing things.... *
+       ***********************************/
+       dn_times_now(fp, DEVFS_UPDATE_CHANGE);
 
        if (!error) {
-           error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp);
+               error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp);
        }
 out1:
        DEVFS_UNLOCK();
 
-       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");
@@ -758,15 +971,15 @@ out1:
  */
 static int
 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 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;
@@ -775,10 +988,9 @@ devfs_rename(struct vnop_rename_args *ap)
        struct componentname *tcnp = ap->a_tcnp;
        struct componentname *fcnp = ap->a_fcnp;
        devnode_t *fp, *fdp, *tp, *tdp;
-       devdirent_t *fnp,*tnp;
+       devdirent_t *fnp, *tnp;
        int doingdirectory = 0;
        int error = 0;
-       struct timeval now;
 
        DEVFS_LOCK();
        /*
@@ -801,7 +1013,7 @@ devfs_rename(struct vnop_rename_args *ap)
        fnp = dev_findname(fdp, fcnp->cn_nameptr);
 
        if (fnp == NULL) {
-               error = ENOENT;
+               error = ENOENT;
                goto out;
        }
        tp = NULL;
@@ -811,12 +1023,12 @@ devfs_rename(struct vnop_rename_args *ap)
                tnp = dev_findname(tdp, tcnp->cn_nameptr);
 
                if (tnp == NULL) {
-                       error = ENOENT;
+                       error = ENOENT;
                        goto out;
                }
                tp = VTODN(tvp);
        }
-       
+
        /*
         * Make sure that we don't try do something stupid
         */
@@ -824,30 +1036,39 @@ devfs_rename(struct vnop_rename_args *ap)
                /*
                 * 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 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;
                tmp = tdp;
                do {
-                       if(tmp == fp) {
+                       if (tmp == fp) {
                                /* XXX unlock stuff here probably */
                                error = EINVAL;
                                goto out;
@@ -857,14 +1078,10 @@ devfs_rename(struct vnop_rename_args *ap)
        }
 
        /***********************************
-        * Start actually doing things.... *
-        ***********************************/
-       fp->dn_change = 1;
-       microtime(&now);
+       * Start actually doing things.... *
+       ***********************************/
+       dn_times_now(fp, DEVFS_UPDATE_CHANGE);
 
-       if ( (error = devfs_update(fvp, &now, &now)) ) {
-           goto out;
-       }
        /*
         * Check if just deleting a link name.
         */
@@ -888,22 +1105,21 @@ devfs_rename(struct vnop_rename_args *ap)
        /*
         * 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 = kauth_cred_getuid(vfs_context_ucred(tcnp->cn_context));
                /*
                 * 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;
+               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.. */
 
@@ -912,40 +1128,36 @@ bad:
        fp->dn_links--; /* we added one earlier*/
 out:
        DEVFS_UNLOCK();
-       return (error);
+       return error;
 }
 
 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;
-        } */
+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;
        vfs_context_t ctx = cnp->cn_context;
        struct proc *p = vfs_context_proc(ctx);
        int error = 0;
        devnode_t * dir_p;
-       devnode_type_t typeinfo;
        devdirent_t * nm_p;
        devnode_t * dev_p;
-       struct vnode_attr *     vap = ap->a_vap;
+       struct vnode_attr *     vap = ap->a_vap;
        struct vnode * * vpp = ap->a_vpp;
 
-       dir_p = VTODN(ap->a_dvp);
-       typeinfo.Slnk.name = ap->a_target;
-       typeinfo.Slnk.namelen = strlen(ap->a_target);
-
        DEVFS_LOCK();
-       error = dev_add_entry(cnp->cn_nameptr, dir_p, DEV_SLNK, 
-                             &typeinfo, NULL, NULL, &nm_p);
+
+       dir_p = VTODN(ap->a_dvp);
+       error = dev_add_entry(cnp->cn_nameptr, dir_p, DEV_DIR,
+           NULL, NULL, NULL, &nm_p);
        if (error) {
-           goto failure;
+               goto failure;
        }
        dev_p = nm_p->de_dnp;
        dev_p->dn_uid = dir_p->dn_uid;
@@ -960,49 +1172,136 @@ failure:
        return error;
 }
 
+/*
+ * 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.
+ */
+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_make_symlink(devnode_t *dir_p, char *name, int mode, char *target, devdirent_t **newent)
+{
+       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 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;
+       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;
+       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;
 
        *vpp = NULL;
        if (!(vap->va_type == VBLK) && !(vap->va_type == VCHR)) {
-               return (EINVAL); /* only support mknod of special files */
+               return EINVAL; /* only support mknod of special files */
        }
-       dir_p = VTODN(dvp);
        typeinfo.dev = vap->va_rdev;
 
        DEVFS_LOCK();
-       error = dev_add_entry(cnp->cn_nameptr, dir_p, 
-                             (vap->va_type == VBLK) ? DEV_BDEV : DEV_CDEV,
-                             &typeinfo, NULL, NULL, &devent);
+
+       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;
+       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;
@@ -1012,7 +1311,7 @@ devfs_mknod(struct vnop_mknod_args *ap)
 failure:
        DEVFS_UNLOCK();
 
-       return (error);
+       return error;
 }
 
 /*
@@ -1020,33 +1319,35 @@ failure:
  */
 static int
 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 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;
+       int     startpos, pos;
 
-       if (ap->a_flags & (VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF))
-               return (EINVAL);
+       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;
 
@@ -1055,33 +1356,29 @@ devfs_readdir(struct vnop_readdir_args *ap)
        name_node = dir_node->dn_typeinfo.Dir.dirlist;
        nodenumber = 0;
 
-       dir_node->dn_access = 1;
-
-       while ((name_node || (nodenumber < 2)) && (uio_resid(uio) > 0))
-       {
-               switch(nodenumber)
-               {
-               case    0:
-                       dirent.d_fileno = (int32_t)(void *)dir_node;
+       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;
@@ -1098,29 +1395,33 @@ devfs_readdir(struct vnop_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_resid(uio) < 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();
        uio->uio_offset = pos;
 
-       return (error);
+       devfs_consider_time_update(dir_node, DEVFS_UPDATE_ACCESS);
+
+       return error;
 }
 
 
@@ -1128,11 +1429,11 @@ devfs_readdir(struct vnop_readdir_args *ap)
  */
 static int
 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 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;
@@ -1143,41 +1444,39 @@ devfs_readlink(struct vnop_readlink_args *ap)
        lnk_node = VTODN(vp);
 
        if (lnk_node->dn_type != DEV_SLNK) {
-               error = EINVAL;
+               error = EINVAL;
                goto out;
        }
-       error = uiomove(lnk_node->dn_typeinfo.Slnk.name, 
-                       lnk_node->dn_typeinfo.Slnk.namelen, uio);
-out:   
+       error = uiomove(lnk_node->dn_typeinfo.Slnk.name,
+           lnk_node->dn_typeinfo.Slnk.namelen, uio);
+out:
        return error;
 }
 
 static int
 devfs_reclaim(struct vnop_reclaim_args *ap)
-        /*struct vnop_reclaim_args {
              struct vnode *a_vp;
       } */
+/*struct vnop_reclaim_args {
*       struct vnode *a_vp;
*  } */
 {
-    struct vnode *     vp = ap->a_vp;
-    devnode_t *        dnp = VTODN(vp);
-    
-    DEVFS_LOCK();
-
-    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;
+       struct vnode *      vp = ap->a_vp;
+       devnode_t *         dnp;
+
+       DEVFS_LOCK();
+
+       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);
 
-       if (dnp->dn_delete) {
-           devnode_free(dnp);
+               /* This could delete the node, if we are the last vnode */
+               devfs_rele_node(dnp);
        }
-    }
-    DEVFS_UNLOCK();
+       DEVFS_UNLOCK();
 
-    return(0);
+       return 0;
 }
 
 
@@ -1187,11 +1486,11 @@ devfs_reclaim(struct vnop_reclaim_args *ap)
 static int
 devs_vnop_pathconf(
        struct vnop_pathconf_args /* {
-               struct vnode *a_vp;
-               int a_name;
-               int *a_retval;
-               vfs_context_t a_context;
-       } */ *ap)
+                                   *  struct vnode *a_vp;
+                                   *  int a_name;
+                                   *  int *a_retval;
+                                   *  vfs_context_t a_context;
+                                   *  } */*ap)
 {
        switch (ap->a_name) {
        case _PC_LINK_MAX:
@@ -1199,13 +1498,13 @@ devs_vnop_pathconf(
                *ap->a_retval = 32767;
                break;
        case _PC_NAME_MAX:
-               *ap->a_retval = DEVMAXNAMESIZE - 1;     /* includes NUL */
+               *ap->a_retval = DEVMAXNAMESIZE - 1;     /* includes NUL */
                break;
        case _PC_PATH_MAX:
-               *ap->a_retval = DEVMAXPATHSIZE - 1;     /* XXX nonconformant */
+               *ap->a_retval = DEVMAXPATHSIZE - 1;     /* XXX nonconformant */
                break;
        case _PC_CHOWN_RESTRICTED:
-               *ap->a_retval = 1;
+               *ap->a_retval = 200112;         /* _POSIX_CHOWN_RESTRICTED */
                break;
        case _PC_NO_TRUNC:
                *ap->a_retval = 0;
@@ -1217,10 +1516,10 @@ devs_vnop_pathconf(
                *ap->a_retval = 1;
                break;
        default:
-               return (EINVAL);
+               return EINVAL;
        }
 
-       return (0);
+       return 0;
 }
 
 
@@ -1234,13 +1533,24 @@ devs_vnop_pathconf(
  *     struct vnop_inactive_args {
  *             struct vnode *a_vp;
  *             vfs_context_t a_context;
- *     } 
+ *     }
  */
 
 static int
 devfs_inactive(__unused struct vnop_inactive_args *ap)
 {
-    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;
 }
 
 /*
@@ -1254,104 +1564,137 @@ devfs_update(struct vnode *vp, struct timeval *access, struct timeval *modify)
 
        ip = VTODN(vp);
        if (vp->v_mount->mnt_flag & MNT_RDONLY) {
-               ip->dn_access = 0;
-               ip->dn_change = 0;
-               ip->dn_update = 0;
+               ip->dn_access = 0;
+               ip->dn_change = 0;
+               ip->dn_update = 0;
 
-               return (0);
+               return 0;
        }
+
+       DEVFS_ATTR_LOCK_SPIN();
        microtime(&now);
-       dn_times(ip, access, modify, &now);
+       dn_times_locked(ip, access, modify, &now, DEVFS_UPDATE_ACCESS | DEVFS_UPDATE_MOD);
+       DEVFS_ATTR_UNLOCK();
 
-       return (0);
+       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[] = {
-       { &vnop_default_desc, (VOPFUNC)vn_default_error },
-       { &vnop_lookup_desc, (VOPFUNC)devfs_lookup },           /* lookup */
-       { &vnop_create_desc, (VOPFUNC)err_create },             /* create */
-       { &vnop_whiteout_desc, (VOPFUNC)err_whiteout },         /* whiteout */
-       { &vnop_mknod_desc, (VOPFUNC)devfs_mknod },             /* mknod */
-       { &vnop_open_desc, (VOPFUNC)nop_open },                 /* open */
-       { &vnop_close_desc, (VOPFUNC)devfs_close },             /* close */
-       { &vnop_getattr_desc, (VOPFUNC)devfs_getattr },         /* getattr */
-       { &vnop_setattr_desc, (VOPFUNC)devfs_setattr },         /* setattr */
-       { &vnop_read_desc, (VOPFUNC)devfs_read },               /* read */
-       { &vnop_write_desc, (VOPFUNC)devfs_write },             /* write */
-       { &vnop_ioctl_desc, (VOPFUNC)err_ioctl },               /* ioctl */
-       { &vnop_select_desc, (VOPFUNC)err_select },             /* select */
-       { &vnop_revoke_desc, (VOPFUNC)err_revoke },             /* revoke */
-       { &vnop_mmap_desc, (VOPFUNC)err_mmap },                 /* mmap */
-       { &vnop_fsync_desc, (VOPFUNC)nop_fsync },               /* fsync */
-       { &vnop_remove_desc, (VOPFUNC)devfs_remove },           /* remove */
-       { &vnop_link_desc, (VOPFUNC)devfs_link },               /* link */
-       { &vnop_rename_desc, (VOPFUNC)devfs_rename },           /* rename */
-       { &vnop_mkdir_desc, (VOPFUNC)err_mkdir },               /* mkdir */
-       { &vnop_rmdir_desc, (VOPFUNC)err_rmdir },               /* rmdir */
-       { &vnop_symlink_desc, (VOPFUNC)devfs_symlink },         /* symlink */
-       { &vnop_readdir_desc, (VOPFUNC)devfs_readdir },         /* readdir */
-       { &vnop_readlink_desc, (VOPFUNC)devfs_readlink },       /* readlink */
-       { &vnop_inactive_desc, (VOPFUNC)devfs_inactive },       /* inactive */
-       { &vnop_reclaim_desc, (VOPFUNC)devfs_reclaim },         /* reclaim */
-       { &vnop_strategy_desc, (VOPFUNC)err_strategy },         /* strategy */
-       { &vnop_pathconf_desc, (VOPFUNC)devs_vnop_pathconf },   /* pathconf */
-       { &vnop_advlock_desc, (VOPFUNC)err_advlock },           /* advlock */
-       { &vnop_bwrite_desc, (VOPFUNC)err_bwrite },
-       { &vnop_pagein_desc, (VOPFUNC)err_pagein },             /* Pagein */
-       { &vnop_pageout_desc, (VOPFUNC)err_pageout },           /* Pageout */
-       { &vnop_copyfile_desc, (VOPFUNC)err_copyfile },         /* Copyfile */
-       { &vnop_blktooff_desc, (VOPFUNC)err_blktooff },         /* blktooff */
-       { &vnop_offtoblk_desc, (VOPFUNC)err_offtoblk },         /* offtoblk */
-       { &vnop_blockmap_desc, (VOPFUNC)err_blockmap },         /* blockmap */
-       { (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[] = {
-       { &vnop_default_desc, (VOPFUNC)vn_default_error },
-       { &vnop_lookup_desc, (VOPFUNC)spec_lookup },            /* lookup */
-       { &vnop_create_desc, (VOPFUNC)spec_create },            /* create */
-       { &vnop_mknod_desc, (VOPFUNC)spec_mknod },              /* mknod */
-       { &vnop_open_desc, (VOPFUNC)spec_open },                        /* open */
-       { &vnop_close_desc, (VOPFUNC)devfsspec_close },         /* close */
-       { &vnop_getattr_desc, (VOPFUNC)devfs_getattr },         /* getattr */
-       { &vnop_setattr_desc, (VOPFUNC)devfs_setattr },         /* setattr */
-       { &vnop_read_desc, (VOPFUNC)devfsspec_read },           /* read */
-       { &vnop_write_desc, (VOPFUNC)devfsspec_write },         /* write */
-       { &vnop_ioctl_desc, (VOPFUNC)spec_ioctl },              /* ioctl */
-       { &vnop_select_desc, (VOPFUNC)spec_select },            /* select */
-       { &vnop_revoke_desc, (VOPFUNC)spec_revoke },            /* revoke */
-       { &vnop_mmap_desc, (VOPFUNC)spec_mmap },                        /* mmap */
-       { &vnop_fsync_desc, (VOPFUNC)spec_fsync },              /* fsync */
-       { &vnop_remove_desc, (VOPFUNC)devfs_remove },           /* remove */
-       { &vnop_link_desc, (VOPFUNC)devfs_link },               /* link */
-       { &vnop_rename_desc, (VOPFUNC)spec_rename },            /* rename */
-       { &vnop_mkdir_desc, (VOPFUNC)spec_mkdir },              /* mkdir */
-       { &vnop_rmdir_desc, (VOPFUNC)spec_rmdir },              /* rmdir */
-       { &vnop_symlink_desc, (VOPFUNC)spec_symlink },          /* symlink */
-       { &vnop_readdir_desc, (VOPFUNC)spec_readdir },          /* readdir */
-       { &vnop_readlink_desc, (VOPFUNC)spec_readlink },                /* readlink */
-       { &vnop_inactive_desc, (VOPFUNC)devfs_inactive },       /* inactive */
-       { &vnop_reclaim_desc, (VOPFUNC)devfs_reclaim },         /* reclaim */
-       { &vnop_strategy_desc, (VOPFUNC)spec_strategy },                /* strategy */
-       { &vnop_pathconf_desc, (VOPFUNC)spec_pathconf },                /* pathconf */
-       { &vnop_advlock_desc, (VOPFUNC)spec_advlock },          /* advlock */
-       { &vnop_bwrite_desc, (VOPFUNC)vn_bwrite },
-       { &vnop_pagein_desc, (VOPFUNC)err_pagein },             /* Pagein */
-       { &vnop_pageout_desc, (VOPFUNC)err_pageout },           /* Pageout */
-       { &vnop_copyfile_desc, (VOPFUNC)err_copyfile },         /* Copyfile */
-       { &vnop_blktooff_desc, (VOPFUNC)spec_blktooff },        /* blktooff */
-       { &vnop_blktooff_desc, (VOPFUNC)spec_offtoblk  },       /* blkofftoblk */
-       { &vnop_blockmap_desc, (VOPFUNC)spec_blockmap },        /* blockmap */
-       { (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 */