X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..3a60a9f5b85abb8c2cf24e1926c5c7b3f608a5e2:/bsd/vfs/vfs_lookup.c?ds=sidebyside diff --git a/bsd/vfs/vfs_lookup.c b/bsd/vfs/vfs_lookup.c index f7eb1ca2a..d10ba8fd3 100644 --- a/bsd/vfs/vfs_lookup.c +++ b/bsd/vfs/vfs_lookup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -61,23 +61,31 @@ */ #include +#include #include #include #include #include -#include -#include +#include +#include #include #include #include -#include +#include #include #include /* For _PC_NAME_MAX */ +#include +#include + +#include #if KTRACE #include #endif + +static void kdebug_lookup(struct vnode *dp, struct componentname *cnp); + /* * Convert a pathname into a pointer to a locked inode. * @@ -98,6 +106,7 @@ * if symbolic link, massage name in buffer and continue * } */ + int namei(ndp) register struct nameidata *ndp; @@ -105,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; + } + } } - FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI); - vrele(ndp->ni_dvp); - vput(ndp->ni_vp); - ndp->ni_vp = NULL; + /* + * 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; + ndp->ni_vp = NULLVP; + return (error); } + /* * Search a pathname. * This is a very central and rather complicated routine. @@ -271,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, @@ -285,103 +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 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); - -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; - 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; + cp = cnp->cn_nameptr; - /* - * 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; + if (*cp == '\0') { + if ( (vnode_getwithref(dp)) ) { + dp = NULLVP; + error = ENOENT; 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); + goto emptyname; + } +dirloop: + ndp->ni_vp = NULLVP; + + 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; } /* @@ -394,22 +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; + 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; } } @@ -417,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_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. @@ -448,22 +542,31 @@ unionlookup: error = EROFS; goto bad; } + 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 @@ -474,46 +577,117 @@ 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; - error = VFS_ROOT(mp, &tdp); - vfs_unbusy(mp, p); - if (error) - goto bad2; - vput(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) || *ndp->ni_next == '/')) { + ((cnp->cn_flags & FOLLOW) || trailing_slash || *ndp->ni_next == '/')) { cnp->cn_flags |= ISSYMLINK; return (0); } + /* + * Check for bogus trailing slashes. + */ + if (trailing_slash) { + if (dp->v_type != VDIR) { + error = ENOTDIR; + goto bad2; + } + trailing_slash = 0; + } + nextname: /* * Not a symbolic link. If more pathname, * 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; } @@ -526,24 +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: - 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); @@ -558,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 "") @@ -618,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. @@ -647,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 @@ -676,34 +894,47 @@ 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 7 +#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; long dbg_parms[NUMPARMS]; char dbg_buf[4]; @@ -716,6 +947,7 @@ kdebug_lookup(dp, cnp) if (dbg_namelen > sizeof(dbg_parms)) dbg_namelen = sizeof(dbg_parms); dbg_nameptr -= dbg_namelen; + save_dbg_namelen = dbg_namelen; i = 0; @@ -744,8 +976,19 @@ kdebug_lookup(dp, cnp) else dbg_parms[i++] = 0; } - KERNEL_DEBUG_CONSTANT((FSDBG_CODE(DBG_FSRW,36)) | DBG_FUNC_NONE, - dp, dbg_parms[0], dbg_parms[1], dbg_parms[2], 0); - KERNEL_DEBUG_CONSTANT((FSDBG_CODE(DBG_FSRW,36)) | DBG_FUNC_NONE, - dbg_parms[3], dbg_parms[4], dbg_parms[5], dbg_parms[6], 0); + + /* + In the event that we collect multiple, consecutive pathname + entries, we must mark the start of the path's string. + */ + KERNEL_DEBUG_CONSTANT((FSDBG_CODE(DBG_FSRW,36)) | DBG_FUNC_START, + (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; + dbg_namelen -=(4 * sizeof(long)), i+= 4) + { + KERNEL_DEBUG_CONSTANT((FSDBG_CODE(DBG_FSRW,36)) | DBG_FUNC_NONE, + dbg_parms[i], dbg_parms[i+1], dbg_parms[i+2], dbg_parms[i+3], 0); + } }