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