]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/vfs/vfs_lookup.c
xnu-792.6.22.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_lookup.c
index 189de5062490168e24f05cb74e26b4718700274f..d10ba8fd37373cd9fd6b9f3e98b8f4d63af79d35 100644 (file)
@@ -1,24 +1,21 @@
 /*
- * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
- * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
+ * The contents of this file constitute Original Code as defined in and
+ * are subject to the Apple Public Source License Version 1.1 (the
+ * "License").  You may not use this file except in compliance with the
+ * License.  Please obtain a copy of the License at
+ * http://www.apple.com/publicsource and read it before using this file.
  * 
- * This 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. 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
+ * This 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.
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
+ * License for the specific language governing rights and limitations
+ * under the License.
  * 
  * @APPLE_LICENSE_HEADER_END@
  */
  */
 
 #include <sys/param.h>
+#include <sys/systm.h>
 #include <sys/syslimits.h>
 #include <sys/time.h>
 #include <sys/namei.h>
 #include <sys/vm.h>
-#include <sys/vnode.h>
-#include <sys/mount.h>
+#include <sys/vnode_internal.h>
+#include <sys/mount_internal.h>
 #include <sys/errno.h>
 #include <sys/malloc.h>
 #include <sys/filedesc.h>
-#include <sys/proc.h>
+#include <sys/proc_internal.h>
 #include <sys/kdebug.h>
 #include <sys/unistd.h>                /* For _PC_NAME_MAX */
+#include <sys/uio_internal.h>
+#include <sys/kauth.h>
+
+#include <bsm/audit_kernel.h>
 
 #if KTRACE
 #include <sys/ktrace.h>
 #endif
 
+
+static void kdebug_lookup(struct vnode *dp, struct componentname *cnp);
+
 /*
  * Convert a pathname into a pointer to a locked inode.
  *
  *             if symbolic link, massage name in buffer and continue
  *     }
  */
+
 int
 namei(ndp)
        register struct nameidata *ndp;
@@ -108,144 +114,227 @@ namei(ndp)
        register struct filedesc *fdp;  /* pointer to file descriptor state */
        register char *cp;              /* pointer into pathname argument */
        register struct vnode *dp;      /* the directory we are searching */
-       struct iovec aiov;              /* uio for reading symbolic links */
-       struct uio auio;
-       int error, linklen;
+       uio_t auio;
+       int error;
        struct componentname *cnp = &ndp->ni_cnd;
-       struct proc *p = cnp->cn_proc;
+       vfs_context_t ctx = cnp->cn_context;
+       struct proc *p = vfs_context_proc(ctx);
+       char *tmppn;
+       char uio_buf[ UIO_SIZEOF(1) ];
 
-       ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred;
 #if DIAGNOSTIC
-       if (!cnp->cn_cred || !cnp->cn_proc)
+       if (!vfs_context_ucred(ctx) || !p)
                panic ("namei: bad cred/proc");
        if (cnp->cn_nameiop & (~OPMASK))
                panic ("namei: nameiop contaminated with flags");
        if (cnp->cn_flags & OPMASK)
                panic ("namei: flags contaminated with nameiops");
 #endif
-       fdp = cnp->cn_proc->p_fd;
+       fdp = p->p_fd;
 
        /*
         * Get a buffer for the name to be translated, and copy the
         * name into the buffer.
         */
        if ((cnp->cn_flags & HASBUF) == 0) {
-               MALLOC_ZONE(cnp->cn_pnbuf, caddr_t,
-                               MAXPATHLEN, M_NAMEI, M_WAITOK);
-               cnp->cn_pnlen = MAXPATHLEN;
+               cnp->cn_pnbuf = &ndp->ni_pathbuf;
+               cnp->cn_pnlen = PATHBUFLEN;
        }
-       if (ndp->ni_segflg == UIO_SYSSPACE)
-               error = copystr(ndp->ni_dirp, cnp->cn_pnbuf,
-                           MAXPATHLEN, &ndp->ni_pathlen);
-       else
+#if LP64_DEBUG
+       if (IS_VALID_UIO_SEGFLG(ndp->ni_segflg) == 0) {
+               panic("%s :%d - invalid ni_segflg\n", __FILE__, __LINE__); 
+       }
+#endif /* LP64_DEBUG */
+
+retry_copy:
+       if (UIO_SEG_IS_USER_SPACE(ndp->ni_segflg))
                error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf,
-                           MAXPATHLEN, &ndp->ni_pathlen);
+                           cnp->cn_pnlen, (size_t *)&ndp->ni_pathlen);
+       else
+               error = copystr(CAST_DOWN(void *, ndp->ni_dirp), cnp->cn_pnbuf,
+                           cnp->cn_pnlen, (size_t *)&ndp->ni_pathlen);
+
+       if (error == ENAMETOOLONG && !(cnp->cn_flags & HASBUF)) {
+               MALLOC_ZONE(cnp->cn_pnbuf, caddr_t,
+                           MAXPATHLEN, M_NAMEI, M_WAITOK);
+
+               cnp->cn_flags |= HASBUF;
+               cnp->cn_pnlen = MAXPATHLEN;
+               
+               goto retry_copy;
+       }
+       if (error)
+               goto error_out;
+
+       /* If we are auditing the kernel pathname, save the user pathname */
+       if (cnp->cn_flags & AUDITVNPATH1)
+               AUDIT_ARG(upath, p, cnp->cn_pnbuf, ARG_UPATH1); 
+       if (cnp->cn_flags & AUDITVNPATH2)
+               AUDIT_ARG(upath, p, cnp->cn_pnbuf, ARG_UPATH2); 
+
        /*
         * Do not allow empty pathnames
         */
-       if (!error && *cnp->cn_pnbuf == '\0')
+       if (*cnp->cn_pnbuf == '\0') {
                error = ENOENT;
-
-       if (error) {
-               _FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
-               ndp->ni_vp = NULL;
-               return (error);
+               goto error_out;
        }
        ndp->ni_loopcnt = 0;
 #if KTRACE
-       if (KTRPOINT(cnp->cn_proc, KTR_NAMEI))
-               ktrnamei(cnp->cn_proc->p_tracep, cnp->cn_pnbuf);
+       if (KTRPOINT(p, KTR_NAMEI))
+               ktrnamei(p->p_tracep, cnp->cn_pnbuf);
 #endif
 
        /*
-        * Get starting point for the translation.
+        * determine the starting point for the translation.
         */
-       if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL)
-               ndp->ni_rootdir = rootvnode;
-       dp = fdp->fd_cdir;
-       VREF(dp);
-       for (;;) {
-               /*
-                * Check if root directory should replace current directory.
-                * Done at start of translation and after symbolic link.
-                */
-               cnp->cn_nameptr = cnp->cn_pnbuf;
-               if (*(cnp->cn_nameptr) == '/') {
-                       vrele(dp);
-                       while (*(cnp->cn_nameptr) == '/') {
-                               cnp->cn_nameptr++;
-                               ndp->ni_pathlen--;
-                       }
-                       dp = ndp->ni_rootdir;
-                       VREF(dp);
+       if ((ndp->ni_rootdir = fdp->fd_rdir) == NULLVP) {
+               if ( !(fdp->fd_flags & FD_CHROOT))
+                       ndp->ni_rootdir = rootvnode;
+       }
+       cnp->cn_nameptr = cnp->cn_pnbuf;
+
+       ndp->ni_usedvp = NULLVP;
+
+       if (*(cnp->cn_nameptr) == '/') {
+               while (*(cnp->cn_nameptr) == '/') {
+                       cnp->cn_nameptr++;
+                       ndp->ni_pathlen--;
                }
+               dp = ndp->ni_rootdir;
+       } else if (cnp->cn_flags & USEDVP) {
+               dp = ndp->ni_dvp;
+               ndp->ni_usedvp = dp;
+       } else
+               dp = fdp->fd_cdir;
+
+       if (dp == NULLVP) {
+               error = ENOENT;
+               goto error_out;
+       }
+       ndp->ni_dvp = NULLVP;
+       ndp->ni_vp  = NULLVP;
+
+       for (;;) {
+               int need_newpathbuf;
+               int linklen;
+
                ndp->ni_startdir = dp;
-               if (error = lookup(ndp)) {
-                       FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
-                       return (error);
+
+               if ( (error = lookup(ndp)) ) {
+                       goto error_out;
                }
                /*
                 * Check for symbolic link
                 */
                if ((cnp->cn_flags & ISSYMLINK) == 0) {
-                       if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0) {
-                               FREE_ZONE(cnp->cn_pnbuf,
-                                               cnp->cn_pnlen, M_NAMEI);
-                       } else {
-                               cnp->cn_flags |= HASBUF;
-                       }
                        return (0);
                }
-               if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
-                       VOP_UNLOCK(ndp->ni_dvp, 0, p);
+               if ((cnp->cn_flags & FSNODELOCKHELD)) {
+                       cnp->cn_flags &= ~FSNODELOCKHELD;
+                       unlock_fsnode(ndp->ni_dvp, NULL);
+               }       
                if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
                        error = ELOOP;
                        break;
                }
-               if (ndp->ni_pathlen > 1) {
+               if (ndp->ni_pathlen > 1 || !(cnp->cn_flags & HASBUF))
+                       need_newpathbuf = 1;
+               else
+                       need_newpathbuf = 0;
+
+               if (need_newpathbuf) {
                        MALLOC_ZONE(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
                } else {
                        cp = cnp->cn_pnbuf;
                }
-               aiov.iov_base = cp;
-               aiov.iov_len = MAXPATHLEN;
-               auio.uio_iov = &aiov;
-               auio.uio_iovcnt = 1;
-               auio.uio_offset = 0;
-               auio.uio_rw = UIO_READ;
-               auio.uio_segflg = UIO_SYSSPACE;
-               auio.uio_procp = (struct proc *)0;
-               auio.uio_resid = MAXPATHLEN;
-               if (error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred)) {
-                       if (ndp->ni_pathlen > 1)
-                               _FREE_ZONE(cp, MAXPATHLEN, M_NAMEI);
+               auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
+
+               uio_addiov(auio, CAST_USER_ADDR_T(cp), MAXPATHLEN);
+
+               error = VNOP_READLINK(ndp->ni_vp, auio, ctx);
+               if (error) {
+                       if (need_newpathbuf)
+                               FREE_ZONE(cp, MAXPATHLEN, M_NAMEI);
                        break;
                }
-               linklen = MAXPATHLEN - auio.uio_resid;
-               if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
-                       if (ndp->ni_pathlen > 1)
-                               _FREE_ZONE(cp, MAXPATHLEN, M_NAMEI);
+               // LP64todo - fix this
+               linklen = MAXPATHLEN - uio_resid(auio);
+               if (linklen + ndp->ni_pathlen > MAXPATHLEN) {
+                       if (need_newpathbuf)
+                               FREE_ZONE(cp, MAXPATHLEN, M_NAMEI);
+
                        error = ENAMETOOLONG;
                        break;
                }
-               if (ndp->ni_pathlen > 1) {
+               if (need_newpathbuf) {
+                       long len = cnp->cn_pnlen;
+
+                       tmppn = cnp->cn_pnbuf;
                        bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
-                       FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
                        cnp->cn_pnbuf = cp;
                        cnp->cn_pnlen = MAXPATHLEN;
+
+                       if ( (cnp->cn_flags & HASBUF) )
+                               FREE_ZONE(tmppn, len, M_NAMEI);
+                       else
+                               cnp->cn_flags |= HASBUF;
                } else
                        cnp->cn_pnbuf[linklen] = '\0';
+
                ndp->ni_pathlen += linklen;
-               vput(ndp->ni_vp);
+               cnp->cn_nameptr = cnp->cn_pnbuf;
+
+               /*
+                * starting point for 'relative'
+                * symbolic link path
+                */
                dp = ndp->ni_dvp;
+               /*
+                * get rid of references returned via 'lookup'
+                */
+               vnode_put(ndp->ni_vp);
+               vnode_put(ndp->ni_dvp);
+
+               ndp->ni_vp = NULLVP;
+               ndp->ni_dvp = NULLVP;
+
+               /*
+                * Check if symbolic link restarts us at the root
+                */
+               if (*(cnp->cn_nameptr) == '/') {
+                       while (*(cnp->cn_nameptr) == '/') {
+                               cnp->cn_nameptr++;
+                               ndp->ni_pathlen--;
+                       }
+                       if ((dp = ndp->ni_rootdir) == NULLVP) {
+                               error = ENOENT;
+                               goto error_out;
+                       }
+               }
+       }
+       /*
+        * only come here if we fail to handle a SYMLINK...
+        * if either ni_dvp or ni_vp is non-NULL, then
+        * we need to drop the iocount that was picked
+        * up in the lookup routine
+        */
+       if (ndp->ni_dvp)
+               vnode_put(ndp->ni_dvp);
+       if (ndp->ni_vp)
+               vnode_put(ndp->ni_vp);
+ error_out:
+       if ( (cnp->cn_flags & HASBUF) ) {
+               cnp->cn_flags &= ~HASBUF;
+               FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
        }
-       FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
-       vrele(ndp->ni_dvp);
-       vput(ndp->ni_vp);
-       ndp->ni_vp = NULL;
+       cnp->cn_pnbuf = NULL;
+       ndp->ni_vp = NULLVP;
+
        return (error);
 }
 
+
 /*
  * Search a pathname.
  * This is a very central and rather complicated routine.
@@ -274,7 +363,7 @@ namei(ndp)
  *     identify next component of name at ndp->ni_ptr
  *     handle degenerate case where name is null string
  *     if .. and crossing mount points and on mounted filesys, find parent
- *     call VOP_LOOKUP routine for next component name
+ *     call VNOP_LOOKUP routine for next component name
  *         directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set
  *         component vnode returned in ni_vp (if it exists), locked.
  *     if result vnode is mounted on and crossing mount points,
@@ -288,124 +377,66 @@ int
 lookup(ndp)
        register struct nameidata *ndp;
 {
-       register char *cp;              /* pointer into pathname argument */
-       register struct vnode *dp = 0;  /* the directory we are searching */
-       struct vnode *tdp;              /* saved dp */
-       struct mount *mp;               /* mount table entry */
-       int namemax = 0;                        /* maximun number of bytes for filename returned by pathconf() */
-       int docache;                    /* == 0 do not cache last component */
+       register char   *cp;            /* pointer into pathname argument */
+       vnode_t         tdp;            /* saved dp */
+       vnode_t         dp;             /* the directory we are searching */
+       mount_t         mp;             /* mount table entry */
+       int docache = 1;                /* == 0 do not cache last component */
        int wantparent;                 /* 1 => wantparent or lockparent flag */
-       int dp_unlocked = 0;    /* 1 => dp already VOP_UNLOCK()-ed */
        int rdonly;                     /* lookup read-only flag bit */
-       int trailing_slash;
+       int trailing_slash = 0;
+       int dp_authorized = 0;
        int error = 0;
        struct componentname *cnp = &ndp->ni_cnd;
-       struct proc *p = cnp->cn_proc;
-       int i;
+       vfs_context_t ctx = cnp->cn_context;
 
        /*
         * Setup: break out flag bits into variables.
         */
+       if (cnp->cn_flags & (NOCACHE | DOWHITEOUT)) {
+               if ((cnp->cn_flags & NOCACHE) || (cnp->cn_nameiop == DELETE))
+                       docache = 0;
+       }
        wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
-       docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
-       if (cnp->cn_nameiop == DELETE ||
-           (wantparent && cnp->cn_nameiop != CREATE &&
-             cnp->cn_nameiop != LOOKUP))
-               docache = 0;
        rdonly = cnp->cn_flags & RDONLY;
-       ndp->ni_dvp = NULL;
        cnp->cn_flags &= ~ISSYMLINK;
+       cnp->cn_consume = 0;
+
        dp = ndp->ni_startdir;
        ndp->ni_startdir = NULLVP;
-       vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
 
-dirloop:
-       /*
-        * Search a new directory.
-        *
-        * The cn_hash value is for use by vfs_cache.
-        * Check pathconf for maximun length of name
-        * The last component of the filename is left accessible via
-        * cnp->cn_nameptr for callers that need the name. Callers needing
-        * the name set the SAVENAME flag. When done, they assume
-        * responsibility for freeing the pathname buffer.
-        */
-       cnp->cn_consume = 0;
-       cnp->cn_hash = 0;
-       for (cp = cnp->cn_nameptr, i=1; *cp != 0 && *cp != '/'; i++, cp++)
-               cnp->cn_hash += (unsigned char)*cp * i;
-       cnp->cn_namelen = cp - cnp->cn_nameptr;
-    if (VOP_PATHCONF(dp, _PC_NAME_MAX, &namemax))
-               namemax = NAME_MAX;
-    if (cnp->cn_namelen > namemax) {
-               error = ENAMETOOLONG;
-               goto bad;
-       }
-#ifdef NAMEI_DIAGNOSTIC
-       { char c = *cp;
-       *cp = '\0';
-       printf("{%s}: ", cnp->cn_nameptr);
-       *cp = c; }
-#endif
-       ndp->ni_pathlen -= cnp->cn_namelen;
-       ndp->ni_next = cp;
+       cp = cnp->cn_nameptr;
 
-       /*
-        * Replace multiple slashes by a single slash and trailing slashes
-        * by a null.  This must be done before VOP_LOOKUP() because some
-        * fs's don't know about trailing slashes.  Remember if there were
-        * trailing slashes to handle symlinks, existing non-directories
-        * and non-existing files that won't be directories specially later.
-        */
-       trailing_slash = 0;
-       while (*cp == '/' && (cp[1] == '/' || cp[1] == '\0')) {
-               cp++;
-               ndp->ni_pathlen--;
-               if (*cp == '\0') {
-                       trailing_slash = 1;
-                       *ndp->ni_next = '\0';
+       if (*cp == '\0') {
+               if ( (vnode_getwithref(dp)) ) {
+                       dp = NULLVP;
+                       error = ENOENT;
+                       goto bad;
                }
+               goto emptyname;
        }
-       ndp->ni_next = cp;
-
-       cnp->cn_flags |= MAKEENTRY;
-       if (*cp == '\0' && docache == 0)
-               cnp->cn_flags &= ~MAKEENTRY;
-       if (cnp->cn_namelen == 2 &&
-           cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
-               cnp->cn_flags |= ISDOTDOT;
-       else
-               cnp->cn_flags &= ~ISDOTDOT;
-       if (*ndp->ni_next == 0)
-               cnp->cn_flags |= ISLASTCN;
-       else
-               cnp->cn_flags &= ~ISLASTCN;
+dirloop: 
+       ndp->ni_vp = NULLVP;
 
-
-       /*
-        * Check for degenerate name (e.g. / or "")
-        * which is a way of talking about a directory,
-        * e.g. like "/." or ".".
-        */
-       if (cnp->cn_nameptr[0] == '\0') {
-               if (dp->v_type != VDIR) {
-                       error = ENOTDIR;
-                       goto bad;
-               }
-               if (cnp->cn_nameiop != LOOKUP) {
-                       error = EISDIR;
-                       goto bad;
-               }
-               if (wantparent) {
-                       ndp->ni_dvp = dp;
-                       VREF(dp);
-               }
-               ndp->ni_vp = dp;
-               if (!(cnp->cn_flags & (LOCKPARENT | LOCKLEAF)))
-                       VOP_UNLOCK(dp, 0, p);
-               if (cnp->cn_flags & SAVESTART)
-                       panic("lookup: SAVESTART");
-               return (0);
+       if ( (error = cache_lookup_path(ndp, cnp, dp, ctx, &trailing_slash, &dp_authorized)) ) {
+               dp = NULLVP;
+               goto bad;
+       }
+       if ((cnp->cn_flags & ISLASTCN)) {
+               if (docache)
+                       cnp->cn_flags |= MAKEENTRY;
+       } else
+               cnp->cn_flags |= MAKEENTRY;
+
+       dp = ndp->ni_dvp;
+
+       if (ndp->ni_vp != NULLVP) {
+               /*
+                * cache_lookup_path returned a non-NULL ni_vp then,
+                * we're guaranteed that the dp is a VDIR, it's 
+                * been authorized, and vp is not ".."
+                */
+               goto returned_from_lookup_path;
        }
 
        /*
@@ -418,27 +449,44 @@ dirloop:
         *    vnode which was mounted on so we take the
         *    .. in the other file system.
         */
-       if (cnp->cn_flags & ISDOTDOT) {
+       if ( (cnp->cn_flags & ISDOTDOT) ) {
                for (;;) {
-                       if (dp == ndp->ni_rootdir || dp == rootvnode) {
-                               ndp->ni_dvp = dp;
+                       if (dp == ndp->ni_rootdir || dp == rootvnode) {
+                               ndp->ni_dvp = dp;
                                ndp->ni_vp = dp;
-                               VREF(dp);
+                               /*
+                                * we're pinned at the root
+                                * we've already got one reference on 'dp'
+                                * courtesy of cache_lookup_path... take
+                                * another one for the ".."
+                                * if we fail to get the new reference, we'll
+                                * drop our original down in 'bad'
+                                */
+                               if ( (vnode_get(dp)) ) {
+                                       error = ENOENT;
+                                       goto bad;
+                               }
                                goto nextname;
                        }
                        if ((dp->v_flag & VROOT) == 0 ||
                            (cnp->cn_flags & NOCROSSMOUNT))
-                               break;
+                               break;
                        if (dp->v_mount == NULL) {      /* forced umount */
-                               error = EBADF;
+                               error = EBADF;
                                goto bad;
                        }
-
                        tdp = dp;
-                       dp = dp->v_mount->mnt_vnodecovered;
-                       vput(tdp);
-                       VREF(dp);
-                       vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
+                       dp = tdp->v_mount->mnt_vnodecovered;
+
+                       vnode_put(tdp);
+
+                       if ( (vnode_getwithref(dp)) ) {
+                               dp = NULLVP;
+                               error = ENOENT;
+                               goto bad;
+                       }
+                       ndp->ni_dvp = dp;
+                       dp_authorized = 0;
                }
        }
 
@@ -446,29 +494,46 @@ dirloop:
         * We now have a segment name to search for, and a directory to search.
         */
 unionlookup:
-       ndp->ni_dvp = dp;
-       ndp->ni_vp = NULL;
-       if (error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) {
-#if DIAGNOSTIC
-               if (ndp->ni_vp != NULL)
-                       panic("leaf should be empty");
-#endif
-#ifdef NAMEI_DIAGNOSTIC
-               printf("not found\n");
-#endif
+       ndp->ni_vp = NULLVP;
+
+       if (dp->v_type != VDIR) {
+               error = ENOTDIR;
+               goto lookup_error;
+       }
+       if ( !(dp_authorized || (cnp->cn_flags & DONOTAUTH)) ) {
+               if ( (error = vnode_authorize(dp, NULL, KAUTH_VNODE_SEARCH, ctx)) )
+                       goto lookup_error;
+       }
+       if ( (error = VNOP_LOOKUP(dp, &ndp->ni_vp, cnp, ctx)) ) {
+lookup_error:
                if ((error == ENOENT) &&
                    (dp->v_flag & VROOT) && (dp->v_mount != NULL) &&
                    (dp->v_mount->mnt_flag & MNT_UNION)) {
+                       if ((cnp->cn_flags & FSNODELOCKHELD)) {
+                               cnp->cn_flags &= ~FSNODELOCKHELD;
+                               unlock_fsnode(dp, NULL);
+                       }       
                        tdp = dp;
-                       dp = dp->v_mount->mnt_vnodecovered;
-                       vput(tdp);
-                       VREF(dp);
-                       vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
+                       dp = tdp->v_mount->mnt_vnodecovered;
+
+                       vnode_put(tdp);
+
+                       if ( (vnode_getwithref(dp)) ) {
+                               dp = NULLVP;
+                               error = ENOENT;
+                               goto bad;
+                       }
+                       ndp->ni_dvp = dp;
+                       dp_authorized = 0;
                        goto unionlookup;
                }
 
                if (error != EJUSTRETURN)
                        goto bad;
+
+               if (ndp->ni_vp != NULLVP)
+                       panic("leaf should be empty");
+
                /*
                 * If creating and at end of pathname, then can consider
                 * allowing file to be created.
@@ -477,27 +542,31 @@ unionlookup:
                        error = EROFS;
                        goto bad;
                }
-               if (*cp == '\0' && trailing_slash &&
-                       !(cnp->cn_flags & WILLBEDIR)) {
+               if ((cnp->cn_flags & ISLASTCN) && trailing_slash && !(cnp->cn_flags & WILLBEDIR)) {
                        error = ENOENT;
                        goto bad;
                }
                /*
                 * We return with ni_vp NULL to indicate that the entry
                 * doesn't currently exist, leaving a pointer to the
-                * (possibly locked) directory inode in ndp->ni_dvp.
+                * referenced directory vnode in ndp->ni_dvp.
                 */
                if (cnp->cn_flags & SAVESTART) {
+                       if ( (vnode_get(ndp->ni_dvp)) ) {
+                               error = ENOENT;
+                               goto bad;
+                       }
                        ndp->ni_startdir = ndp->ni_dvp;
-                       VREF(ndp->ni_startdir);
                }
+               if (!wantparent)
+                       vnode_put(ndp->ni_dvp);
+
                if (kdebug_enable)
                        kdebug_lookup(ndp->ni_dvp, cnp);
                return (0);
        }
-#ifdef NAMEI_DIAGNOSTIC
-       printf("found\n");
-#endif
+returned_from_lookup_path:
+       dp = ndp->ni_vp;
 
        /*
         * Take into account any additional components consumed by
@@ -508,34 +577,82 @@ unionlookup:
                ndp->ni_next += cnp->cn_consume;
                ndp->ni_pathlen -= cnp->cn_consume;
                cnp->cn_consume = 0;
+       } else {
+               if (dp->v_name == NULL || dp->v_parent == NULLVP) {
+                       int isdot_or_dotdot;
+                       int  update_flags = 0;
+
+                       isdot_or_dotdot = (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') || (cnp->cn_flags & ISDOTDOT);
+           
+                       if (isdot_or_dotdot == 0) {
+                               if (dp->v_name == NULL)
+                                       update_flags |= VNODE_UPDATE_NAME;
+                               if (ndp->ni_dvp != NULLVP && dp->v_parent == NULLVP)
+                                       update_flags |= VNODE_UPDATE_PARENT;
+
+                               if (update_flags)
+                                       vnode_update_identity(dp, ndp->ni_dvp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_hash, update_flags);
+                       }
+               }
+
+               if ( (cnp->cn_flags & MAKEENTRY) && (dp->v_flag & VNCACHEABLE) && LIST_FIRST(&dp->v_nclinks) == NULL) {
+                       /*
+                        * missing from name cache, but should
+                        * be in it... this can happen if volfs
+                        * causes the vnode to be created or the
+                        * name cache entry got recycled but the
+                        * vnode didn't...
+                        * check to make sure that ni_dvp is valid
+                        * cache_lookup_path may return a NULL
+                        */
+                       if (ndp->ni_dvp != NULL)
+                               cache_enter(ndp->ni_dvp, dp, cnp);
+               }
        }
 
-       dp = ndp->ni_vp;
        /*
-        * Check to see if the vnode has been mounted on;
+        * Check to see if the vnode has been mounted on...
         * if so find the root of the mounted file system.
         */
-       while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
-              (cnp->cn_flags & NOCROSSMOUNT) == 0) {
-               if (vfs_busy(mp, 0, 0, p))
-                       continue;
-               VOP_UNLOCK(dp, 0, p);
-               error = VFS_ROOT(mp, &tdp);
-               vfs_unbusy(mp, p);
-               if (error) {
-                       dp_unlocked = 1;                /* Signal error path 'dp' has already been unlocked */
-                       goto bad2;
-               };
-               vrele(dp);
-               ndp->ni_vp = dp = tdp;
+check_mounted_on:
+       if ((dp->v_type == VDIR) && dp->v_mountedhere &&
+            ((cnp->cn_flags & NOCROSSMOUNT) == 0)) {
+         
+               vnode_lock(dp);
+
+               if ((dp->v_type == VDIR) && (mp = dp->v_mountedhere)) {
+
+                       mp->mnt_crossref++;
+                       vnode_unlock(dp);
+
+                       if (vfs_busy(mp, 0)) {
+                               mount_dropcrossref(mp, dp, 0);
+                               goto check_mounted_on;
+                       }
+                       error = VFS_ROOT(mp, &tdp, ctx);
+                       /*
+                        * mount_dropcrossref does a vnode_put
+                        * on dp if the 3rd arg is non-zero
+                        */
+                       mount_dropcrossref(mp, dp, 1);
+                       dp = NULL;
+                       vfs_unbusy(mp);
+
+                       if (error) {
+                               goto bad2;
+                       }
+                       ndp->ni_vp = dp = tdp;
+                       
+                       goto check_mounted_on;
+               } 
+               vnode_unlock(dp);
        }
 
        /*
         * Check for symbolic link
         */
        if ((dp->v_type == VLNK) &&
-           ((cnp->cn_flags & FOLLOW) || trailing_slash ||
-               *ndp->ni_next == '/')) {
+           ((cnp->cn_flags & FOLLOW) || trailing_slash || *ndp->ni_next == '/')) {
                cnp->cn_flags |= ISSYMLINK;
                return (0);
        }
@@ -543,10 +660,13 @@ unionlookup:
        /*
         * Check for bogus trailing slashes.
         */
-        if (trailing_slash && dp->v_type != VDIR) {
-               error = ENOTDIR;
-               goto bad2;
-        }
+       if (trailing_slash) {
+               if (dp->v_type != VDIR) {
+                       error = ENOTDIR;
+                       goto bad2;
+               }
+               trailing_slash = 0;
+       }
 
 nextname:
        /*
@@ -554,12 +674,20 @@ nextname:
         * continue at next component, else return.
         */
        if (*ndp->ni_next == '/') {
-               cnp->cn_nameptr = ndp->ni_next;
+               cnp->cn_nameptr = ndp->ni_next + 1;
+               ndp->ni_pathlen--;
                while (*cnp->cn_nameptr == '/') {
                        cnp->cn_nameptr++;
                        ndp->ni_pathlen--;
                }
-               vrele(ndp->ni_dvp);
+               vnode_put(ndp->ni_dvp);
+
+               cp = cnp->cn_nameptr;
+
+               if (*cp == '\0')
+                       goto emptyname;
+
+               vnode_put(dp);
                goto dirloop;
        }
                                  
@@ -572,28 +700,94 @@ nextname:
                goto bad2;
        }
        if (cnp->cn_flags & SAVESTART) {
+               /*      
+                * note that we already hold a reference
+                * on both dp and ni_dvp, but for some reason
+                * can't get another one... in this case we
+                * need to do vnode_put on dp in 'bad2'
+                */
+               if ( (vnode_get(ndp->ni_dvp)) ) {
+                       error = ENOENT;
+                       goto bad2;
+               }
                ndp->ni_startdir = ndp->ni_dvp;
-               VREF(ndp->ni_startdir);
        }
-       if (!wantparent)
-               vrele(ndp->ni_dvp);
-       if ((cnp->cn_flags & LOCKLEAF) == 0)
-               VOP_UNLOCK(dp, 0, p);
+       if (!wantparent && ndp->ni_dvp)
+               vnode_put(ndp->ni_dvp);
+
+       if (cnp->cn_flags & AUDITVNPATH1)
+               AUDIT_ARG(vnpath, dp, ARG_VNODE1);
+       else if (cnp->cn_flags & AUDITVNPATH2)
+               AUDIT_ARG(vnpath, dp, ARG_VNODE2);
+
        if (kdebug_enable)
                kdebug_lookup(dp, cnp);
        return (0);
 
+emptyname:
+       cnp->cn_namelen = 0;
+       /*
+        * A degenerate name (e.g. / or "") which is a way of
+        * talking about a directory, e.g. like "/." or ".".
+        */
+       if (dp->v_type != VDIR) {
+               error = ENOTDIR;
+               goto bad;
+       }
+       if (cnp->cn_nameiop != LOOKUP) {
+               error = EISDIR;
+               goto bad;
+       }
+       if (wantparent) {
+               /*      
+                * note that we already hold a reference
+                * on dp, but for some reason can't
+                * get another one... in this case we
+                * need to do vnode_put on dp in 'bad'
+                */
+               if ( (vnode_get(dp)) ) {
+                       error = ENOENT;
+                       goto bad;
+               }
+               ndp->ni_dvp = dp;
+       }
+       cnp->cn_flags &= ~ISDOTDOT;
+       cnp->cn_flags |= ISLASTCN;
+       ndp->ni_next = cp;
+       ndp->ni_vp = dp;
+
+       if (cnp->cn_flags & AUDITVNPATH1)
+               AUDIT_ARG(vnpath, dp, ARG_VNODE1);
+       else if (cnp->cn_flags & AUDITVNPATH2)
+               AUDIT_ARG(vnpath, dp, ARG_VNODE2);
+       if (cnp->cn_flags & SAVESTART)
+               panic("lookup: SAVESTART");
+       return (0);
+
 bad2:
-       if ((cnp->cn_flags & LOCKPARENT) && *ndp->ni_next == '\0')
-               VOP_UNLOCK(ndp->ni_dvp, 0, p);
-       vrele(ndp->ni_dvp);
+       if ((cnp->cn_flags & FSNODELOCKHELD)) {
+               cnp->cn_flags &= ~FSNODELOCKHELD;
+               unlock_fsnode(ndp->ni_dvp, NULL);
+       }
+       if (ndp->ni_dvp)
+               vnode_put(ndp->ni_dvp);
+       if (dp)
+               vnode_put(dp);
+       ndp->ni_vp = NULLVP;
+
+       if (kdebug_enable)
+               kdebug_lookup(dp, cnp);
+       return (error);
+
 bad:
-       if (dp_unlocked) {
-               vrele(dp);
-       } else {
-               vput(dp);
-       };
-       ndp->ni_vp = NULL;
+       if ((cnp->cn_flags & FSNODELOCKHELD)) {
+               cnp->cn_flags &= ~FSNODELOCKHELD;
+               unlock_fsnode(ndp->ni_dvp, NULL);
+       }       
+       if (dp)
+               vnode_put(dp);
+       ndp->ni_vp = NULLVP;
+
        if (kdebug_enable)
                kdebug_lookup(dp, cnp);
        return (error);
@@ -608,51 +802,29 @@ relookup(dvp, vpp, cnp)
        struct vnode *dvp, **vpp;
        struct componentname *cnp;
 {
-       struct proc *p = cnp->cn_proc;
        struct vnode *dp = 0;           /* the directory we are searching */
-       int docache;                    /* == 0 do not cache last component */
        int wantparent;                 /* 1 => wantparent or lockparent flag */
        int rdonly;                     /* lookup read-only flag bit */
        int error = 0;
 #ifdef NAMEI_DIAGNOSTIC
-       int newhash;                    /* DEBUG: check name hash */
+       int i, newhash;                 /* DEBUG: check name hash */
        char *cp;                       /* DEBUG: check name ptr/len */
 #endif
+       vfs_context_t ctx = cnp->cn_context;;
 
        /*
         * Setup: break out flag bits into variables.
         */
        wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT);
-       docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
-       if (cnp->cn_nameiop == DELETE ||
-           (wantparent && cnp->cn_nameiop != CREATE))
-               docache = 0;
        rdonly = cnp->cn_flags & RDONLY;
        cnp->cn_flags &= ~ISSYMLINK;
-       dp = dvp;
-       vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
 
-/* dirloop: */
-       /*
-        * Search a new directory.
-        *
-        * The cn_hash value is for use by vfs_cache.
-        * The last component of the filename is left accessible via
-        * cnp->cn_nameptr for callers that need the name. Callers needing
-        * the name set the SAVENAME flag. When done, they assume
-        * responsibility for freeing the pathname buffer.
-        */
-#ifdef NAMEI_DIAGNOSTIC
-       for (newhash = 0, cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++)
-               newhash += (unsigned char)*cp;
-       if (newhash != cnp->cn_hash)
-               panic("relookup: bad hash");
-       if (cnp->cn_namelen != cp - cnp->cn_nameptr)
-               panic ("relookup: bad len");
-       if (*cp != 0)
-               panic("relookup: not last component");
-       printf("{%s}: ", cnp->cn_nameptr);
-#endif
+       if (cnp->cn_flags & NOCACHE)
+               cnp->cn_flags &= ~MAKEENTRY;
+       else
+               cnp->cn_flags |= MAKEENTRY;
+
+       dp = dvp;
 
        /*
         * Check for degenerate name (e.g. / or "")
@@ -668,27 +840,26 @@ relookup(dvp, vpp, cnp)
                        error = ENOTDIR;
                        goto bad;
                }
-               if (!(cnp->cn_flags & LOCKLEAF))
-                       VOP_UNLOCK(dp, 0, p);
+               if ( (vnode_get(dp)) ) {
+                       error = ENOENT;
+                       goto bad;
+               }
                *vpp = dp;
+
                if (cnp->cn_flags & SAVESTART)
                        panic("lookup: SAVESTART");
                return (0);
        }
-
-       if (cnp->cn_flags & ISDOTDOT)
-               panic ("relookup: lookup on dot-dot");
-
        /*
         * We now have a segment name to search for, and a directory to search.
         */
-       if (error = VOP_LOOKUP(dp, vpp, cnp)) {
+       if ( (error = VNOP_LOOKUP(dp, vpp, cnp, ctx)) ) {
+               if (error != EJUSTRETURN)
+                       goto bad;
 #if DIAGNOSTIC
                if (*vpp != NULL)
                        panic("leaf should be empty");
 #endif
-               if (error != EJUSTRETURN)
-                       goto bad;
                /*
                 * If creating and at end of pathname, then can consider
                 * allowing file to be created.
@@ -697,9 +868,6 @@ relookup(dvp, vpp, cnp)
                        error = EROFS;
                        goto bad;
                }
-               /* ASSERT(dvp == ndp->ni_startdir) */
-               if (cnp->cn_flags & SAVESTART)
-                       VREF(dvp);
                /*
                 * We return with ni_vp NULL to indicate that the entry
                 * doesn't currently exist, leaving a pointer to the
@@ -726,33 +894,45 @@ relookup(dvp, vpp, cnp)
                goto bad2;
        }
        /* ASSERT(dvp == ndp->ni_startdir) */
-       if (cnp->cn_flags & SAVESTART)
-               VREF(dvp);
        
-       if (!wantparent)
-               vrele(dvp);
-       if ((cnp->cn_flags & LOCKLEAF) == 0)
-               VOP_UNLOCK(dp, 0, p);
        return (0);
 
 bad2:
-       if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN))
-               VOP_UNLOCK(dvp, 0, p);
-       vrele(dvp);
-bad:
-       vput(dp);
+       vnode_put(dp);
+bad:   
        *vpp = NULL;
+
        return (error);
 }
 
+/*
+ * Free pathname buffer
+ */
+void
+nameidone(struct nameidata *ndp)
+{
+       if ((ndp->ni_cnd.cn_flags & FSNODELOCKHELD)) {
+               ndp->ni_cnd.cn_flags &= ~FSNODELOCKHELD;
+               unlock_fsnode(ndp->ni_dvp, NULL);
+       }       
+       if (ndp->ni_cnd.cn_flags & HASBUF) {
+               char *tmp = ndp->ni_cnd.cn_pnbuf;
+
+               ndp->ni_cnd.cn_pnbuf = NULL;
+               ndp->ni_cnd.cn_flags &= ~HASBUF;
+               FREE_ZONE(tmp, ndp->ni_cnd.cn_pnlen, M_NAMEI);
+       }
+}
+
 
 #define NUMPARMS 23
 
+static void
 kdebug_lookup(dp, cnp)
-        struct vnode *dp;
+       struct vnode *dp;
        struct componentname *cnp;
 {
-        register int i, n;
+       register unsigned int i, n;
        register int dbg_namelen;
        register int save_dbg_namelen;
        register char *dbg_nameptr;
@@ -802,7 +982,7 @@ kdebug_lookup(dp, cnp)
          entries, we must mark the start of the path's string.
        */
        KERNEL_DEBUG_CONSTANT((FSDBG_CODE(DBG_FSRW,36)) | DBG_FUNC_START,
-                             dp, dbg_parms[0], dbg_parms[1], dbg_parms[2], 0);
+               (unsigned int)dp, dbg_parms[0], dbg_parms[1], dbg_parms[2], 0);
 
        for (dbg_namelen = save_dbg_namelen-12, i=3;
             dbg_namelen > 0;