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