X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/e5568f75972dfc723778653c11cb6b4dc825716a..8ad349bb6ed4a0be06e34c92be0d98b92e078db4:/bsd/vfs/vfs_lookup.c diff --git a/bsd/vfs/vfs_lookup.c b/bsd/vfs/vfs_lookup.c index d65c10b90..242691065 100644 --- a/bsd/vfs/vfs_lookup.c +++ b/bsd/vfs/vfs_lookup.c @@ -1,23 +1,31 @@ /* - * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved. * - * 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 OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * @APPLE_LICENSE_OSREFERENCE_HEADER_START@ * - * @APPLE_LICENSE_HEADER_END@ + * 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 + * limitations under the License. + * + * @APPLE_LICENSE_OSREFERENCE_HEADER_END@ */ /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ /* @@ -66,14 +74,16 @@ #include #include #include -#include -#include +#include +#include #include #include #include -#include +#include #include #include /* For _PC_NAME_MAX */ +#include +#include #include @@ -81,7 +91,8 @@ #include #endif -static void kdebug_lookup(struct vnode *dp, struct componentname *cnp); + +static void kdebug_lookup(struct vnode *dp, struct componentname *cnp); /* * Convert a pathname into a pointer to a locked inode. @@ -103,6 +114,7 @@ static void kdebug_lookup(struct vnode *dp, struct componentname *cnp); * if symbolic link, massage name in buffer and continue * } */ + int namei(ndp) register struct nameidata *ndp; @@ -110,16 +122,16 @@ 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"); @@ -133,17 +145,34 @@ namei(ndp) * 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_flags |= HASBUF; + 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, (size_t *)&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, (size_t *)&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) @@ -154,19 +183,9 @@ namei(ndp) /* * Do not allow empty pathnames */ - if (!error && *cnp->cn_pnbuf == '\0') + if (*cnp->cn_pnbuf == '\0') { error = ENOENT; - - if (!error && ((dp = fdp->fd_cdir) == NULL)) - error = EPERM; /* 3382843 */ - - if (error) { - tmppn = cnp->cn_pnbuf; - cnp->cn_pnbuf = NULL; - cnp->cn_flags &= ~HASBUF; - FREE_ZONE(tmppn, cnp->cn_pnlen, M_NAMEI); - ndp->ni_vp = NULL; - return (error); + goto error_out; } ndp->ni_loopcnt = 0; #if KTRACE @@ -175,113 +194,155 @@ namei(ndp) #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; - if (ndp->ni_cnd.cn_flags & USEDVP) { - dp = ndp->ni_dvp; - ndp->ni_dvp = NULL; - } else { - dp = fdp->fd_cdir; + if ((ndp->ni_rootdir = fdp->fd_rdir) == NULLVP) { + if ( !(fdp->fd_flags & FD_CHROOT)) + ndp->ni_rootdir = rootvnode; } + cnp->cn_nameptr = cnp->cn_pnbuf; - 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); + 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)) { - long len = cnp->cn_pnlen; - tmppn = cnp->cn_pnbuf; - cnp->cn_pnbuf = NULL; - cnp->cn_flags &= ~HASBUF; - FREE_ZONE(tmppn, len, 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) { - tmppn = cnp->cn_pnbuf; - cnp->cn_pnbuf = NULL; - cnp->cn_flags &= ~HASBUF; - FREE_ZONE(tmppn, 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) + 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) + // 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); cnp->cn_pnbuf = cp; cnp->cn_pnlen = MAXPATHLEN; - FREE_ZONE(tmppn, len, M_NAMEI); + + 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; - tmppn = cnp->cn_pnbuf; + /* + * 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); + } cnp->cn_pnbuf = NULL; - cnp->cn_flags &= ~HASBUF; - FREE_ZONE(tmppn, cnp->cn_pnlen, M_NAMEI); + ndp->ni_vp = NULLVP; - vrele(ndp->ni_dvp); - vput(ndp->ni_vp); - ndp->ni_vp = NULL; return (error); } + /* * Search a pathname. * This is a very central and rather complicated routine. @@ -310,7 +371,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, @@ -324,111 +385,67 @@ 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 = 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); - cnp->cn_consume = 0; -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. - */ - { - register unsigned int hash; - register unsigned int ch; - register int i; - - hash = 0; - cp = cnp->cn_nameptr; - ch = *cp; - if (ch == '\0') { - cnp->cn_namelen = 0; - goto emptyname; - } + cp = cnp->cn_nameptr; - for (i = 1; (ch != '/') && (ch != '\0'); i++) { - hash += ch * i; - ch = *(++cp); - } - cnp->cn_hash = hash; - } - cnp->cn_namelen = cp - cnp->cn_nameptr; - if (cnp->cn_namelen > NCHNAMLEN) { - if (VOP_PATHCONF(dp, _PC_NAME_MAX, &namemax)) - namemax = NAME_MAX; - if (cnp->cn_namelen > namemax) { - error = ENAMETOOLONG; + if (*cp == '\0') { + if ( (vnode_getwithref(dp)) ) { + dp = NULLVP; + error = ENOENT; goto bad; } + goto emptyname; } -#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; +dirloop: + ndp->ni_vp = NULLVP; - /* - * 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 ( (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; } - ndp->ni_next = cp; - - cnp->cn_flags |= MAKEENTRY; - if (*cp == '\0' && docache == 0) - cnp->cn_flags &= ~MAKEENTRY; - - if (*ndp->ni_next == 0) - cnp->cn_flags |= ISLASTCN; - else - cnp->cn_flags &= ~ISLASTCN; /* * Handle "..": two special cases. @@ -440,62 +457,91 @@ dirloop: * vnode which was mounted on so we take the * .. in the other file system. */ - if (cnp->cn_namelen == 2 && - cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.') { - 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; } - } else { - cnp->cn_flags &= ~ISDOTDOT; } /* * 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. @@ -504,27 +550,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 @@ -536,48 +586,81 @@ unionlookup: ndp->ni_pathlen -= cnp->cn_consume; cnp->cn_consume = 0; } else { - int isdot_or_dotdot; + 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); + isdot_or_dotdot = (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') || (cnp->cn_flags & ISDOTDOT); - if (VNAME(ndp->ni_vp) == NULL && isdot_or_dotdot == 0) { - VNAME(ndp->ni_vp) = add_name(cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_hash, 0); - } - if (VPARENT(ndp->ni_vp) == NULL && isdot_or_dotdot == 0) { - if (vget(ndp->ni_dvp, 0, p) == 0) { - VPARENT(ndp->ni_vp) = ndp->ni_dvp; + 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, LK_NOWAIT, 0, p)) { - error = ENOENT; - goto bad2; - } - 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); } @@ -591,7 +674,7 @@ unionlookup: goto bad2; } trailing_slash = 0; - } + } nextname: /* @@ -605,7 +688,14 @@ nextname: 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; } @@ -618,22 +708,32 @@ 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 (!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 ((cnp->cn_flags & LOCKLEAF) == 0) - VOP_UNLOCK(dp, 0, p); + 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 ".". @@ -647,34 +747,55 @@ emptyname: 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; - VREF(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 & (LOCKPARENT | LOCKLEAF))) - VOP_UNLOCK(dp, 0, p); 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); @@ -689,9 +810,7 @@ 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; @@ -699,41 +818,21 @@ relookup(dvp, vpp, cnp) 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 (i=1, newhash = 0, cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++) - newhash += (unsigned char)*cp * i; - 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 "") @@ -749,27 +848,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. @@ -778,9 +876,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 @@ -807,25 +902,36 @@ 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 @@ -834,7 +940,7 @@ kdebug_lookup(dp, cnp) 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;