]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/miscfs/devfs/devfs_vnops.c
xnu-3247.1.106.tar.gz
[apple/xnu.git] / bsd / miscfs / devfs / devfs_vnops.c
index bf7f11047df37398227a43f7366a56b893077e6e..207a50c01dfa3410221a2b7e1a7b7f18d1cf7536 100644 (file)
@@ -1,31 +1,29 @@
 /*
- * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
+ * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
- * @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.
  * 
- * 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, 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.
  *  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);
+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;
 
-static int devfs_update(struct vnode *vp, struct timeval *access,
-                        struct timeval *modify);
+       DEVFS_ATTR_LOCK_SPIN();
+       microtime(&now);
+       dn_times_locked(dnp, &now, &now, &now, just_changed_flags);
+       DEVFS_ATTR_UNLOCK();
+}
 
 
 /*
@@ -165,8 +249,7 @@ 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);
 
        /*
@@ -336,27 +419,32 @@ devfs_getattr(struct vnop_getattr_args *ap)
        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);
 
+       /*
+        * 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:
-               VATTR_RETURN(vap, va_rdev,  (dev_t)file_node->dn_dvm);
+#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);
+               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);
+               VATTR_RETURN(vap, va_rdev, vp->v_rdev);
                vap->va_mode |= (S_IFBLK);
                break;
        case    DEV_SLNK:
@@ -371,7 +459,7 @@ devfs_getattr(struct vnop_getattr_args *ap)
        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) */
@@ -381,6 +469,13 @@ devfs_getattr(struct vnop_getattr_args *ap)
                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_sec();
@@ -393,11 +488,20 @@ devfs_getattr(struct vnop_getattr_args *ap)
        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;
@@ -413,15 +517,13 @@ devfs_setattr(struct vnop_setattr_args *ap)
 {
        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();
+
+       file_node = VTODN(vp);
        /*
         * Go through the fields and update if set.
         */
@@ -473,6 +575,29 @@ devfs_setattr(struct vnop_setattr_args *ap)
        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 {
@@ -508,13 +633,12 @@ devfs_close(struct vnop_close_args *ap)
        } */
 {
        struct vnode *          vp = ap->a_vp;
-       register devnode_t *    dnp = VTODN(vp);
-       struct timeval now;
+       register devnode_t *    dnp;
 
        if (vnode_isinuse(vp, 1)) {
            DEVFS_LOCK();
-           microtime(&now);
-           dn_times(dnp, &now, &now, &now);
+           dnp = VTODN(vp);
+           dn_times_now(dnp, 0);
            DEVFS_UNLOCK();
        }
        return (0);
@@ -529,18 +653,69 @@ devfsspec_close(struct vnop_close_args *ap)
        } */
 {
        struct vnode *          vp = ap->a_vp;
-       register devnode_t *    dnp = VTODN(vp);
-       struct timeval now;
+       register devnode_t *    dnp;
 
-       if (vnode_isinuse(vp, 1)) {
+       if (vnode_isinuse(vp, 0)) {
            DEVFS_LOCK();
-           microtime(&now);
-           dn_times(dnp, &now, &now, &now);
+           dnp = VTODN(vp);
+           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 vnop_read_args *ap)
         /* struct vnop_read_args {
@@ -552,7 +727,7 @@ devfsspec_read(struct vnop_read_args *ap)
 {
        register 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));
 }
@@ -568,8 +743,7 @@ devfsspec_write(struct vnop_write_args *ap)
 {
        register 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));
 }
@@ -596,8 +770,12 @@ devfs_write(struct vnop_write_args *ap)
        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)
+devfs_vnop_remove(struct vnop_remove_args *ap)
         /* struct vnop_remove_args  {
                 struct vnode *a_dvp;
                 struct vnode *a_vp;
@@ -607,23 +785,22 @@ devfs_remove(struct vnop_remove_args *ap)
        struct vnode *vp = ap->a_vp;
        struct vnode *dvp = ap->a_dvp;
        struct componentname *cnp = ap->a_cnp;
-       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);
 
@@ -650,8 +827,7 @@ devfs_remove(struct vnop_remove_args *ap)
        /***********************************
         * Start actually doing things.... *
         ***********************************/
-       tdp->dn_change = 1;
-       tdp->dn_update = 1;
+       devfs_consider_time_update(tdp, DEVFS_UPDATE_CHANGE | DEVFS_UPDATE_MOD);
 
        /*
         * Target must be empty if a directory and have no links
@@ -687,7 +863,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,22 +878,20 @@ 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);
        }
        DEVFS_LOCK();
 
+       fp = VTODN(vp);
 
        /***********************************
         * Start actually doing things.... *
         ***********************************/
-       fp->dn_change = 1;
-
-       microtime(&now);
-       error = devfs_update(vp, &now, &now);
+       dn_times_now(fp, DEVFS_UPDATE_CHANGE);
 
        if (!error) {
            error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp);
@@ -778,7 +951,6 @@ devfs_rename(struct vnop_rename_args *ap)
        devdirent_t *fnp,*tnp;
        int doingdirectory = 0;
        int error = 0;
-       struct timeval now;
 
        DEVFS_LOCK();
        /*
@@ -859,12 +1031,8 @@ devfs_rename(struct vnop_rename_args *ap)
        /***********************************
         * Start actually doing things.... *
         ***********************************/
-       fp->dn_change = 1;
-       microtime(&now);
+       dn_times_now(fp, DEVFS_UPDATE_CHANGE);
 
-       if ( (error = devfs_update(fvp, &now, &now)) ) {
-           goto out;
-       }
        /*
         * Check if just deleting a link name.
         */
@@ -890,7 +1058,6 @@ devfs_rename(struct vnop_rename_args *ap)
         * 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
@@ -916,13 +1083,12 @@ out:
 }
 
 static int
-devfs_symlink(struct vnop_symlink_args *ap)
-        /*struct vnop_symlink_args {
+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;
-                char *a_target;
                vfs_context_t a_context;
         } */
 {
@@ -931,19 +1097,16 @@ devfs_symlink(struct vnop_symlink_args *ap)
        struct proc *p = vfs_context_proc(ctx);
        int error = 0;
        devnode_t * dir_p;
-       devnode_type_t typeinfo;
        devdirent_t * nm_p;
        devnode_t * dev_p;
        struct vnode_attr *     vap = ap->a_vap;
        struct vnode * * vpp = ap->a_vpp;
 
-       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;
        }
@@ -960,6 +1123,91 @@ 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
  */
@@ -989,10 +1237,12 @@ devfs_mknod(struct vnop_mknod_args *ap)
        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();
+
+       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);
@@ -1034,7 +1284,7 @@ devfs_readdir(struct vnop_readdir_args *ap)
        struct dirent dirent;
        devnode_t * dir_node;
        devdirent_t *   name_node;
-       char    *name;
+       const char *name;
        int error = 0;
        int reclen;
        int nodenumber;
@@ -1055,30 +1305,27 @@ 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;
+                       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;
+                               dirent.d_fileno = dir_node->dn_typeinfo.Dir.parent->dn_ino;
                        else
-                               dirent.d_fileno = (u_int32_t)dir_node;
+                               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) {
@@ -1107,7 +1354,7 @@ devfs_readdir(struct vnop_readdir_args *ap)
                {
                        if (uio_resid(uio) < reclen) /* will it fit? */
                                break;
-                       strcpy( dirent.d_name,name);
+                       strlcpy(dirent.d_name, name, DEVMAXNAMESIZE);
                        if ((error = uiomove ((caddr_t)&dirent,
                                        dirent.d_reclen, uio)) != 0)
                                break;
@@ -1120,6 +1367,8 @@ devfs_readdir(struct vnop_readdir_args *ap)
        DEVFS_UNLOCK();
        uio->uio_offset = pos;
 
+       devfs_consider_time_update(dir_node, DEVFS_UPDATE_ACCESS);
+
        return (error);
 }
 
@@ -1159,21 +1408,19 @@ devfs_reclaim(struct vnop_reclaim_args *ap)
         } */
 {
     struct vnode *     vp = ap->a_vp;
-    devnode_t *        dnp = VTODN(vp);
+    devnode_t *        dnp;
     
     DEVFS_LOCK();
 
+    dnp = VTODN(vp);
+
     if (dnp) {
-       /* 
-        * do the same as devfs_inactive in case it is not called
-        * before us (can that ever happen?)
-        */
+       /* If this is a cloning device, it didn't have a dn_vn anyway */
        dnp->dn_vn = NULL;
-       vp->v_data = 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();
 
@@ -1205,7 +1452,7 @@ devs_vnop_pathconf(
                *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;
@@ -1240,7 +1487,18 @@ devs_vnop_pathconf(
 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);
 }
 
 /*
@@ -1260,8 +1518,11 @@ devfs_update(struct vnode *vp, struct timeval *access, struct timeval *modify)
 
                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);
 }
@@ -1287,11 +1548,11 @@ static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = {
        { &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_remove_desc, (VOPFUNC)devfs_vnop_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_mkdir_desc, (VOPFUNC)devfs_mkdir },             /* mkdir */
+       { &vnop_rmdir_desc, (VOPFUNC)devfs_rmdir },             /* rmdir */
        { &vnop_symlink_desc, (VOPFUNC)devfs_symlink },         /* symlink */
        { &vnop_readdir_desc, (VOPFUNC)devfs_readdir },         /* readdir */
        { &vnop_readlink_desc, (VOPFUNC)devfs_readlink },       /* readlink */
@@ -1307,6 +1568,9 @@ static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = {
        { &vnop_blktooff_desc, (VOPFUNC)err_blktooff },         /* blktooff */
        { &vnop_offtoblk_desc, (VOPFUNC)err_offtoblk },         /* offtoblk */
        { &vnop_blockmap_desc, (VOPFUNC)err_blockmap },         /* blockmap */
+#if CONFIG_MACF
+       { &vnop_setlabel_desc, (VOPFUNC)devfs_setlabel },       /* setlabel */
+#endif
        { (struct vnodeop_desc*)NULL, (int(*)())NULL }
 };
 struct vnodeopv_desc devfs_vnodeop_opv_desc =
@@ -1330,7 +1594,7 @@ static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries[] = {
        { &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_remove_desc, (VOPFUNC)devfs_vnop_remove },      /* remove */
        { &vnop_link_desc, (VOPFUNC)devfs_link },               /* link */
        { &vnop_rename_desc, (VOPFUNC)spec_rename },            /* rename */
        { &vnop_mkdir_desc, (VOPFUNC)spec_mkdir },              /* mkdir */
@@ -1344,15 +1608,43 @@ static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries[] = {
        { &vnop_pathconf_desc, (VOPFUNC)spec_pathconf },                /* pathconf */
        { &vnop_advlock_desc, (VOPFUNC)spec_advlock },          /* advlock */
        { &vnop_bwrite_desc, (VOPFUNC)vn_bwrite },
-       { &vnop_devblocksize_desc, (VOPFUNC)spec_devblocksize },        /* devblocksize */
        { &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 */
+#if CONFIG_MACF
+       { &vnop_setlabel_desc, (VOPFUNC)devfs_setlabel },       /* setlabel */
+#endif
        { (struct vnodeop_desc*)NULL, (int(*)())NULL }
 };
 struct vnodeopv_desc devfs_spec_vnodeop_opv_desc =
        { &devfs_spec_vnodeop_p, devfs_spec_vnodeop_entries };
 
+
+#if FDESC
+int (**devfs_devfd_vnodeop_p)(void*);
+static struct vnodeopv_entry_desc devfs_devfd_vnodeop_entries[] = {
+       { &vnop_default_desc, (VOPFUNC)vn_default_error },
+       { &vnop_lookup_desc, (VOPFUNC)devfs_devfd_lookup},      /* lookup */
+       { &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_revoke_desc, (VOPFUNC)err_revoke },             /* revoke */
+       { &vnop_fsync_desc, (VOPFUNC)nop_fsync },               /* fsync */
+       { &vnop_readdir_desc, (VOPFUNC)devfs_devfd_readdir},            /* readdir */
+       { &vnop_inactive_desc, (VOPFUNC)devfs_inactive },       /* inactive */
+       { &vnop_reclaim_desc, (VOPFUNC)devfs_reclaim },         /* reclaim */
+       { &vnop_pathconf_desc, (VOPFUNC)devs_vnop_pathconf },   /* pathconf */
+#if CONFIG_MACF
+       { &vnop_setlabel_desc, (VOPFUNC)devfs_setlabel },       /* setlabel */
+#endif
+       { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc devfs_devfd_vnodeop_opv_desc =
+       { &devfs_devfd_vnodeop_p, devfs_devfd_vnodeop_entries};
+#endif /* FDESC */
+
+