123401Smckusick /* 263180Sbostic * Copyright (c) 1982, 1986, 1989, 1993 363180Sbostic * The Regents of the University of California. All rights reserved. 465771Sbostic * (c) UNIX System Laboratories, Inc. 565771Sbostic * All or some portions of this file are derived from material licensed 665771Sbostic * to the University of California by American Telephone and Telegraph 765771Sbostic * Co. or Unix System Laboratories, Inc. and are reproduced herein with 865771Sbostic * the permission of UNIX System Laboratories, Inc. 923401Smckusick * 1044457Sbostic * %sccs.include.redist.c% 1137715Smckusick * 12*66128Spendry * @(#)vfs_lookup.c 8.4 (Berkeley) 02/16/94 1323401Smckusick */ 1430Sbill 1556517Sbostic #include <sys/param.h> 1656517Sbostic #include <sys/syslimits.h> 1756517Sbostic #include <sys/time.h> 1856517Sbostic #include <sys/namei.h> 1956517Sbostic #include <sys/vnode.h> 2056517Sbostic #include <sys/mount.h> 2156517Sbostic #include <sys/errno.h> 2256517Sbostic #include <sys/malloc.h> 2356517Sbostic #include <sys/filedesc.h> 2456517Sbostic #include <sys/proc.h> 2537715Smckusick 2637582Smarc #ifdef KTRACE 2756517Sbostic #include <sys/ktrace.h> 2837582Smarc #endif 2930Sbill 3030Sbill /* 3127268Smckusick * Convert a pathname into a pointer to a locked inode. 3230Sbill * 3316688Smckusick * The FOLLOW flag is set when symbolic links are to be followed 349166Ssam * when they occur at the end of the name translation process. 3527268Smckusick * Symbolic links are always followed for all other pathname 3627268Smckusick * components other than the last. 379166Ssam * 3827268Smckusick * The segflg defines whether the name is to be copied from user 3927268Smckusick * space or kernel space. 4027268Smckusick * 4115798Smckusick * Overall outline of namei: 4215798Smckusick * 437534Sroot * copy in name 447534Sroot * get starting directory 4549736Smckusick * while (!done && !error) { 4649736Smckusick * call lookup to search path. 4749736Smckusick * if symbolic link, massage name in buffer and continue 4849736Smckusick * } 4949736Smckusick */ 5052633Sheideman int 5152633Sheideman namei(ndp) 5249736Smckusick register struct nameidata *ndp; 5349736Smckusick { 5449736Smckusick register struct filedesc *fdp; /* pointer to file descriptor state */ 5549736Smckusick register char *cp; /* pointer into pathname argument */ 5649736Smckusick register struct vnode *dp; /* the directory we are searching */ 5749736Smckusick struct iovec aiov; /* uio for reading symbolic links */ 5849736Smckusick struct uio auio; 5949736Smckusick int error, linklen; 6052633Sheideman struct componentname *cnp = &ndp->ni_cnd; 6149736Smckusick 6252633Sheideman ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred; 6352633Sheideman #ifdef DIAGNOSTIC 6452633Sheideman if (!cnp->cn_cred || !cnp->cn_proc) 6552633Sheideman panic ("namei: bad cred/proc"); 6652633Sheideman if (cnp->cn_nameiop & (~OPMASK)) 6752633Sheideman panic ("namei: nameiop contaminated with flags"); 6852633Sheideman if (cnp->cn_flags & OPMASK) 6952633Sheideman panic ("namei: flags contaminated with nameiops"); 7052633Sheideman #endif 7152633Sheideman fdp = cnp->cn_proc->p_fd; 7249736Smckusick 7349736Smckusick /* 7449736Smckusick * Get a buffer for the name to be translated, and copy the 7549736Smckusick * name into the buffer. 7649736Smckusick */ 7752633Sheideman if ((cnp->cn_flags & HASBUF) == 0) 7852633Sheideman MALLOC(cnp->cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); 7949736Smckusick if (ndp->ni_segflg == UIO_SYSSPACE) 8052633Sheideman error = copystr(ndp->ni_dirp, cnp->cn_pnbuf, 8149736Smckusick MAXPATHLEN, &ndp->ni_pathlen); 8249736Smckusick else 8352633Sheideman error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf, 8449736Smckusick MAXPATHLEN, &ndp->ni_pathlen); 8549736Smckusick if (error) { 8652633Sheideman free(cnp->cn_pnbuf, M_NAMEI); 8749736Smckusick ndp->ni_vp = NULL; 8849736Smckusick return (error); 8949736Smckusick } 9049736Smckusick ndp->ni_loopcnt = 0; 9149736Smckusick #ifdef KTRACE 9252633Sheideman if (KTRPOINT(cnp->cn_proc, KTR_NAMEI)) 9352633Sheideman ktrnamei(cnp->cn_proc->p_tracep, cnp->cn_pnbuf); 9449736Smckusick #endif 9549736Smckusick 9649736Smckusick /* 9749736Smckusick * Get starting point for the translation. 9849736Smckusick */ 9949736Smckusick if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL) 10064414Sbostic ndp->ni_rootdir = rootvnode; 10149736Smckusick dp = fdp->fd_cdir; 10249736Smckusick VREF(dp); 10349736Smckusick for (;;) { 10449736Smckusick /* 10549736Smckusick * Check if root directory should replace current directory. 10649736Smckusick * Done at start of translation and after symbolic link. 10749736Smckusick */ 10852633Sheideman cnp->cn_nameptr = cnp->cn_pnbuf; 10952633Sheideman if (*(cnp->cn_nameptr) == '/') { 11049736Smckusick vrele(dp); 11152633Sheideman while (*(cnp->cn_nameptr) == '/') { 11252633Sheideman cnp->cn_nameptr++; 11349736Smckusick ndp->ni_pathlen--; 11449736Smckusick } 11549736Smckusick dp = ndp->ni_rootdir; 11649736Smckusick VREF(dp); 11749736Smckusick } 11849736Smckusick ndp->ni_startdir = dp; 11952633Sheideman if (error = lookup(ndp)) { 12052633Sheideman FREE(cnp->cn_pnbuf, M_NAMEI); 12149736Smckusick return (error); 12249736Smckusick } 12349736Smckusick /* 12449736Smckusick * Check for symbolic link 12549736Smckusick */ 12652633Sheideman if ((cnp->cn_flags & ISSYMLINK) == 0) { 12752633Sheideman if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0) 12852633Sheideman FREE(cnp->cn_pnbuf, M_NAMEI); 12949736Smckusick else 13052633Sheideman cnp->cn_flags |= HASBUF; 13149736Smckusick return (0); 13249736Smckusick } 13352633Sheideman if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1) 13449736Smckusick VOP_UNLOCK(ndp->ni_dvp); 13549736Smckusick if (ndp->ni_loopcnt++ >= MAXSYMLINKS) { 13649736Smckusick error = ELOOP; 13749736Smckusick break; 13849736Smckusick } 13949736Smckusick if (ndp->ni_pathlen > 1) 14049736Smckusick MALLOC(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); 14149736Smckusick else 14252633Sheideman cp = cnp->cn_pnbuf; 14349736Smckusick aiov.iov_base = cp; 14449736Smckusick aiov.iov_len = MAXPATHLEN; 14549736Smckusick auio.uio_iov = &aiov; 14649736Smckusick auio.uio_iovcnt = 1; 14749736Smckusick auio.uio_offset = 0; 14849736Smckusick auio.uio_rw = UIO_READ; 14949736Smckusick auio.uio_segflg = UIO_SYSSPACE; 15049736Smckusick auio.uio_procp = (struct proc *)0; 15149736Smckusick auio.uio_resid = MAXPATHLEN; 15252633Sheideman if (error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred)) { 15349736Smckusick if (ndp->ni_pathlen > 1) 15449736Smckusick free(cp, M_NAMEI); 15549736Smckusick break; 15649736Smckusick } 15749736Smckusick linklen = MAXPATHLEN - auio.uio_resid; 15849736Smckusick if (linklen + ndp->ni_pathlen >= MAXPATHLEN) { 15949736Smckusick if (ndp->ni_pathlen > 1) 16049736Smckusick free(cp, M_NAMEI); 16149736Smckusick error = ENAMETOOLONG; 16249736Smckusick break; 16349736Smckusick } 16449736Smckusick if (ndp->ni_pathlen > 1) { 16549736Smckusick bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen); 16652633Sheideman FREE(cnp->cn_pnbuf, M_NAMEI); 16752633Sheideman cnp->cn_pnbuf = cp; 16849736Smckusick } else 16952633Sheideman cnp->cn_pnbuf[linklen] = '\0'; 17049736Smckusick ndp->ni_pathlen += linklen; 17149736Smckusick vput(ndp->ni_vp); 17249736Smckusick dp = ndp->ni_dvp; 17349736Smckusick } 17452633Sheideman FREE(cnp->cn_pnbuf, M_NAMEI); 17549736Smckusick vrele(ndp->ni_dvp); 17649736Smckusick vput(ndp->ni_vp); 17749736Smckusick ndp->ni_vp = NULL; 17849736Smckusick return (error); 17949736Smckusick } 18049736Smckusick 18149736Smckusick /* 18249736Smckusick * Search a pathname. 18349736Smckusick * This is a very central and rather complicated routine. 18449736Smckusick * 18549736Smckusick * The pathname is pointed to by ni_ptr and is of length ni_pathlen. 18649736Smckusick * The starting directory is taken from ni_startdir. The pathname is 18749736Smckusick * descended until done, or a symbolic link is encountered. The variable 18849736Smckusick * ni_more is clear if the path is completed; it is set to one if a 18949736Smckusick * symbolic link needing interpretation is encountered. 19049736Smckusick * 19149736Smckusick * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on 19249736Smckusick * whether the name is to be looked up, created, renamed, or deleted. 19349736Smckusick * When CREATE, RENAME, or DELETE is specified, information usable in 19449736Smckusick * creating, renaming, or deleting a directory entry may be calculated. 19549736Smckusick * If flag has LOCKPARENT or'ed into it, the parent directory is returned 19649736Smckusick * locked. If flag has WANTPARENT or'ed into it, the parent directory is 19749736Smckusick * returned unlocked. Otherwise the parent directory is not returned. If 19849736Smckusick * the target of the pathname exists and LOCKLEAF is or'ed into the flag 19949736Smckusick * the target is returned locked, otherwise it is returned unlocked. 20049736Smckusick * When creating or renaming and LOCKPARENT is specified, the target may not 20149736Smckusick * be ".". When deleting and LOCKPARENT is specified, the target may be ".". 20249736Smckusick * 20349736Smckusick * Overall outline of lookup: 20449736Smckusick * 2057534Sroot * dirloop: 20649736Smckusick * identify next component of name at ndp->ni_ptr 2077534Sroot * handle degenerate case where name is null string 20849736Smckusick * if .. and crossing mount points and on mounted filesys, find parent 20949736Smckusick * call VOP_LOOKUP routine for next component name 21049736Smckusick * directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set 21149736Smckusick * component vnode returned in ni_vp (if it exists), locked. 21249736Smckusick * if result vnode is mounted on and crossing mount points, 21349736Smckusick * find mounted on vnode 2147534Sroot * if more components of name, do next level at dirloop 21549736Smckusick * return the answer in ni_vp, locked if LOCKLEAF set 21649736Smckusick * if LOCKPARENT set, return locked parent in ni_dvp 21749736Smckusick * if WANTPARENT set, return unlocked parent in ni_dvp 21830Sbill */ 21952633Sheideman int 22052633Sheideman lookup(ndp) 22116688Smckusick register struct nameidata *ndp; 22230Sbill { 2237534Sroot register char *cp; /* pointer into pathname argument */ 22437715Smckusick register struct vnode *dp = 0; /* the directory we are searching */ 22537715Smckusick struct vnode *tdp; /* saved dp */ 22637715Smckusick struct mount *mp; /* mount table entry */ 22718109Smckusick int docache; /* == 0 do not cache last component */ 22837715Smckusick int wantparent; /* 1 => wantparent or lockparent flag */ 22952181Smckusick int rdonly; /* lookup read-only flag bit */ 23037715Smckusick int error = 0; 23152230Sheideman struct componentname *cnp = &ndp->ni_cnd; 23230Sbill 23341337Smckusick /* 23441337Smckusick * Setup: break out flag bits into variables. 23541337Smckusick */ 23652633Sheideman wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT); 23752230Sheideman docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE; 23852633Sheideman if (cnp->cn_nameiop == DELETE || 23952633Sheideman (wantparent && cnp->cn_nameiop != CREATE)) 24015798Smckusick docache = 0; 24152230Sheideman rdonly = cnp->cn_flags & RDONLY; 24249736Smckusick ndp->ni_dvp = NULL; 24352230Sheideman cnp->cn_flags &= ~ISSYMLINK; 24449736Smckusick dp = ndp->ni_startdir; 24549736Smckusick ndp->ni_startdir = NULLVP; 24637715Smckusick VOP_LOCK(dp); 2477534Sroot 2486571Smckusic dirloop: 24930Sbill /* 25049736Smckusick * Search a new directory. 25149736Smckusick * 25252230Sheideman * The cn_hash value is for use by vfs_cache. 25349736Smckusick * The last component of the filename is left accessible via 25452230Sheideman * cnp->cn_nameptr for callers that need the name. Callers needing 25549736Smckusick * the name set the SAVENAME flag. When done, they assume 25649736Smckusick * responsibility for freeing the pathname buffer. 2577534Sroot */ 25855091Spendry cnp->cn_consume = 0; 25952230Sheideman cnp->cn_hash = 0; 26052230Sheideman for (cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++) 26152230Sheideman cnp->cn_hash += (unsigned char)*cp; 26252230Sheideman cnp->cn_namelen = cp - cnp->cn_nameptr; 26357653Smckusick if (cnp->cn_namelen > NAME_MAX) { 26449736Smckusick error = ENAMETOOLONG; 26549736Smckusick goto bad; 26649736Smckusick } 26737743Smckusick #ifdef NAMEI_DIAGNOSTIC 26849736Smckusick { char c = *cp; 26949736Smckusick *cp = '\0'; 27052230Sheideman printf("{%s}: ", cnp->cn_nameptr); 27149736Smckusick *cp = c; } 27237743Smckusick #endif 27352230Sheideman ndp->ni_pathlen -= cnp->cn_namelen; 27449736Smckusick ndp->ni_next = cp; 27552230Sheideman cnp->cn_flags |= MAKEENTRY; 27618109Smckusick if (*cp == '\0' && docache == 0) 27752230Sheideman cnp->cn_flags &= ~MAKEENTRY; 27852230Sheideman if (cnp->cn_namelen == 2 && 27955181Smckusick cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.') 28052230Sheideman cnp->cn_flags |= ISDOTDOT; 28155181Smckusick else 28255181Smckusick cnp->cn_flags &= ~ISDOTDOT; 28352230Sheideman if (*ndp->ni_next == 0) 28452230Sheideman cnp->cn_flags |= ISLASTCN; 28555181Smckusick else 28655181Smckusick cnp->cn_flags &= ~ISLASTCN; 2877534Sroot 28852230Sheideman 2897534Sroot /* 2907534Sroot * Check for degenerate name (e.g. / or "") 2917534Sroot * which is a way of talking about a directory, 2927534Sroot * e.g. like "/." or ".". 2937534Sroot */ 29452230Sheideman if (cnp->cn_nameptr[0] == '\0') { 29559371Smckusick if (cnp->cn_nameiop != LOOKUP) { 29637715Smckusick error = EISDIR; 2977534Sroot goto bad; 2985972Swnj } 29949736Smckusick if (dp->v_type != VDIR) { 30049736Smckusick error = ENOTDIR; 30149736Smckusick goto bad; 30249736Smckusick } 30359371Smckusick if (wantparent) { 30459371Smckusick ndp->ni_dvp = dp; 305*66128Spendry VREF(dp); 30659371Smckusick } 30759371Smckusick ndp->ni_vp = dp; 30859371Smckusick if (!(cnp->cn_flags & (LOCKPARENT | LOCKLEAF))) 30937715Smckusick VOP_UNLOCK(dp); 31052230Sheideman if (cnp->cn_flags & SAVESTART) 31149736Smckusick panic("lookup: SAVESTART"); 31252633Sheideman return (0); 3135972Swnj } 3147534Sroot 3156571Smckusic /* 31637715Smckusick * Handle "..": two special cases. 31737715Smckusick * 1. If at root directory (e.g. after chroot) 31856313Shibler * or at absolute root directory 31937715Smckusick * then ignore it so can't get out. 32037715Smckusick * 2. If this vnode is the root of a mounted 32149736Smckusick * filesystem, then replace it with the 32237715Smckusick * vnode which was mounted on so we take the 32337715Smckusick * .. in the other file system. 32436547Smckusick */ 32552230Sheideman if (cnp->cn_flags & ISDOTDOT) { 32636547Smckusick for (;;) { 32764414Sbostic if (dp == ndp->ni_rootdir || dp == rootvnode) { 32837715Smckusick ndp->ni_dvp = dp; 32938390Smckusick ndp->ni_vp = dp; 33038347Smckusick VREF(dp); 33137715Smckusick goto nextname; 33236547Smckusick } 33341337Smckusick if ((dp->v_flag & VROOT) == 0 || 33452230Sheideman (cnp->cn_flags & NOCROSSMOUNT)) 33536547Smckusick break; 33637715Smckusick tdp = dp; 33741400Smckusick dp = dp->v_mount->mnt_vnodecovered; 33837715Smckusick vput(tdp); 33938390Smckusick VREF(dp); 34037715Smckusick VOP_LOCK(dp); 34136547Smckusick } 34236547Smckusick } 34336547Smckusick 34436547Smckusick /* 34515798Smckusick * We now have a segment name to search for, and a directory to search. 34615798Smckusick */ 34755451Spendry unionlookup: 34852230Sheideman ndp->ni_dvp = dp; 34952230Sheideman if (error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) { 35049736Smckusick #ifdef DIAGNOSTIC 35137715Smckusick if (ndp->ni_vp != NULL) 35237715Smckusick panic("leaf should be empty"); 35349736Smckusick #endif 35437582Smarc #ifdef NAMEI_DIAGNOSTIC 35537715Smckusick printf("not found\n"); 35637582Smarc #endif 35755451Spendry if ((error == ENOENT) && 35855451Spendry (dp->v_flag & VROOT) && 35955451Spendry (dp->v_mount->mnt_flag & MNT_UNION)) { 36055451Spendry tdp = dp; 36155451Spendry dp = dp->v_mount->mnt_vnodecovered; 36255451Spendry vput(tdp); 36355451Spendry VREF(dp); 36455451Spendry VOP_LOCK(dp); 36555451Spendry goto unionlookup; 36655451Spendry } 36755451Spendry 36852798Smckusick if (error != EJUSTRETURN) 36938581Smckusick goto bad; 3705972Swnj /* 37137715Smckusick * If creating and at end of pathname, then can consider 37237715Smckusick * allowing file to be created. 3735972Swnj */ 37452181Smckusick if (rdonly || (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY)) { 37537715Smckusick error = EROFS; 3767534Sroot goto bad; 37738581Smckusick } 3785972Swnj /* 37937715Smckusick * We return with ni_vp NULL to indicate that the entry 38037715Smckusick * doesn't currently exist, leaving a pointer to the 38137715Smckusick * (possibly locked) directory inode in ndp->ni_dvp. 3825972Swnj */ 38352230Sheideman if (cnp->cn_flags & SAVESTART) { 38449736Smckusick ndp->ni_startdir = ndp->ni_dvp; 38549736Smckusick VREF(ndp->ni_startdir); 38649736Smckusick } 38752633Sheideman return (0); 3887534Sroot } 38937582Smarc #ifdef NAMEI_DIAGNOSTIC 39037715Smckusick printf("found\n"); 39137582Smarc #endif 3927534Sroot 39355091Spendry /* 39455091Spendry * Take into account any additional components consumed by 39555091Spendry * the underlying filesystem. 39655091Spendry */ 39755091Spendry if (cnp->cn_consume > 0) { 39855091Spendry cnp->cn_nameptr += cnp->cn_consume; 39955091Spendry ndp->ni_next += cnp->cn_consume; 40055091Spendry ndp->ni_pathlen -= cnp->cn_consume; 40155091Spendry cnp->cn_consume = 0; 40255091Spendry } 40355091Spendry 40449736Smckusick dp = ndp->ni_vp; 4057534Sroot /* 40637715Smckusick * Check for symbolic link 4077534Sroot */ 40837715Smckusick if ((dp->v_type == VLNK) && 40952230Sheideman ((cnp->cn_flags & FOLLOW) || *ndp->ni_next == '/')) { 41052230Sheideman cnp->cn_flags |= ISSYMLINK; 41152633Sheideman return (0); 41215798Smckusick } 41315798Smckusick 4147534Sroot /* 41537715Smckusick * Check to see if the vnode has been mounted on; 41637715Smckusick * if so find the root of the mounted file system. 4177534Sroot */ 41841337Smckusick while (dp->v_type == VDIR && (mp = dp->v_mountedhere) && 41952230Sheideman (cnp->cn_flags & NOCROSSMOUNT) == 0) { 42052116Smarc if (mp->mnt_flag & MNT_MLOCK) { 42141400Smckusick mp->mnt_flag |= MNT_MWAIT; 42237715Smckusick sleep((caddr_t)mp, PVFS); 42352634Smckusick continue; 42421014Smckusick } 42549736Smckusick if (error = VFS_ROOT(dp->v_mountedhere, &tdp)) 4267534Sroot goto bad2; 42737715Smckusick vput(dp); 42837715Smckusick ndp->ni_vp = dp = tdp; 42930Sbill } 4307534Sroot 43137715Smckusick nextname: 43230Sbill /* 4337534Sroot * Not a symbolic link. If more pathname, 4347534Sroot * continue at next component, else return. 43530Sbill */ 43649736Smckusick if (*ndp->ni_next == '/') { 43752230Sheideman cnp->cn_nameptr = ndp->ni_next; 43852230Sheideman while (*cnp->cn_nameptr == '/') { 43952230Sheideman cnp->cn_nameptr++; 44037715Smckusick ndp->ni_pathlen--; 44137715Smckusick } 44237715Smckusick vrele(ndp->ni_dvp); 4437534Sroot goto dirloop; 44430Sbill } 4457534Sroot /* 44638400Smckusick * Check for read-only file systems. 4477534Sroot */ 44852230Sheideman if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) { 44938267Smckusick /* 45038400Smckusick * Disallow directory write attempts on read-only 45138400Smckusick * file systems. 45238267Smckusick */ 45352181Smckusick if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) || 45452181Smckusick (wantparent && 45552181Smckusick (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY))) { 45638267Smckusick error = EROFS; 45738267Smckusick goto bad2; 45838267Smckusick } 45938267Smckusick } 46052230Sheideman if (cnp->cn_flags & SAVESTART) { 46149736Smckusick ndp->ni_startdir = ndp->ni_dvp; 46249736Smckusick VREF(ndp->ni_startdir); 46349736Smckusick } 46437715Smckusick if (!wantparent) 46537715Smckusick vrele(ndp->ni_dvp); 46652230Sheideman if ((cnp->cn_flags & LOCKLEAF) == 0) 46737715Smckusick VOP_UNLOCK(dp); 46852633Sheideman return (0); 4697534Sroot 47037715Smckusick bad2: 47152230Sheideman if ((cnp->cn_flags & LOCKPARENT) && *ndp->ni_next == '\0') 47238001Smckusick VOP_UNLOCK(ndp->ni_dvp); 47337715Smckusick vrele(ndp->ni_dvp); 47437715Smckusick bad: 47537715Smckusick vput(dp); 47637715Smckusick ndp->ni_vp = NULL; 47752633Sheideman return (error); 4785972Swnj } 47952230Sheideman 48052230Sheideman 481