Commit | Line | Data |
---|---|---|
da7c5cc6 | 1 | /* |
6d0f0ece KM |
2 | * Copyright (c) 1982, 1986, 1989 Regents of the University of California. |
3 | * All rights reserved. | |
da7c5cc6 | 4 | * |
dbf0c423 | 5 | * %sccs.include.redist.c% |
6d0f0ece | 6 | * |
ea67b335 | 7 | * @(#)vfs_lookup.c 7.39 (Berkeley) %G% |
da7c5cc6 | 8 | */ |
10873320 | 9 | |
94368568 | 10 | #include "param.h" |
955e7a55 | 11 | #include "syslimits.h" |
6d0f0ece KM |
12 | #include "time.h" |
13 | #include "namei.h" | |
14 | #include "vnode.h" | |
94368568 | 15 | #include "mount.h" |
6d0f0ece | 16 | #include "errno.h" |
c3a74062 | 17 | #include "malloc.h" |
5e00df3b | 18 | #include "filedesc.h" |
658f5fdc | 19 | #include "proc.h" |
0caff0ad KM |
20 | |
21 | #ifdef KTRACE | |
658f5fdc MT |
22 | #include "ktrace.h" |
23 | #endif | |
10873320 BJ |
24 | |
25 | /* | |
7f69b0e6 | 26 | * Convert a pathname into a pointer to a locked inode. |
4f083fd7 | 27 | * |
d870be74 | 28 | * The FOLLOW flag is set when symbolic links are to be followed |
4f083fd7 | 29 | * when they occur at the end of the name translation process. |
7f69b0e6 KM |
30 | * Symbolic links are always followed for all other pathname |
31 | * components other than the last. | |
32 | * | |
33 | * The segflg defines whether the name is to be copied from user | |
34 | * space or kernel space. | |
10873320 | 35 | * |
f93197fc | 36 | * Overall outline of namei: |
6bd0bb92 BJ |
37 | * |
38 | * copy in name | |
39 | * get starting directory | |
955e7a55 KM |
40 | * while (!done && !error) { |
41 | * call lookup to search path. | |
42 | * if symbolic link, massage name in buffer and continue | |
43 | * } | |
10873320 | 44 | */ |
743467cb JH |
45 | int |
46 | namei(ndp) | |
d870be74 | 47 | register struct nameidata *ndp; |
10873320 | 48 | { |
9342689a JH |
49 | USES_VOP_READLINK; |
50 | USES_VOP_UNLOCK; | |
5e00df3b | 51 | register struct filedesc *fdp; /* pointer to file descriptor state */ |
6bd0bb92 | 52 | register char *cp; /* pointer into pathname argument */ |
955e7a55 KM |
53 | register struct vnode *dp; /* the directory we are searching */ |
54 | struct iovec aiov; /* uio for reading symbolic links */ | |
55 | struct uio auio; | |
56 | int error, linklen; | |
743467cb | 57 | struct componentname *cnp = &ndp->ni_cnd; |
10873320 | 58 | |
743467cb JH |
59 | ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred; |
60 | #ifdef DIAGNOSTIC | |
61 | if (!cnp->cn_cred || !cnp->cn_proc) | |
62 | panic ("namei: bad cred/proc"); | |
63 | if (cnp->cn_nameiop & (~OPMASK)) | |
64 | panic ("namei: nameiop contaminated with flags"); | |
65 | if (cnp->cn_flags & OPMASK) | |
66 | panic ("namei: flags contaminated with nameiops"); | |
67 | #endif | |
68 | fdp = cnp->cn_proc->p_fd; | |
955e7a55 | 69 | |
f5039631 | 70 | /* |
6bd0bb92 BJ |
71 | * Get a buffer for the name to be translated, and copy the |
72 | * name into the buffer. | |
f5039631 | 73 | */ |
743467cb JH |
74 | if ((cnp->cn_flags & HASBUF) == 0) |
75 | MALLOC(cnp->cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); | |
955e7a55 | 76 | if (ndp->ni_segflg == UIO_SYSSPACE) |
743467cb | 77 | error = copystr(ndp->ni_dirp, cnp->cn_pnbuf, |
955e7a55 KM |
78 | MAXPATHLEN, &ndp->ni_pathlen); |
79 | else | |
743467cb | 80 | error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf, |
955e7a55 KM |
81 | MAXPATHLEN, &ndp->ni_pathlen); |
82 | if (error) { | |
743467cb | 83 | free(cnp->cn_pnbuf, M_NAMEI); |
955e7a55 KM |
84 | ndp->ni_vp = NULL; |
85 | return (error); | |
d870be74 | 86 | } |
6d0f0ece | 87 | ndp->ni_loopcnt = 0; |
658f5fdc | 88 | #ifdef KTRACE |
743467cb JH |
89 | if (KTRPOINT(cnp->cn_proc, KTR_NAMEI)) |
90 | ktrnamei(cnp->cn_proc->p_tracep, cnp->cn_pnbuf); | |
658f5fdc | 91 | #endif |
6bd0bb92 | 92 | |
5f9e231a KM |
93 | /* |
94 | * Get starting point for the translation. | |
95 | */ | |
955e7a55 KM |
96 | if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL) |
97 | ndp->ni_rootdir = rootdir; | |
98 | dp = fdp->fd_cdir; | |
5f9e231a | 99 | VREF(dp); |
955e7a55 KM |
100 | for (;;) { |
101 | /* | |
102 | * Check if root directory should replace current directory. | |
103 | * Done at start of translation and after symbolic link. | |
104 | */ | |
743467cb JH |
105 | cnp->cn_nameptr = cnp->cn_pnbuf; |
106 | if (*(cnp->cn_nameptr) == '/') { | |
955e7a55 | 107 | vrele(dp); |
743467cb JH |
108 | while (*(cnp->cn_nameptr) == '/') { |
109 | cnp->cn_nameptr++; | |
955e7a55 KM |
110 | ndp->ni_pathlen--; |
111 | } | |
112 | dp = ndp->ni_rootdir; | |
113 | VREF(dp); | |
114 | } | |
115 | ndp->ni_startdir = dp; | |
743467cb JH |
116 | if (error = lookup(ndp)) { |
117 | FREE(cnp->cn_pnbuf, M_NAMEI); | |
955e7a55 KM |
118 | return (error); |
119 | } | |
120 | /* | |
121 | * Check for symbolic link | |
122 | */ | |
743467cb JH |
123 | if ((cnp->cn_flags & ISSYMLINK) == 0) { |
124 | if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0) | |
125 | FREE(cnp->cn_pnbuf, M_NAMEI); | |
955e7a55 | 126 | else |
743467cb | 127 | cnp->cn_flags |= HASBUF; |
955e7a55 KM |
128 | return (0); |
129 | } | |
743467cb | 130 | if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1) |
955e7a55 KM |
131 | VOP_UNLOCK(ndp->ni_dvp); |
132 | if (ndp->ni_loopcnt++ >= MAXSYMLINKS) { | |
133 | error = ELOOP; | |
134 | break; | |
135 | } | |
136 | if (ndp->ni_pathlen > 1) | |
137 | MALLOC(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); | |
138 | else | |
743467cb | 139 | cp = cnp->cn_pnbuf; |
955e7a55 KM |
140 | aiov.iov_base = cp; |
141 | aiov.iov_len = MAXPATHLEN; | |
142 | auio.uio_iov = &aiov; | |
143 | auio.uio_iovcnt = 1; | |
144 | auio.uio_offset = 0; | |
145 | auio.uio_rw = UIO_READ; | |
146 | auio.uio_segflg = UIO_SYSSPACE; | |
147 | auio.uio_procp = (struct proc *)0; | |
148 | auio.uio_resid = MAXPATHLEN; | |
743467cb | 149 | if (error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred)) { |
955e7a55 KM |
150 | if (ndp->ni_pathlen > 1) |
151 | free(cp, M_NAMEI); | |
152 | break; | |
153 | } | |
154 | linklen = MAXPATHLEN - auio.uio_resid; | |
155 | if (linklen + ndp->ni_pathlen >= MAXPATHLEN) { | |
156 | if (ndp->ni_pathlen > 1) | |
157 | free(cp, M_NAMEI); | |
158 | error = ENAMETOOLONG; | |
159 | break; | |
6d0f0ece | 160 | } |
955e7a55 KM |
161 | if (ndp->ni_pathlen > 1) { |
162 | bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen); | |
743467cb JH |
163 | FREE(cnp->cn_pnbuf, M_NAMEI); |
164 | cnp->cn_pnbuf = cp; | |
955e7a55 | 165 | } else |
743467cb | 166 | cnp->cn_pnbuf[linklen] = '\0'; |
955e7a55 KM |
167 | ndp->ni_pathlen += linklen; |
168 | vput(ndp->ni_vp); | |
169 | dp = ndp->ni_dvp; | |
6d0f0ece | 170 | } |
743467cb | 171 | FREE(cnp->cn_pnbuf, M_NAMEI); |
955e7a55 KM |
172 | vrele(ndp->ni_dvp); |
173 | vput(ndp->ni_vp); | |
174 | ndp->ni_vp = NULL; | |
175 | return (error); | |
176 | } | |
177 | ||
178 | /* | |
179 | * Search a pathname. | |
180 | * This is a very central and rather complicated routine. | |
181 | * | |
182 | * The pathname is pointed to by ni_ptr and is of length ni_pathlen. | |
183 | * The starting directory is taken from ni_startdir. The pathname is | |
184 | * descended until done, or a symbolic link is encountered. The variable | |
185 | * ni_more is clear if the path is completed; it is set to one if a | |
186 | * symbolic link needing interpretation is encountered. | |
187 | * | |
188 | * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on | |
189 | * whether the name is to be looked up, created, renamed, or deleted. | |
190 | * When CREATE, RENAME, or DELETE is specified, information usable in | |
191 | * creating, renaming, or deleting a directory entry may be calculated. | |
192 | * If flag has LOCKPARENT or'ed into it, the parent directory is returned | |
193 | * locked. If flag has WANTPARENT or'ed into it, the parent directory is | |
194 | * returned unlocked. Otherwise the parent directory is not returned. If | |
195 | * the target of the pathname exists and LOCKLEAF is or'ed into the flag | |
196 | * the target is returned locked, otherwise it is returned unlocked. | |
197 | * When creating or renaming and LOCKPARENT is specified, the target may not | |
198 | * be ".". When deleting and LOCKPARENT is specified, the target may be ".". | |
199 | * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent vnode unlocked. | |
200 | * | |
201 | * Overall outline of lookup: | |
202 | * | |
203 | * dirloop: | |
204 | * identify next component of name at ndp->ni_ptr | |
205 | * handle degenerate case where name is null string | |
206 | * if .. and crossing mount points and on mounted filesys, find parent | |
207 | * call VOP_LOOKUP routine for next component name | |
208 | * directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set | |
209 | * component vnode returned in ni_vp (if it exists), locked. | |
210 | * if result vnode is mounted on and crossing mount points, | |
211 | * find mounted on vnode | |
212 | * if more components of name, do next level at dirloop | |
213 | * return the answer in ni_vp, locked if LOCKLEAF set | |
214 | * if LOCKPARENT set, return locked parent in ni_dvp | |
215 | * if WANTPARENT set, return unlocked parent in ni_dvp | |
216 | */ | |
743467cb JH |
217 | int |
218 | lookup(ndp) | |
955e7a55 | 219 | register struct nameidata *ndp; |
955e7a55 | 220 | { |
9342689a JH |
221 | USES_VOP_LOCK; |
222 | USES_VOP_LOOKUP; | |
223 | USES_VOP_UNLOCK; | |
955e7a55 KM |
224 | register char *cp; /* pointer into pathname argument */ |
225 | register struct vnode *dp = 0; /* the directory we are searching */ | |
226 | struct vnode *tdp; /* saved dp */ | |
227 | struct mount *mp; /* mount table entry */ | |
228 | int docache; /* == 0 do not cache last component */ | |
955e7a55 | 229 | int wantparent; /* 1 => wantparent or lockparent flag */ |
6c235bc4 | 230 | int rdonly; /* lookup read-only flag bit */ |
955e7a55 | 231 | int error = 0; |
cfef4373 | 232 | struct componentname *cnp = &ndp->ni_cnd; |
6bd0bb92 | 233 | |
10873320 | 234 | /* |
955e7a55 | 235 | * Setup: break out flag bits into variables. |
10873320 | 236 | */ |
743467cb | 237 | wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT); |
cfef4373 | 238 | docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE; |
743467cb JH |
239 | if (cnp->cn_nameiop == DELETE || |
240 | (wantparent && cnp->cn_nameiop != CREATE)) | |
955e7a55 | 241 | docache = 0; |
cfef4373 | 242 | rdonly = cnp->cn_flags & RDONLY; |
955e7a55 | 243 | ndp->ni_dvp = NULL; |
cfef4373 | 244 | cnp->cn_flags &= ~ISSYMLINK; |
955e7a55 KM |
245 | dp = ndp->ni_startdir; |
246 | ndp->ni_startdir = NULLVP; | |
247 | VOP_LOCK(dp); | |
248 | ||
6bd0bb92 | 249 | dirloop: |
6bd0bb92 | 250 | /* |
955e7a55 KM |
251 | * Search a new directory. |
252 | * | |
cfef4373 | 253 | * The cn_hash value is for use by vfs_cache. |
955e7a55 | 254 | * The last component of the filename is left accessible via |
cfef4373 | 255 | * cnp->cn_nameptr for callers that need the name. Callers needing |
955e7a55 KM |
256 | * the name set the SAVENAME flag. When done, they assume |
257 | * responsibility for freeing the pathname buffer. | |
6bd0bb92 | 258 | */ |
cfef4373 JH |
259 | cnp->cn_hash = 0; |
260 | for (cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++) | |
261 | cnp->cn_hash += (unsigned char)*cp; | |
262 | cnp->cn_namelen = cp - cnp->cn_nameptr; | |
263 | if (cnp->cn_namelen >= NAME_MAX) { | |
955e7a55 KM |
264 | error = ENAMETOOLONG; |
265 | goto bad; | |
266 | } | |
6d0f0ece | 267 | #ifdef NAMEI_DIAGNOSTIC |
955e7a55 KM |
268 | { char c = *cp; |
269 | *cp = '\0'; | |
cfef4373 | 270 | printf("{%s}: ", cnp->cn_nameptr); |
955e7a55 | 271 | *cp = c; } |
6d0f0ece | 272 | #endif |
cfef4373 | 273 | ndp->ni_pathlen -= cnp->cn_namelen; |
955e7a55 | 274 | ndp->ni_next = cp; |
cfef4373 | 275 | cnp->cn_flags |= MAKEENTRY; |
87c05e6e | 276 | if (*cp == '\0' && docache == 0) |
cfef4373 JH |
277 | cnp->cn_flags &= ~MAKEENTRY; |
278 | if (cnp->cn_namelen == 2 && | |
279 | cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.') | |
280 | cnp->cn_flags |= ISDOTDOT; | |
281 | else cnp->cn_flags &= ~ISDOTDOT; | |
282 | if (*ndp->ni_next == 0) | |
283 | cnp->cn_flags |= ISLASTCN; | |
284 | else cnp->cn_flags &= ~ISLASTCN; | |
285 | ||
6bd0bb92 BJ |
286 | |
287 | /* | |
288 | * Check for degenerate name (e.g. / or "") | |
289 | * which is a way of talking about a directory, | |
290 | * e.g. like "/." or ".". | |
291 | */ | |
cfef4373 JH |
292 | if (cnp->cn_nameptr[0] == '\0') { |
293 | if (cnp->cn_nameiop != LOOKUP || wantparent) { | |
6d0f0ece | 294 | error = EISDIR; |
6bd0bb92 | 295 | goto bad; |
f5039631 | 296 | } |
955e7a55 KM |
297 | if (dp->v_type != VDIR) { |
298 | error = ENOTDIR; | |
299 | goto bad; | |
300 | } | |
cfef4373 | 301 | if (!(cnp->cn_flags & LOCKLEAF)) |
6d0f0ece KM |
302 | VOP_UNLOCK(dp); |
303 | ndp->ni_vp = dp; | |
cfef4373 | 304 | if (cnp->cn_flags & SAVESTART) |
955e7a55 | 305 | panic("lookup: SAVESTART"); |
743467cb | 306 | return (0); |
f5039631 | 307 | } |
6bd0bb92 | 308 | |
e47da406 | 309 | /* |
6d0f0ece KM |
310 | * Handle "..": two special cases. |
311 | * 1. If at root directory (e.g. after chroot) | |
312 | * then ignore it so can't get out. | |
313 | * 2. If this vnode is the root of a mounted | |
955e7a55 | 314 | * filesystem, then replace it with the |
6d0f0ece KM |
315 | * vnode which was mounted on so we take the |
316 | * .. in the other file system. | |
e47da406 | 317 | */ |
cfef4373 | 318 | if (cnp->cn_flags & ISDOTDOT) { |
e47da406 | 319 | for (;;) { |
955e7a55 | 320 | if (dp == ndp->ni_rootdir) { |
6d0f0ece | 321 | ndp->ni_dvp = dp; |
7655c64a | 322 | ndp->ni_vp = dp; |
8fe1c702 | 323 | VREF(dp); |
6d0f0ece | 324 | goto nextname; |
e47da406 | 325 | } |
d5cea2aa | 326 | if ((dp->v_flag & VROOT) == 0 || |
cfef4373 | 327 | (cnp->cn_flags & NOCROSSMOUNT)) |
e47da406 | 328 | break; |
6d0f0ece | 329 | tdp = dp; |
54fb9dc2 | 330 | dp = dp->v_mount->mnt_vnodecovered; |
6d0f0ece | 331 | vput(tdp); |
8fe1c702 | 332 | VREF(dp); |
7655c64a | 333 | VOP_LOCK(dp); |
e47da406 KM |
334 | } |
335 | } | |
336 | ||
f93197fc KM |
337 | /* |
338 | * We now have a segment name to search for, and a directory to search. | |
6459ebe0 | 339 | */ |
cfef4373 JH |
340 | ndp->ni_dvp = dp; |
341 | if (error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) { | |
955e7a55 | 342 | #ifdef DIAGNOSTIC |
6d0f0ece KM |
343 | if (ndp->ni_vp != NULL) |
344 | panic("leaf should be empty"); | |
955e7a55 | 345 | #endif |
658f5fdc | 346 | #ifdef NAMEI_DIAGNOSTIC |
6d0f0ece | 347 | printf("not found\n"); |
658f5fdc | 348 | #endif |
39a892ba | 349 | if (error != EJUSTRETURN) |
1f6ef9f5 | 350 | goto bad; |
f5039631 | 351 | /* |
6d0f0ece KM |
352 | * If creating and at end of pathname, then can consider |
353 | * allowing file to be created. | |
f5039631 | 354 | */ |
6c235bc4 | 355 | if (rdonly || (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY)) { |
6d0f0ece | 356 | error = EROFS; |
6bd0bb92 | 357 | goto bad; |
1f6ef9f5 | 358 | } |
f5039631 | 359 | /* |
6d0f0ece KM |
360 | * We return with ni_vp NULL to indicate that the entry |
361 | * doesn't currently exist, leaving a pointer to the | |
362 | * (possibly locked) directory inode in ndp->ni_dvp. | |
f5039631 | 363 | */ |
cfef4373 | 364 | if (cnp->cn_flags & SAVESTART) { |
955e7a55 KM |
365 | ndp->ni_startdir = ndp->ni_dvp; |
366 | VREF(ndp->ni_startdir); | |
b8c8091e | 367 | p->p_spare[1]++; |
955e7a55 | 368 | } |
743467cb | 369 | return (0); |
6bd0bb92 | 370 | } |
658f5fdc | 371 | #ifdef NAMEI_DIAGNOSTIC |
6d0f0ece | 372 | printf("found\n"); |
658f5fdc | 373 | #endif |
6bd0bb92 | 374 | |
955e7a55 | 375 | dp = ndp->ni_vp; |
6bd0bb92 | 376 | /* |
6d0f0ece | 377 | * Check for symbolic link |
4f083fd7 | 378 | */ |
6d0f0ece | 379 | if ((dp->v_type == VLNK) && |
cfef4373 JH |
380 | ((cnp->cn_flags & FOLLOW) || *ndp->ni_next == '/')) { |
381 | cnp->cn_flags |= ISSYMLINK; | |
743467cb | 382 | return (0); |
f93197fc KM |
383 | } |
384 | ||
6bd0bb92 | 385 | /* |
6d0f0ece KM |
386 | * Check to see if the vnode has been mounted on; |
387 | * if so find the root of the mounted file system. | |
6bd0bb92 | 388 | */ |
d5cea2aa | 389 | while (dp->v_type == VDIR && (mp = dp->v_mountedhere) && |
cfef4373 | 390 | (cnp->cn_flags & NOCROSSMOUNT) == 0) { |
17c70d83 | 391 | if (mp->mnt_flag & MNT_MLOCK) { |
54fb9dc2 | 392 | mp->mnt_flag |= MNT_MWAIT; |
6d0f0ece | 393 | sleep((caddr_t)mp, PVFS); |
74f8a498 | 394 | continue; |
6bd0bb92 | 395 | } |
955e7a55 | 396 | if (error = VFS_ROOT(dp->v_mountedhere, &tdp)) |
6bd0bb92 | 397 | goto bad2; |
6d0f0ece KM |
398 | vput(dp); |
399 | ndp->ni_vp = dp = tdp; | |
10873320 | 400 | } |
6bd0bb92 | 401 | |
6d0f0ece | 402 | nextname: |
10873320 | 403 | /* |
6bd0bb92 BJ |
404 | * Not a symbolic link. If more pathname, |
405 | * continue at next component, else return. | |
10873320 | 406 | */ |
955e7a55 | 407 | if (*ndp->ni_next == '/') { |
cfef4373 JH |
408 | cnp->cn_nameptr = ndp->ni_next; |
409 | while (*cnp->cn_nameptr == '/') { | |
410 | cnp->cn_nameptr++; | |
6d0f0ece | 411 | ndp->ni_pathlen--; |
6bd0bb92 | 412 | } |
6d0f0ece KM |
413 | vrele(ndp->ni_dvp); |
414 | goto dirloop; | |
6bd0bb92 BJ |
415 | } |
416 | /* | |
01633fea | 417 | * Check for read-only file systems. |
6bd0bb92 | 418 | */ |
cfef4373 | 419 | if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) { |
dfc0e8dd | 420 | /* |
01633fea KM |
421 | * Disallow directory write attempts on read-only |
422 | * file systems. | |
dfc0e8dd | 423 | */ |
6c235bc4 KM |
424 | if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) || |
425 | (wantparent && | |
426 | (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY))) { | |
dfc0e8dd KM |
427 | error = EROFS; |
428 | goto bad2; | |
429 | } | |
430 | } | |
cfef4373 | 431 | if (cnp->cn_flags & SAVESTART) { |
955e7a55 | 432 | ndp->ni_startdir = ndp->ni_dvp; |
b8c8091e | 433 | p->p_spare[1]++; |
955e7a55 KM |
434 | VREF(ndp->ni_startdir); |
435 | } | |
6d0f0ece KM |
436 | if (!wantparent) |
437 | vrele(ndp->ni_dvp); | |
cfef4373 | 438 | if ((cnp->cn_flags & LOCKLEAF) == 0) |
6d0f0ece | 439 | VOP_UNLOCK(dp); |
743467cb | 440 | return (0); |
4f083fd7 | 441 | |
6d0f0ece | 442 | bad2: |
cfef4373 | 443 | if ((cnp->cn_flags & LOCKPARENT) && *ndp->ni_next == '\0') |
206be3f9 | 444 | VOP_UNLOCK(ndp->ni_dvp); |
6d0f0ece KM |
445 | vrele(ndp->ni_dvp); |
446 | bad: | |
447 | vput(dp); | |
448 | ndp->ni_vp = NULL; | |
743467cb | 449 | return (error); |
b1aa93b9 | 450 | } |
cfef4373 JH |
451 | |
452 |