vfs_lookup.c revision 1.16 1 /* $NetBSD: vfs_lookup.c,v 1.16 1996/02/04 02:18:25 christos Exp $ */
2
3 /*
4 * Copyright (c) 1982, 1986, 1989, 1993
5 * The Regents of the University of California. All rights reserved.
6 * (c) UNIX System Laboratories, Inc.
7 * All or some portions of this file are derived from material licensed
8 * to the University of California by American Telephone and Telegraph
9 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
10 * the permission of UNIX System Laboratories, Inc.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the University of
23 * California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 *
40 * @(#)vfs_lookup.c 8.6 (Berkeley) 11/21/94
41 */
42
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/syslimits.h>
46 #include <sys/time.h>
47 #include <sys/namei.h>
48 #include <sys/vnode.h>
49 #include <sys/mount.h>
50 #include <sys/errno.h>
51 #include <sys/malloc.h>
52 #include <sys/filedesc.h>
53 #include <sys/proc.h>
54
55 #ifdef KTRACE
56 #include <sys/ktrace.h>
57 #endif
58
59 #include <kern/kern_extern.h>
60
61 /*
62 * Convert a pathname into a pointer to a locked inode.
63 *
64 * The FOLLOW flag is set when symbolic links are to be followed
65 * when they occur at the end of the name translation process.
66 * Symbolic links are always followed for all other pathname
67 * components other than the last.
68 *
69 * The segflg defines whether the name is to be copied from user
70 * space or kernel space.
71 *
72 * Overall outline of namei:
73 *
74 * copy in name
75 * get starting directory
76 * while (!done && !error) {
77 * call lookup to search path.
78 * if symbolic link, massage name in buffer and continue
79 * }
80 */
81 int
82 namei(ndp)
83 register struct nameidata *ndp;
84 {
85 register struct filedesc *fdp; /* pointer to file descriptor state */
86 register char *cp; /* pointer into pathname argument */
87 register struct vnode *dp; /* the directory we are searching */
88 struct iovec aiov; /* uio for reading symbolic links */
89 struct uio auio;
90 int error, linklen;
91 struct componentname *cnp = &ndp->ni_cnd;
92
93 ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred;
94 #ifdef DIAGNOSTIC
95 if (!cnp->cn_cred || !cnp->cn_proc)
96 panic ("namei: bad cred/proc");
97 if (cnp->cn_nameiop & (~OPMASK))
98 panic ("namei: nameiop contaminated with flags");
99 if (cnp->cn_flags & OPMASK)
100 panic ("namei: flags contaminated with nameiops");
101 #endif
102 fdp = cnp->cn_proc->p_fd;
103
104 /*
105 * Get a buffer for the name to be translated, and copy the
106 * name into the buffer.
107 */
108 if ((cnp->cn_flags & HASBUF) == 0)
109 MALLOC(cnp->cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
110 if (ndp->ni_segflg == UIO_SYSSPACE)
111 error = copystr(ndp->ni_dirp, cnp->cn_pnbuf,
112 MAXPATHLEN, &ndp->ni_pathlen);
113 else
114 error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf,
115 MAXPATHLEN, &ndp->ni_pathlen);
116 if (error) {
117 free(cnp->cn_pnbuf, M_NAMEI);
118 ndp->ni_vp = NULL;
119 return (error);
120 }
121 ndp->ni_loopcnt = 0;
122 #ifdef KTRACE
123 if (KTRPOINT(cnp->cn_proc, KTR_NAMEI))
124 ktrnamei(cnp->cn_proc->p_tracep, cnp->cn_pnbuf);
125 #endif
126
127 /*
128 * Get starting point for the translation.
129 */
130 if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL)
131 ndp->ni_rootdir = rootvnode;
132 dp = fdp->fd_cdir;
133 VREF(dp);
134 for (;;) {
135 /*
136 * Check if root directory should replace current directory.
137 * Done at start of translation and after symbolic link.
138 */
139 cnp->cn_nameptr = cnp->cn_pnbuf;
140 if (*(cnp->cn_nameptr) == '/') {
141 vrele(dp);
142 while (*(cnp->cn_nameptr) == '/') {
143 cnp->cn_nameptr++;
144 ndp->ni_pathlen--;
145 }
146 dp = ndp->ni_rootdir;
147 VREF(dp);
148 }
149 ndp->ni_startdir = dp;
150 if ((error = lookup(ndp)) != 0) {
151 FREE(cnp->cn_pnbuf, M_NAMEI);
152 return (error);
153 }
154 /*
155 * Check for symbolic link
156 */
157 if ((cnp->cn_flags & ISSYMLINK) == 0) {
158 if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0)
159 FREE(cnp->cn_pnbuf, M_NAMEI);
160 else
161 cnp->cn_flags |= HASBUF;
162 return (0);
163 }
164 if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
165 VOP_UNLOCK(ndp->ni_dvp);
166 if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
167 error = ELOOP;
168 break;
169 }
170 if (ndp->ni_pathlen > 1)
171 MALLOC(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
172 else
173 cp = cnp->cn_pnbuf;
174 aiov.iov_base = cp;
175 aiov.iov_len = MAXPATHLEN;
176 auio.uio_iov = &aiov;
177 auio.uio_iovcnt = 1;
178 auio.uio_offset = 0;
179 auio.uio_rw = UIO_READ;
180 auio.uio_segflg = UIO_SYSSPACE;
181 auio.uio_procp = (struct proc *)0;
182 auio.uio_resid = MAXPATHLEN;
183 error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
184 if (error) {
185 if (ndp->ni_pathlen > 1)
186 free(cp, M_NAMEI);
187 break;
188 }
189 linklen = MAXPATHLEN - auio.uio_resid;
190 if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
191 if (ndp->ni_pathlen > 1)
192 free(cp, M_NAMEI);
193 error = ENAMETOOLONG;
194 break;
195 }
196 if (ndp->ni_pathlen > 1) {
197 bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
198 FREE(cnp->cn_pnbuf, M_NAMEI);
199 cnp->cn_pnbuf = cp;
200 } else
201 cnp->cn_pnbuf[linklen] = '\0';
202 ndp->ni_pathlen += linklen;
203 vput(ndp->ni_vp);
204 dp = ndp->ni_dvp;
205 }
206 FREE(cnp->cn_pnbuf, M_NAMEI);
207 vrele(ndp->ni_dvp);
208 vput(ndp->ni_vp);
209 ndp->ni_vp = NULL;
210 return (error);
211 }
212
213 /*
214 * Search a pathname.
215 * This is a very central and rather complicated routine.
216 *
217 * The pathname is pointed to by ni_ptr and is of length ni_pathlen.
218 * The starting directory is taken from ni_startdir. The pathname is
219 * descended until done, or a symbolic link is encountered. The variable
220 * ni_more is clear if the path is completed; it is set to one if a
221 * symbolic link needing interpretation is encountered.
222 *
223 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
224 * whether the name is to be looked up, created, renamed, or deleted.
225 * When CREATE, RENAME, or DELETE is specified, information usable in
226 * creating, renaming, or deleting a directory entry may be calculated.
227 * If flag has LOCKPARENT or'ed into it, the parent directory is returned
228 * locked. If flag has WANTPARENT or'ed into it, the parent directory is
229 * returned unlocked. Otherwise the parent directory is not returned. If
230 * the target of the pathname exists and LOCKLEAF is or'ed into the flag
231 * the target is returned locked, otherwise it is returned unlocked.
232 * When creating or renaming and LOCKPARENT is specified, the target may not
233 * be ".". When deleting and LOCKPARENT is specified, the target may be ".".
234 *
235 * Overall outline of lookup:
236 *
237 * dirloop:
238 * identify next component of name at ndp->ni_ptr
239 * handle degenerate case where name is null string
240 * if .. and crossing mount points and on mounted filesys, find parent
241 * call VOP_LOOKUP routine for next component name
242 * directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set
243 * component vnode returned in ni_vp (if it exists), locked.
244 * if result vnode is mounted on and crossing mount points,
245 * find mounted on vnode
246 * if more components of name, do next level at dirloop
247 * return the answer in ni_vp, locked if LOCKLEAF set
248 * if LOCKPARENT set, return locked parent in ni_dvp
249 * if WANTPARENT set, return unlocked parent in ni_dvp
250 */
251 int
252 lookup(ndp)
253 register struct nameidata *ndp;
254 {
255 register char *cp; /* pointer into pathname argument */
256 register struct vnode *dp = 0; /* the directory we are searching */
257 struct vnode *tdp; /* saved dp */
258 struct mount *mp; /* mount table entry */
259 int docache; /* == 0 do not cache last component */
260 int wantparent; /* 1 => wantparent or lockparent flag */
261 int rdonly; /* lookup read-only flag bit */
262 int error = 0;
263 struct componentname *cnp = &ndp->ni_cnd;
264
265 /*
266 * Setup: break out flag bits into variables.
267 */
268 wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
269 docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
270 if (cnp->cn_nameiop == DELETE ||
271 (wantparent && cnp->cn_nameiop != CREATE))
272 docache = 0;
273 rdonly = cnp->cn_flags & RDONLY;
274 ndp->ni_dvp = NULL;
275 cnp->cn_flags &= ~ISSYMLINK;
276 dp = ndp->ni_startdir;
277 ndp->ni_startdir = NULLVP;
278 VOP_LOCK(dp);
279
280 dirloop:
281 /*
282 * Search a new directory.
283 *
284 * The cn_hash value is for use by vfs_cache.
285 * The last component of the filename is left accessible via
286 * cnp->cn_nameptr for callers that need the name. Callers needing
287 * the name set the SAVENAME flag. When done, they assume
288 * responsibility for freeing the pathname buffer.
289 */
290 cnp->cn_consume = 0;
291 cnp->cn_hash = 0;
292 for (cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++)
293 cnp->cn_hash += (unsigned char)*cp;
294 cnp->cn_namelen = cp - cnp->cn_nameptr;
295 if (cnp->cn_namelen > NAME_MAX) {
296 error = ENAMETOOLONG;
297 goto bad;
298 }
299 #ifdef NAMEI_DIAGNOSTIC
300 { char c = *cp;
301 *cp = '\0';
302 printf("{%s}: ", cnp->cn_nameptr);
303 *cp = c; }
304 #endif
305 ndp->ni_pathlen -= cnp->cn_namelen;
306 ndp->ni_next = cp;
307 cnp->cn_flags |= MAKEENTRY;
308 if (*cp == '\0' && docache == 0)
309 cnp->cn_flags &= ~MAKEENTRY;
310 if (cnp->cn_namelen == 2 &&
311 cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
312 cnp->cn_flags |= ISDOTDOT;
313 else
314 cnp->cn_flags &= ~ISDOTDOT;
315 if (*ndp->ni_next == 0)
316 cnp->cn_flags |= ISLASTCN;
317 else
318 cnp->cn_flags &= ~ISLASTCN;
319
320
321 /*
322 * Check for degenerate name (e.g. / or "")
323 * which is a way of talking about a directory,
324 * e.g. like "/." or ".".
325 */
326 if (cnp->cn_nameptr[0] == '\0') {
327 if (dp->v_type != VDIR) {
328 error = ENOTDIR;
329 goto bad;
330 }
331 if (cnp->cn_nameiop != LOOKUP) {
332 error = EISDIR;
333 goto bad;
334 }
335 if (wantparent) {
336 ndp->ni_dvp = dp;
337 VREF(dp);
338 }
339 ndp->ni_vp = dp;
340 if (!(cnp->cn_flags & (LOCKPARENT | LOCKLEAF)))
341 VOP_UNLOCK(dp);
342 if (cnp->cn_flags & SAVESTART)
343 panic("lookup: SAVESTART");
344 return (0);
345 }
346
347 /*
348 * Handle "..": two special cases.
349 * 1. If at root directory (e.g. after chroot)
350 * or at absolute root directory
351 * then ignore it so can't get out.
352 * 2. If this vnode is the root of a mounted
353 * filesystem, then replace it with the
354 * vnode which was mounted on so we take the
355 * .. in the other file system.
356 */
357 if (cnp->cn_flags & ISDOTDOT) {
358 for (;;) {
359 if (dp == ndp->ni_rootdir || dp == rootvnode) {
360 ndp->ni_dvp = dp;
361 ndp->ni_vp = dp;
362 VREF(dp);
363 goto nextname;
364 }
365 if ((dp->v_flag & VROOT) == 0 ||
366 (cnp->cn_flags & NOCROSSMOUNT))
367 break;
368 tdp = dp;
369 dp = dp->v_mount->mnt_vnodecovered;
370 vput(tdp);
371 VREF(dp);
372 VOP_LOCK(dp);
373 }
374 }
375
376 /*
377 * We now have a segment name to search for, and a directory to search.
378 */
379 unionlookup:
380 ndp->ni_dvp = dp;
381 if ((error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) != 0) {
382 #ifdef DIAGNOSTIC
383 if (ndp->ni_vp != NULL)
384 panic("leaf should be empty");
385 #endif
386 #ifdef NAMEI_DIAGNOSTIC
387 printf("not found\n");
388 #endif
389 if ((error == ENOENT) &&
390 (dp->v_flag & VROOT) &&
391 (dp->v_mount->mnt_flag & MNT_UNION)) {
392 tdp = dp;
393 dp = dp->v_mount->mnt_vnodecovered;
394 vput(tdp);
395 VREF(dp);
396 VOP_LOCK(dp);
397 goto unionlookup;
398 }
399
400 if (error != EJUSTRETURN)
401 goto bad;
402 /*
403 * If creating and at end of pathname, then can consider
404 * allowing file to be created.
405 */
406 if (rdonly || (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY)) {
407 error = EROFS;
408 goto bad;
409 }
410 /*
411 * We return with ni_vp NULL to indicate that the entry
412 * doesn't currently exist, leaving a pointer to the
413 * (possibly locked) directory inode in ndp->ni_dvp.
414 */
415 if (cnp->cn_flags & SAVESTART) {
416 ndp->ni_startdir = ndp->ni_dvp;
417 VREF(ndp->ni_startdir);
418 }
419 return (0);
420 }
421 #ifdef NAMEI_DIAGNOSTIC
422 printf("found\n");
423 #endif
424
425 /*
426 * Take into account any additional components consumed by
427 * the underlying filesystem.
428 */
429 if (cnp->cn_consume > 0) {
430 cnp->cn_nameptr += cnp->cn_consume;
431 ndp->ni_next += cnp->cn_consume;
432 ndp->ni_pathlen -= cnp->cn_consume;
433 cnp->cn_consume = 0;
434 }
435
436 dp = ndp->ni_vp;
437 /*
438 * Check to see if the vnode has been mounted on;
439 * if so find the root of the mounted file system.
440 */
441 while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
442 (cnp->cn_flags & NOCROSSMOUNT) == 0) {
443 if (mp->mnt_flag & MNT_MLOCK) {
444 mp->mnt_flag |= MNT_MWAIT;
445 sleep((caddr_t)mp, PVFS);
446 continue;
447 }
448 if ((error = VFS_ROOT(dp->v_mountedhere, &tdp)) != 0)
449 goto bad2;
450 vput(dp);
451 ndp->ni_vp = dp = tdp;
452 }
453
454 /*
455 * Check for symbolic link
456 */
457 if ((dp->v_type == VLNK) &&
458 ((cnp->cn_flags & FOLLOW) || *ndp->ni_next == '/')) {
459 cnp->cn_flags |= ISSYMLINK;
460 return (0);
461 }
462
463 nextname:
464 /*
465 * Not a symbolic link. If more pathname,
466 * continue at next component, else return.
467 */
468 if (*ndp->ni_next == '/') {
469 cnp->cn_nameptr = ndp->ni_next;
470 while (*cnp->cn_nameptr == '/') {
471 cnp->cn_nameptr++;
472 ndp->ni_pathlen--;
473 }
474 vrele(ndp->ni_dvp);
475 goto dirloop;
476 }
477 /*
478 * Check for read-only file systems.
479 */
480 if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
481 /*
482 * Disallow directory write attempts on read-only
483 * file systems.
484 */
485 if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) ||
486 (wantparent &&
487 (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY))) {
488 error = EROFS;
489 goto bad2;
490 }
491 }
492 if (cnp->cn_flags & SAVESTART) {
493 ndp->ni_startdir = ndp->ni_dvp;
494 VREF(ndp->ni_startdir);
495 }
496 if (!wantparent)
497 vrele(ndp->ni_dvp);
498 if ((cnp->cn_flags & LOCKLEAF) == 0)
499 VOP_UNLOCK(dp);
500 return (0);
501
502 bad2:
503 if ((cnp->cn_flags & LOCKPARENT) && *ndp->ni_next == '\0')
504 VOP_UNLOCK(ndp->ni_dvp);
505 vrele(ndp->ni_dvp);
506 bad:
507 vput(dp);
508 ndp->ni_vp = NULL;
509 return (error);
510 }
511
512 /*
513 * Reacquire a path name component.
514 */
515 int
516 relookup(dvp, vpp, cnp)
517 struct vnode *dvp, **vpp;
518 struct componentname *cnp;
519 {
520 register struct vnode *dp = 0; /* the directory we are searching */
521 int docache; /* == 0 do not cache last component */
522 int wantparent; /* 1 => wantparent or lockparent flag */
523 int rdonly; /* lookup read-only flag bit */
524 int error = 0;
525 #ifdef NAMEI_DIAGNOSTIC
526 int newhash; /* DEBUG: check name hash */
527 char *cp; /* DEBUG: check name ptr/len */
528 #endif
529
530 /*
531 * Setup: break out flag bits into variables.
532 */
533 wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT);
534 docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
535 if (cnp->cn_nameiop == DELETE ||
536 (wantparent && cnp->cn_nameiop != CREATE))
537 docache = 0;
538 rdonly = cnp->cn_flags & RDONLY;
539 cnp->cn_flags &= ~ISSYMLINK;
540 dp = dvp;
541 VOP_LOCK(dp);
542
543 /* dirloop: */
544 /*
545 * Search a new directory.
546 *
547 * The cn_hash value is for use by vfs_cache.
548 * The last component of the filename is left accessible via
549 * cnp->cn_nameptr for callers that need the name. Callers needing
550 * the name set the SAVENAME flag. When done, they assume
551 * responsibility for freeing the pathname buffer.
552 */
553 #ifdef NAMEI_DIAGNOSTIC
554 for (newhash = 0, cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++)
555 newhash += (unsigned char)*cp;
556 if (newhash != cnp->cn_hash)
557 panic("relookup: bad hash");
558 if (cnp->cn_namelen != cp - cnp->cn_nameptr)
559 panic ("relookup: bad len");
560 if (*cp != 0)
561 panic("relookup: not last component");
562 printf("{%s}: ", cnp->cn_nameptr);
563 #endif
564
565 /*
566 * Check for degenerate name (e.g. / or "")
567 * which is a way of talking about a directory,
568 * e.g. like "/." or ".".
569 */
570 if (cnp->cn_nameptr[0] == '\0') {
571 if (dp->v_type != VDIR) {
572 error = ENOTDIR;
573 goto bad;
574 }
575 if (cnp->cn_nameiop != LOOKUP || wantparent) {
576 error = EISDIR;
577 goto bad;
578 }
579 if (!(cnp->cn_flags & LOCKLEAF))
580 VOP_UNLOCK(dp);
581 *vpp = dp;
582 if (cnp->cn_flags & SAVESTART)
583 panic("lookup: SAVESTART");
584 return (0);
585 }
586
587 if (cnp->cn_flags & ISDOTDOT)
588 panic ("relookup: lookup on dot-dot");
589
590 /*
591 * We now have a segment name to search for, and a directory to search.
592 */
593 if ((error = VOP_LOOKUP(dp, vpp, cnp)) != 0) {
594 #ifdef DIAGNOSTIC
595 if (*vpp != NULL)
596 panic("leaf should be empty");
597 #endif
598 if (error != EJUSTRETURN)
599 goto bad;
600 /*
601 * If creating and at end of pathname, then can consider
602 * allowing file to be created.
603 */
604 if (rdonly || (dvp->v_mount->mnt_flag & MNT_RDONLY)) {
605 error = EROFS;
606 goto bad;
607 }
608 /* ASSERT(dvp == ndp->ni_startdir) */
609 if (cnp->cn_flags & SAVESTART)
610 VREF(dvp);
611 /*
612 * We return with ni_vp NULL to indicate that the entry
613 * doesn't currently exist, leaving a pointer to the
614 * (possibly locked) directory inode in ndp->ni_dvp.
615 */
616 return (0);
617 }
618 dp = *vpp;
619
620 #ifdef DIAGNOSTIC
621 /*
622 * Check for symbolic link
623 */
624 if (dp->v_type == VLNK && (cnp->cn_flags & FOLLOW))
625 panic ("relookup: symlink found.\n");
626 #endif
627
628 /*
629 * Check for read-only file systems.
630 */
631 if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
632 /*
633 * Disallow directory write attempts on read-only
634 * file systems.
635 */
636 if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) ||
637 (wantparent &&
638 (dvp->v_mount->mnt_flag & MNT_RDONLY))) {
639 error = EROFS;
640 goto bad2;
641 }
642 }
643 /* ASSERT(dvp == ndp->ni_startdir) */
644 if (cnp->cn_flags & SAVESTART)
645 VREF(dvp);
646
647 if (!wantparent)
648 vrele(dvp);
649 if ((cnp->cn_flags & LOCKLEAF) == 0)
650 VOP_UNLOCK(dp);
651 return (0);
652
653 bad2:
654 if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN))
655 VOP_UNLOCK(dvp);
656 vrele(dvp);
657 bad:
658 vput(dp);
659 *vpp = NULL;
660 return (error);
661 }
662