--- src/sys/kern/vfs_lookup.c 2004/10/22 18:03:50 1.20 +++ src/sys/kern/vfs_lookup.c 2004/11/12 00:09:24 1.21 @@ -63,639 +63,56 @@ SYSCTL_INT(_vfs, OID_AUTO, varsym_enable "Enable Variant Symlinks"); /* - * Convert a pathname into a pointer to a locked inode. + * OLD API FUNCTION * - * The CNP_FOLLOW flag is set when symbolic links are to be followed - * when they occur at the end of the name translation process. - * Symbolic links are always followed for all other pathname - * components other than the last. - * - * The segflg defines whether the name is to be copied from user - * space or kernel space. - * - * Overall outline of namei: - * - * copy in name - * get starting directory - * while (!done && !error) { - * call lookup to search path. - * if symbolic link, massage name in buffer and continue - * } - * - * NOTE: when namei() is called from a pure thread the system rootvnode - * will be used as a basis for the search. - */ -int -namei(struct nameidata *ndp) -{ - struct filedesc *fdp; /* pointer to file descriptor state */ - char *cp; /* pointer into pathname argument */ - struct vnode *dp; /* the directory we are searching */ - struct iovec aiov; /* uio for reading symbolic links */ - struct uio auio; - int error, linklen; - struct componentname *cnp = &ndp->ni_cnd; - struct proc *p; - - KKASSERT(ndp->ni_cnd.cn_td != NULL); - p = cnp->cn_td->td_proc; - if (p == NULL) { - KKASSERT(ndp->ni_segflg == UIO_SYSSPACE); - printf("namei() from non-process\n"); - fdp = NULL; - } else { - KKASSERT(cnp->cn_cred == p->p_ucred); /* YYY */ - fdp = p->p_fd; - } - KASSERT(cnp->cn_cred, ("namei: bad cred/proc")); - KASSERT((cnp->cn_nameiop & (~NAMEI_OPMASK)) == 0, - ("namei: nameiop contaminated with flags")); - KASSERT((cnp->cn_flags & NAMEI_OPMASK) == 0, - ("namei: flags contaminated with nameiops")); - - /* - * Get a buffer for the name to be translated, and copy the - * name into the buffer. - */ - if ((cnp->cn_flags & CNP_HASBUF) == 0) - cnp->cn_pnbuf = zalloc(namei_zone); - if (ndp->ni_segflg == UIO_SYSSPACE) - error = copystr(ndp->ni_dirp, cnp->cn_pnbuf, - MAXPATHLEN, (size_t *)&ndp->ni_pathlen); - else - error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf, - MAXPATHLEN, (size_t *)&ndp->ni_pathlen); - - /* - * Don't allow empty pathnames. - * POSIX.1 requirement: "" is not a vaild file name. - */ - if (!error && *cnp->cn_pnbuf == '\0') - error = ENOENT; - - if (error) { - zfree(namei_zone, cnp->cn_pnbuf); - ndp->ni_vp = NULL; - return (error); - } - ndp->ni_loopcnt = 0; -#ifdef KTRACE - if (KTRPOINT(cnp->cn_td, KTR_NAMEI)) - ktrnamei(cnp->cn_td->td_proc->p_tracep, cnp->cn_pnbuf); -#endif - - /* - * Get starting point for the translation. - */ - if (fdp) { - ndp->ni_rootdir = fdp->fd_rdir; - ndp->ni_topdir = fdp->fd_jdir; - dp = fdp->fd_cdir; - } else { - ndp->ni_rootdir = rootvnode; - ndp->ni_topdir = rootvnode; - dp = rootvnode; - } - 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_startdir = dp; - error = lookup(ndp); - if (error) { - zfree(namei_zone, cnp->cn_pnbuf); - return (error); - } - /* - * Check for symbolic link - */ - if ((cnp->cn_flags & CNP_ISSYMLINK) == 0) { - if ((cnp->cn_flags & (CNP_SAVENAME | CNP_SAVESTART)) == 0) - zfree(namei_zone, cnp->cn_pnbuf); - else - cnp->cn_flags |= CNP_HASBUF; - - if (vn_canvmio(ndp->ni_vp) == TRUE && - (cnp->cn_nameiop != NAMEI_DELETE) && - ((cnp->cn_flags & (CNP_NOOBJ|CNP_LOCKLEAF)) == - CNP_LOCKLEAF)) - vfs_object_create(ndp->ni_vp, ndp->ni_cnd.cn_td); - - return (0); - } - if ((cnp->cn_flags & CNP_LOCKPARENT) && ndp->ni_pathlen == 1) - VOP_UNLOCK(ndp->ni_dvp, 0, cnp->cn_td); - if (ndp->ni_loopcnt++ >= MAXSYMLINKS) { - error = ELOOP; - break; - } - if (ndp->ni_pathlen > 1) - cp = zalloc(namei_zone); - 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_td = cnp->cn_td; - auio.uio_resid = MAXPATHLEN; - error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred); - if (error) { - if (ndp->ni_pathlen > 1) - zfree(namei_zone, cp); - break; - } - linklen = MAXPATHLEN - auio.uio_resid; - if (linklen == 0) { - if (ndp->ni_pathlen > 1) - zfree(namei_zone, cp); - error = ENOENT; - break; - } - if (varsym_enable) { - linklen = varsymreplace(cp, linklen, MAXPATHLEN); - if (linklen < 0) { - if (ndp->ni_pathlen > 1) - zfree(namei_zone, cp); - error = ENAMETOOLONG; - break; - } - } - if (linklen + ndp->ni_pathlen >= MAXPATHLEN) { - if (ndp->ni_pathlen > 1) - zfree(namei_zone, cp); - error = ENAMETOOLONG; - break; - } - if (ndp->ni_pathlen > 1) { - bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen); - zfree(namei_zone, cnp->cn_pnbuf); - cnp->cn_pnbuf = cp; - } else { - cnp->cn_pnbuf[linklen] = '\0'; - } - ndp->ni_pathlen += linklen; - vput(ndp->ni_vp); - dp = ndp->ni_dvp; - } - zfree(namei_zone, cnp->cn_pnbuf); - vrele(ndp->ni_dvp); - vput(ndp->ni_vp); - ndp->ni_vp = NULL; - return (error); -} - -/* - * Old API function, search a patchname. This is an *EXTREMELY* complicated - * function. - * - * The pathname is pointed to by ni_ptr and is of length ni_pathlen. - * The starting directory is taken from ni_startdir. The pathname is - * descended until done, or a symbolic link is encountered. The variable - * ni_more is clear if the path is completed; it is set to one if a - * symbolic link needing interpretation is encountered. - * - * The flag argument is NAMEI_LOOKUP, CREATE, RENAME, or DELETE depending on - * whether the name is to be looked up, created, renamed, or deleted. - * When CREATE, RENAME, or DELETE is specified, information usable in - * creating, renaming, or deleting a directory entry may be calculated. - * If flag has LOCKPARENT or'ed into it, the parent directory is returned - * locked. If flag has WANTPARENT or'ed into it, the parent directory is - * returned unlocked. Otherwise the parent directory is not returned. If - * the target of the pathname exists and LOCKLEAF is or'ed into the flag - * the target is returned locked, otherwise it is returned unlocked. - * When creating or renaming and LOCKPARENT is specified, the target may not - * be ".". When deleting and LOCKPARENT is specified, the target may be ".". - * - * SPECIAL CASE: When a symbolic link is encountered the parent - * directory is always returned in ni_dvp regardless of WANTPARENT|LOCKPARENT - * and the symbolic link is returned in ni_vp. If the symbolic link was not - * the last component ni_dvp will be returned UNLOCKED, regardless of - * LOCKPARENT. If the symbolic link is the last component then ni_dvp will - * be returned locked or unlocked based on LOCKPARENT. - * - * SPECIAL CASE: If an error occurs ni_vp and/or ni_dvp may contain garbage - * on return. - * - * VOP_LOOKUP EXPECTATIONS: VOP_LOOKUP() takes a locked directory vnode - * and returns a locked target vnode on success. VOP_LOOKUP() may unlock the - * directory vnode passed to it, in which case it will set CNP_PDIRUNLOCK. - * However, this only occurs under very specific circumstances. The - * directory vnode will only be returned locked if (1) returned vnode == - * directory vnode, or (2) CNP_LOCKPARENT *AND* CNP_LASTCN are both set. + * Relookup a path name component. This function is only used by the + * old API *_rename() code under very specific conditions. CNP_LOCKPARENT + * must be set in the cnp. dvp must be unlocked on entry and will be left + * unlocked if an error occurs. + * + * The returned *vpp will be NULL if an error occurs. Note that no error + * will occur (error == 0) for CREATE requests on a non-existant target, but + * *vpp will be NULL in that case. Both dvp and *vpp (if not NULL) will be + * locked in the no-error case. No additional references are made to dvp, + * only the locking state changes. */ int -lookup(struct nameidata *ndp) +relookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp) { - char *cp; /* pointer into pathname argument */ - struct vnode *dp = NULL; /* the directory we are searching */ - struct vnode *tdp; /* saved dp */ - struct mount *mp; /* mount table entry */ - int docache; /* == 0 do not cache last component */ - int wantparent; /* 1 => wantparent or lockparent flag */ + struct thread *td = cnp->cn_td; int rdonly; /* lookup read-only flag bit */ - int trailing_slash; int error = 0; - int dpunlocked = 0; /* dp has already been unlocked */ - struct componentname *cnp = &ndp->ni_cnd; - struct thread *td = cnp->cn_td; /* * Setup: break out flag bits into variables. */ - wantparent = cnp->cn_flags & (CNP_LOCKPARENT | CNP_WANTPARENT); - docache = (cnp->cn_flags & CNP_NOCACHE) ^ CNP_NOCACHE; - if (cnp->cn_nameiop == NAMEI_DELETE || - (wantparent && cnp->cn_nameiop != NAMEI_CREATE && - cnp->cn_nameiop != NAMEI_LOOKUP)) - docache = 0; - rdonly = cnp->cn_flags & CNP_RDONLY; - ndp->ni_dvp = NULL; - cnp->cn_flags &= ~CNP_ISSYMLINK; - dp = ndp->ni_startdir; - ndp->ni_startdir = NULLVP; - vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, td); - -dirloop: - /* - * Search a new directory. - * - * 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 CNP_SAVENAME flag. When done, they assume - * responsibility for freeing the pathname buffer. - */ - cnp->cn_consume = 0; - for (cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++) - continue; - cnp->cn_namelen = cp - cnp->cn_nameptr; - if (cnp->cn_namelen > NAME_MAX) { - 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; - - /* - * 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'; /* XXX for direnter() ... */ - } - } - ndp->ni_next = cp; - - cnp->cn_flags |= CNP_MAKEENTRY; - if (*cp == '\0' && docache == 0) - cnp->cn_flags &= ~CNP_MAKEENTRY; - if (cnp->cn_namelen == 2 && - cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.') - cnp->cn_flags |= CNP_ISDOTDOT; - else - cnp->cn_flags &= ~CNP_ISDOTDOT; - if (*ndp->ni_next == 0) - cnp->cn_flags |= CNP_ISLASTCN; - else - cnp->cn_flags &= ~CNP_ISLASTCN; - - - /* - * 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 != NAMEI_LOOKUP) { - error = EISDIR; - goto bad; - } - if (wantparent) { - ndp->ni_dvp = dp; - vref(dp); - } - ndp->ni_vp = dp; - if (!(cnp->cn_flags & (CNP_LOCKPARENT | CNP_LOCKLEAF))) - VOP_UNLOCK(dp, 0, cnp->cn_td); - /* XXX This should probably move to the top of function. */ - if (cnp->cn_flags & CNP_SAVESTART) - panic("lookup: CNP_SAVESTART"); - return (0); - } - - /* - * Handle "..": two special cases. - * 1. If at root directory (e.g. after chroot) - * or at absolute root directory - * then ignore it so can't get out. - * 2. If this vnode is the root of a mounted - * filesystem, then replace it with the - * vnode which was mounted on so we take the - * .. in the other file system. - * 3. If the vnode is the top directory of - * the jail or chroot, don't let them out. - */ - if (cnp->cn_flags & CNP_ISDOTDOT) { - for (;;) { - if (dp == ndp->ni_rootdir || - dp == ndp->ni_topdir || - dp == rootvnode) { - ndp->ni_dvp = dp; - ndp->ni_vp = dp; - vref(dp); - goto nextname; - } - if ((dp->v_flag & VROOT) == 0 || - (cnp->cn_flags & CNP_NOCROSSMOUNT)) - break; - if (dp->v_mount == NULL) { /* forced unmount */ - error = EBADF; - goto bad; - } - tdp = dp; - dp = dp->v_mount->mnt_vnodecovered; - vput(tdp); - vref(dp); - vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, td); - } - } - - /* - * We now have a segment name to search for, and a directory to search. - */ -unionlookup: - ndp->ni_dvp = dp; - ndp->ni_vp = NULL; + KKASSERT(cnp->cn_flags & CNP_LOCKPARENT); + KKASSERT(cnp->cn_flags & CNP_PDIRUNLOCK); + vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); cnp->cn_flags &= ~CNP_PDIRUNLOCK; - ASSERT_VOP_LOCKED(dp, "lookup"); - if ((error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) != 0) { - KASSERT(ndp->ni_vp == NULL, ("leaf should be empty")); -#ifdef NAMEI_DIAGNOSTIC - printf("not found\n"); -#endif - if ((error == ENOENT) && - (dp->v_flag & VROOT) && (dp->v_mount != NULL) && - (dp->v_mount->mnt_flag & MNT_UNION)) { - tdp = dp; - dp = dp->v_mount->mnt_vnodecovered; - if (cnp->cn_flags & CNP_PDIRUNLOCK) - vrele(tdp); - else - vput(tdp); - vref(dp); - vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, td); - goto unionlookup; - } - - if (error != EJUSTRETURN) - goto bad; - /* - * If creating and at end of pathname, then can consider - * allowing file to be created. - */ - if (rdonly) { - error = EROFS; - goto bad; - } - if (*cp == '\0' && trailing_slash && - !(cnp->cn_flags & CNP_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. - */ - if (cnp->cn_flags & CNP_SAVESTART) { - ndp->ni_startdir = ndp->ni_dvp; - vref(ndp->ni_startdir); - } - return (0); - } -#ifdef NAMEI_DIAGNOSTIC - printf("found\n"); -#endif - - ASSERT_VOP_LOCKED(ndp->ni_vp, "lookup"); - - /* - * Take into account any additional components consumed by - * the underlying filesystem. - */ - if (cnp->cn_consume > 0) { - cnp->cn_nameptr += cnp->cn_consume; - ndp->ni_next += cnp->cn_consume; - ndp->ni_pathlen -= cnp->cn_consume; - cnp->cn_consume = 0; - } - - dp = ndp->ni_vp; - - /* - * 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 & CNP_NOCROSSMOUNT) == 0) { - if (vfs_busy(mp, 0, NULL, td)) - continue; - VOP_UNLOCK(dp, 0, td); - error = VFS_ROOT(mp, &tdp); - vfs_unbusy(mp, td); - if (error) { - dpunlocked = 1; - goto bad2; - } - vrele(dp); - ndp->ni_vp = dp = tdp; - } - - /* - * Check for symbolic link - */ - if ((dp->v_type == VLNK) && - ((cnp->cn_flags & CNP_FOLLOW) || trailing_slash || - *ndp->ni_next == '/')) { - cnp->cn_flags |= CNP_ISSYMLINK; - if (dp->v_mount == NULL) { - /* We can't know whether the directory was mounted with - * NOSYMFOLLOW, so we can't follow safely. */ - error = EBADF; - goto bad2; - } - if (dp->v_mount->mnt_flag & MNT_NOSYMFOLLOW) { - error = EACCES; - goto bad2; - } - return (0); - } - - /* - * Check for bogus trailing slashes. - */ - if (trailing_slash && dp->v_type != VDIR) { - error = ENOTDIR; - goto bad2; - } - -nextname: - /* - * Not a symbolic link. If more pathname, - * continue at next component, else return. - */ - if (*ndp->ni_next == '/') { - cnp->cn_nameptr = ndp->ni_next; - while (*cnp->cn_nameptr == '/') { - cnp->cn_nameptr++; - ndp->ni_pathlen--; - } - if (ndp->ni_dvp != ndp->ni_vp) - ASSERT_VOP_UNLOCKED(ndp->ni_dvp, "lookup"); - vrele(ndp->ni_dvp); - goto dirloop; - } - /* - * Disallow directory write attempts on read-only file systems. - */ - if (rdonly && - (cnp->cn_nameiop == NAMEI_DELETE || cnp->cn_nameiop == NAMEI_RENAME)) { - error = EROFS; - goto bad2; - } - if (cnp->cn_flags & CNP_SAVESTART) { - ndp->ni_startdir = ndp->ni_dvp; - vref(ndp->ni_startdir); - } - if (!wantparent) - vrele(ndp->ni_dvp); - - if ((cnp->cn_flags & CNP_LOCKLEAF) == 0) - VOP_UNLOCK(dp, 0, td); - return (0); -bad2: - if ((cnp->cn_flags & (CNP_LOCKPARENT | CNP_PDIRUNLOCK)) == CNP_LOCKPARENT && - *ndp->ni_next == '\0') - VOP_UNLOCK(ndp->ni_dvp, 0, td); - vrele(ndp->ni_dvp); -bad: - if (dpunlocked) - vrele(dp); - else - vput(dp); - ndp->ni_vp = NULL; - return (error); -} - -/* - * relookup - lookup a path name component - * Used by lookup to re-aquire things. - */ -int -relookup(dvp, vpp, cnp) - struct vnode *dvp, **vpp; - struct componentname *cnp; -{ - struct thread *td = cnp->cn_td; - struct vnode *dp = 0; /* the directory we are searching */ - 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 */ - char *cp; /* DEBUG: check name ptr/len */ -#endif - - /* - * Setup: break out flag bits into variables. - */ - wantparent = cnp->cn_flags & (CNP_LOCKPARENT|CNP_WANTPARENT); + *vpp = NULL; rdonly = cnp->cn_flags & CNP_RDONLY; - cnp->cn_flags &= ~CNP_ISSYMLINK; - dp = dvp; - vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, td); -/* dirloop: */ /* * Search a new directory. - * - * 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 CNP_SAVENAME flag. When done, they assume - * responsibility for freeing the pathname buffer. */ -#ifdef NAMEI_DIAGNOSTIC - 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 /* - * Check for degenerate name (e.g. / or "") - * which is a way of talking about a directory, - * e.g. like "/." or ".". + * Degenerate lookups (e.g. "/", "..", derived from "/.", etc) + * are not allowed. */ if (cnp->cn_nameptr[0] == '\0') { - if (cnp->cn_nameiop != NAMEI_LOOKUP || wantparent) { - error = EISDIR; - goto bad; - } - if (dp->v_type != VDIR) { - error = ENOTDIR; - goto bad; - } - if (!(cnp->cn_flags & CNP_LOCKLEAF)) - VOP_UNLOCK(dp, 0, td); - *vpp = dp; - /* XXX This should probably move to the top of function. */ - if (cnp->cn_flags & CNP_SAVESTART) - panic("lookup: CNP_SAVESTART"); - return (0); + error = EISDIR; + goto bad; } - if (cnp->cn_flags & CNP_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)) != 0) { + if ((error = VOP_OLD_LOOKUP(dvp, vpp, cnp)) != 0) { KASSERT(*vpp == NULL, ("leaf should be empty")); if (error != EJUSTRETURN) goto bad; @@ -707,9 +124,6 @@ relookup(dvp, vpp, cnp) error = EROFS; goto bad; } - /* ASSERT(dvp == ndp->ni_startdir) */ - if (cnp->cn_flags & CNP_SAVESTART) - vref(dvp); /* * We return with ni_vp NULL to indicate that the entry * doesn't currently exist, leaving a pointer to the @@ -717,43 +131,33 @@ relookup(dvp, vpp, cnp) */ return (0); } - dp = *vpp; /* * Check for symbolic link */ - KASSERT(dp->v_type != VLNK || !(cnp->cn_flags & CNP_FOLLOW), + KASSERT((*vpp)->v_type != VLNK || !(cnp->cn_flags & CNP_FOLLOW), ("relookup: symlink found.\n")); /* * Disallow directory write attempts on read-only file systems. */ - if (rdonly && - (cnp->cn_nameiop == NAMEI_DELETE || cnp->cn_nameiop == NAMEI_RENAME)) { + if (rdonly && (cnp->cn_nameiop == NAMEI_DELETE || + cnp->cn_nameiop == NAMEI_RENAME)) { error = EROFS; - goto bad2; + goto bad; } - /* ASSERT(dvp == ndp->ni_startdir) */ - if (cnp->cn_flags & CNP_SAVESTART) - vref(dvp); - - if (!wantparent) - vrele(dvp); - - if (vn_canvmio(dp) == TRUE && - ((cnp->cn_flags & (CNP_NOOBJ|CNP_LOCKLEAF)) == CNP_LOCKLEAF)) - vfs_object_create(dp, cnp->cn_td); - - if ((cnp->cn_flags & CNP_LOCKLEAF) == 0) - VOP_UNLOCK(dp, 0, td); + KKASSERT((cnp->cn_flags & CNP_PDIRUNLOCK) == 0); return (0); -bad2: - if ((cnp->cn_flags & CNP_LOCKPARENT) && (cnp->cn_flags & CNP_ISLASTCN)) - VOP_UNLOCK(dvp, 0, td); - vrele(dvp); bad: - vput(dp); - *vpp = NULL; + if ((cnp->cn_flags & CNP_PDIRUNLOCK) == 0) { + cnp->cn_flags |= CNP_PDIRUNLOCK; + VOP_UNLOCK(dvp, 0, td); + } + if (*vpp) { + vput(*vpp); + *vpp = NULL; + } return (error); } +