Annotation of sys/ufs/ufs/ufs_lookup.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: ufs_lookup.c,v 1.37 2007/06/01 23:47:57 deraadt Exp $ */
! 2: /* $NetBSD: ufs_lookup.c,v 1.7 1996/02/09 22:36:06 christos Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 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: * @(#)ufs_lookup.c 8.9 (Berkeley) 8/11/94
! 38: */
! 39:
! 40: #include <sys/param.h>
! 41: #include <sys/systm.h>
! 42: #include <sys/kernel.h>
! 43: #include <sys/namei.h>
! 44: #include <sys/buf.h>
! 45: #include <sys/file.h>
! 46: #include <sys/stat.h>
! 47: #include <sys/mount.h>
! 48: #include <sys/vnode.h>
! 49:
! 50: #include <uvm/uvm_extern.h>
! 51:
! 52: #include <ufs/ufs/quota.h>
! 53: #include <ufs/ufs/inode.h>
! 54: #include <ufs/ufs/dir.h>
! 55: #ifdef UFS_DIRHASH
! 56: #include <ufs/ufs/dirhash.h>
! 57: #endif
! 58: #include <ufs/ufs/ufsmount.h>
! 59: #include <ufs/ufs/ufs_extern.h>
! 60:
! 61: extern struct nchstats nchstats;
! 62:
! 63: #ifdef DIAGNOSTIC
! 64: int dirchk = 1;
! 65: #else
! 66: int dirchk = 0;
! 67: #endif
! 68:
! 69: #define FSFMT(vp) ((vp)->v_mount->mnt_maxsymlinklen <= 0)
! 70:
! 71: /*
! 72: * Convert a component of a pathname into a pointer to a locked inode.
! 73: * This is a very central and rather complicated routine.
! 74: * If the file system is not maintained in a strict tree hierarchy,
! 75: * this can result in a deadlock situation (see comments in code below).
! 76: *
! 77: * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
! 78: * on whether the name is to be looked up, created, renamed, or deleted.
! 79: * When CREATE, RENAME, or DELETE is specified, information usable in
! 80: * creating, renaming, or deleting a directory entry may be calculated.
! 81: * If flag has LOCKPARENT or'ed into it and the target of the pathname
! 82: * exists, lookup returns both the target and its parent directory locked.
! 83: * When creating or renaming and LOCKPARENT is specified, the target may
! 84: * not be ".". When deleting and LOCKPARENT is specified, the target may
! 85: * be "."., but the caller must check to ensure it does an vrele and vput
! 86: * instead of two vputs.
! 87: *
! 88: * Overall outline of ufs_lookup:
! 89: *
! 90: * check accessibility of directory
! 91: * look for name in cache, if found, then if at end of path
! 92: * and deleting or creating, drop it, else return name
! 93: * search for name in directory, to found or notfound
! 94: * notfound:
! 95: * if creating, return locked directory, leaving info on available slots
! 96: * else return error
! 97: * found:
! 98: * if at end of path and deleting, return information to allow delete
! 99: * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
! 100: * inode and return info to allow rewrite
! 101: * if not at end, add name to cache; if at end and neither creating
! 102: * nor deleting, add name to cache
! 103: */
! 104: int
! 105: ufs_lookup(void *v)
! 106: {
! 107: struct vop_lookup_args *ap = v;
! 108: struct vnode *vdp; /* vnode for directory being searched */
! 109: struct inode *dp; /* inode for directory being searched */
! 110: struct buf *bp; /* a buffer of directory entries */
! 111: struct direct *ep; /* the current directory entry */
! 112: int entryoffsetinblock; /* offset of ep in bp's buffer */
! 113: enum {NONE, COMPACT, FOUND} slotstatus;
! 114: doff_t slotoffset; /* offset of area with free space */
! 115: int slotsize; /* size of area at slotoffset */
! 116: int slotfreespace; /* amount of space free in slot */
! 117: int slotneeded; /* size of the entry we're seeking */
! 118: int numdirpasses; /* strategy for directory search */
! 119: doff_t endsearch; /* offset to end directory search */
! 120: doff_t prevoff; /* prev entry dp->i_offset */
! 121: struct vnode *pdp; /* saved dp during symlink work */
! 122: struct vnode *tdp; /* returned by VFS_VGET */
! 123: doff_t enduseful; /* pointer past last used dir slot */
! 124: u_long bmask; /* block offset mask */
! 125: int lockparent; /* 1 => lockparent flag is set */
! 126: int wantparent; /* 1 => wantparent or lockparent flag */
! 127: int namlen, error;
! 128: struct vnode **vpp = ap->a_vpp;
! 129: struct componentname *cnp = ap->a_cnp;
! 130: struct ucred *cred = cnp->cn_cred;
! 131: int flags;
! 132: int nameiop = cnp->cn_nameiop;
! 133: struct proc *p = cnp->cn_proc;
! 134:
! 135: cnp->cn_flags &= ~PDIRUNLOCK;
! 136: flags = cnp->cn_flags;
! 137:
! 138: bp = NULL;
! 139: slotoffset = -1;
! 140: *vpp = NULL;
! 141: vdp = ap->a_dvp;
! 142: dp = VTOI(vdp);
! 143: lockparent = flags & LOCKPARENT;
! 144: wantparent = flags & (LOCKPARENT|WANTPARENT);
! 145:
! 146: /*
! 147: * Check accessiblity of directory.
! 148: */
! 149: if ((DIP(dp, mode) & IFMT) != IFDIR)
! 150: return (ENOTDIR);
! 151: if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) != 0)
! 152: return (error);
! 153:
! 154: if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
! 155: (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
! 156: return (EROFS);
! 157:
! 158: /*
! 159: * We now have a segment name to search for, and a directory to search.
! 160: *
! 161: * Before tediously performing a linear scan of the directory,
! 162: * check the name cache to see if the directory/name pair
! 163: * we are looking for is known already.
! 164: */
! 165: if ((error = cache_lookup(vdp, vpp, cnp)) >= 0)
! 166: return (error);
! 167:
! 168: /*
! 169: * Suppress search for slots unless creating
! 170: * file and at end of pathname, in which case
! 171: * we watch for a place to put the new file in
! 172: * case it doesn't already exist.
! 173: */
! 174: slotstatus = FOUND;
! 175: slotfreespace = slotsize = slotneeded = 0;
! 176: if ((nameiop == CREATE || nameiop == RENAME) &&
! 177: (flags & ISLASTCN)) {
! 178: slotstatus = NONE;
! 179: slotneeded = (sizeof(struct direct) - MAXNAMLEN +
! 180: cnp->cn_namelen + 3) &~ 3;
! 181: }
! 182:
! 183: /*
! 184: * If there is cached information on a previous search of
! 185: * this directory, pick up where we last left off.
! 186: * We cache only lookups as these are the most common
! 187: * and have the greatest payoff. Caching CREATE has little
! 188: * benefit as it usually must search the entire directory
! 189: * to determine that the entry does not exist. Caching the
! 190: * location of the last DELETE or RENAME has not reduced
! 191: * profiling time and hence has been removed in the interest
! 192: * of simplicity.
! 193: */
! 194: bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
! 195:
! 196: #ifdef UFS_DIRHASH
! 197: /*
! 198: * Use dirhash for fast operations on large directories. The logic
! 199: * to determine whether to hash the directory is contained within
! 200: * ufsdirhash_build(); a zero return means that it decided to hash
! 201: * this directory and it successfully built up the hash table.
! 202: */
! 203: if (ufsdirhash_build(dp) == 0) {
! 204: /* Look for a free slot if needed. */
! 205: enduseful = DIP(dp, size);
! 206: if (slotstatus != FOUND) {
! 207: slotoffset = ufsdirhash_findfree(dp, slotneeded,
! 208: &slotsize);
! 209: if (slotoffset >= 0) {
! 210: slotstatus = COMPACT;
! 211: enduseful = ufsdirhash_enduseful(dp);
! 212: if (enduseful < 0)
! 213: enduseful = DIP(dp, size);
! 214: }
! 215: }
! 216: /* Look up the component. */
! 217: numdirpasses = 1;
! 218: entryoffsetinblock = 0; /* silence compiler warning */
! 219: switch (ufsdirhash_lookup(dp, cnp->cn_nameptr, cnp->cn_namelen,
! 220: &dp->i_offset, &bp, nameiop == DELETE ? &prevoff : NULL)) {
! 221: case 0:
! 222: ep = (struct direct *)((char *)bp->b_data +
! 223: (dp->i_offset & bmask));
! 224: goto foundentry;
! 225: case ENOENT:
! 226: #define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */
! 227: dp->i_offset = roundup2(DIP(dp, size), DIRBLKSIZ);
! 228: goto notfound;
! 229: default:
! 230: /* Something failed; just do a linear search. */
! 231: break;
! 232: }
! 233: }
! 234: #endif /* UFS_DIRHASH */
! 235:
! 236: if (nameiop != LOOKUP || dp->i_diroff == 0 ||
! 237: dp->i_diroff >= DIP(dp, size)) {
! 238: entryoffsetinblock = 0;
! 239: dp->i_offset = 0;
! 240: numdirpasses = 1;
! 241: } else {
! 242: dp->i_offset = dp->i_diroff;
! 243: if ((entryoffsetinblock = dp->i_offset & bmask) &&
! 244: (error = UFS_BUFATOFF(dp, (off_t)dp->i_offset, NULL, &bp)))
! 245: return (error);
! 246: numdirpasses = 2;
! 247: nchstats.ncs_2passes++;
! 248: }
! 249: prevoff = dp->i_offset;
! 250: endsearch = roundup(DIP(dp, size), DIRBLKSIZ);
! 251: enduseful = 0;
! 252:
! 253: searchloop:
! 254: while (dp->i_offset < endsearch) {
! 255: /*
! 256: * If necessary, get the next directory block.
! 257: */
! 258: if ((dp->i_offset & bmask) == 0) {
! 259: if (bp != NULL)
! 260: brelse(bp);
! 261: error = UFS_BUFATOFF(dp, (off_t)dp->i_offset, NULL,
! 262: &bp);
! 263: if (error)
! 264: return (error);
! 265: entryoffsetinblock = 0;
! 266: }
! 267: /*
! 268: * If still looking for a slot, and at a DIRBLKSIZE
! 269: * boundary, have to start looking for free space again.
! 270: */
! 271: if (slotstatus == NONE &&
! 272: (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
! 273: slotoffset = -1;
! 274: slotfreespace = 0;
! 275: }
! 276: /*
! 277: * Get pointer to next entry.
! 278: * Full validation checks are slow, so we only check
! 279: * enough to insure forward progress through the
! 280: * directory. Complete checks can be run by patching
! 281: * "dirchk" to be true.
! 282: */
! 283: ep = (struct direct *)((char *)bp->b_data + entryoffsetinblock);
! 284: if (ep->d_reclen == 0 ||
! 285: (dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock))) {
! 286: int i;
! 287:
! 288: ufs_dirbad(dp, dp->i_offset, "mangled entry");
! 289: i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
! 290: dp->i_offset += i;
! 291: entryoffsetinblock += i;
! 292: continue;
! 293: }
! 294:
! 295: /*
! 296: * If an appropriate sized slot has not yet been found,
! 297: * check to see if one is available. Also accumulate space
! 298: * in the current block so that we can determine if
! 299: * compaction is viable.
! 300: */
! 301: if (slotstatus != FOUND) {
! 302: int size = ep->d_reclen;
! 303:
! 304: if (ep->d_ino != 0)
! 305: size -= DIRSIZ(FSFMT(vdp), ep);
! 306: if (size > 0) {
! 307: if (size >= slotneeded) {
! 308: slotstatus = FOUND;
! 309: slotoffset = dp->i_offset;
! 310: slotsize = ep->d_reclen;
! 311: } else if (slotstatus == NONE) {
! 312: slotfreespace += size;
! 313: if (slotoffset == -1)
! 314: slotoffset = dp->i_offset;
! 315: if (slotfreespace >= slotneeded) {
! 316: slotstatus = COMPACT;
! 317: slotsize = dp->i_offset +
! 318: ep->d_reclen - slotoffset;
! 319: }
! 320: }
! 321: }
! 322: }
! 323:
! 324: /*
! 325: * Check for a name match.
! 326: */
! 327: if (ep->d_ino) {
! 328: # if (BYTE_ORDER == LITTLE_ENDIAN)
! 329: if (vdp->v_mount->mnt_maxsymlinklen > 0)
! 330: namlen = ep->d_namlen;
! 331: else
! 332: namlen = ep->d_type;
! 333: # else
! 334: namlen = ep->d_namlen;
! 335: # endif
! 336: if (namlen == cnp->cn_namelen &&
! 337: !bcmp(cnp->cn_nameptr, ep->d_name,
! 338: (unsigned)namlen)) {
! 339: #ifdef UFS_DIRHASH
! 340: foundentry:
! 341: #endif
! 342: /*
! 343: * Save directory entry's inode number and
! 344: * reclen in ndp->ni_ufs area, and release
! 345: * directory buffer.
! 346: */
! 347: dp->i_ino = ep->d_ino;
! 348: dp->i_reclen = ep->d_reclen;
! 349: goto found;
! 350: }
! 351: }
! 352: prevoff = dp->i_offset;
! 353: dp->i_offset += ep->d_reclen;
! 354: entryoffsetinblock += ep->d_reclen;
! 355: if (ep->d_ino)
! 356: enduseful = dp->i_offset;
! 357: }
! 358: #ifdef UFS_DIRHASH
! 359: notfound:
! 360: #endif
! 361: /*
! 362: * If we started in the middle of the directory and failed
! 363: * to find our target, we must check the beginning as well.
! 364: */
! 365: if (numdirpasses == 2) {
! 366: numdirpasses--;
! 367: dp->i_offset = 0;
! 368: endsearch = dp->i_diroff;
! 369: goto searchloop;
! 370: }
! 371: if (bp != NULL)
! 372: brelse(bp);
! 373: /*
! 374: * If creating, and at end of pathname and current
! 375: * directory has not been removed, then can consider
! 376: * allowing file to be created.
! 377: */
! 378: if ((nameiop == CREATE || nameiop == RENAME) &&
! 379: (flags & ISLASTCN) && dp->i_effnlink != 0) {
! 380: /*
! 381: * Access for write is interpreted as allowing
! 382: * creation of files in the directory.
! 383: */
! 384: error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc);
! 385: if (error)
! 386: return (error);
! 387: /*
! 388: * Return an indication of where the new directory
! 389: * entry should be put. If we didn't find a slot,
! 390: * then set dp->i_count to 0 indicating
! 391: * that the new slot belongs at the end of the
! 392: * directory. If we found a slot, then the new entry
! 393: * can be put in the range from dp->i_offset to
! 394: * dp->i_offset + dp->i_count.
! 395: */
! 396: if (slotstatus == NONE) {
! 397: dp->i_offset = roundup(DIP(dp, size), DIRBLKSIZ);
! 398: dp->i_count = 0;
! 399: enduseful = dp->i_offset;
! 400: } else if (nameiop == DELETE) {
! 401: dp->i_offset = slotoffset;
! 402: if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
! 403: dp->i_count = 0;
! 404: else
! 405: dp->i_count = dp->i_offset - prevoff;
! 406: } else {
! 407: dp->i_offset = slotoffset;
! 408: dp->i_count = slotsize;
! 409: if (enduseful < slotoffset + slotsize)
! 410: enduseful = slotoffset + slotsize;
! 411: }
! 412: dp->i_endoff = roundup(enduseful, DIRBLKSIZ);
! 413: /*
! 414: * We return with the directory locked, so that
! 415: * the parameters we set up above will still be
! 416: * valid if we actually decide to do a direnter().
! 417: * We return ni_vp == NULL to indicate that the entry
! 418: * does not currently exist; we leave a pointer to
! 419: * the (locked) directory inode in ndp->ni_dvp.
! 420: * The pathname buffer is saved so that the name
! 421: * can be obtained later.
! 422: *
! 423: * NB - if the directory is unlocked, then this
! 424: * information cannot be used.
! 425: */
! 426: cnp->cn_flags |= SAVENAME;
! 427: if (!lockparent) {
! 428: VOP_UNLOCK(vdp, 0, p);
! 429: cnp->cn_flags |= PDIRUNLOCK;
! 430: }
! 431: return (EJUSTRETURN);
! 432: }
! 433: /*
! 434: * Insert name into cache (as non-existent) if appropriate.
! 435: */
! 436: if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
! 437: cache_enter(vdp, *vpp, cnp);
! 438: return (ENOENT);
! 439:
! 440: found:
! 441: if (numdirpasses == 2)
! 442: nchstats.ncs_pass2++;
! 443: /*
! 444: * Check that directory length properly reflects presence
! 445: * of this entry.
! 446: */
! 447: if (dp->i_offset + DIRSIZ(FSFMT(vdp), ep) > DIP(dp, size)) {
! 448: ufs_dirbad(dp, dp->i_offset, "i_ffs_size too small");
! 449: DIP_ASSIGN(dp, size, dp->i_offset + DIRSIZ(FSFMT(vdp), ep));
! 450: dp->i_flag |= IN_CHANGE | IN_UPDATE;
! 451: }
! 452: brelse(bp);
! 453:
! 454: /*
! 455: * Found component in pathname.
! 456: * If the final component of path name, save information
! 457: * in the cache as to where the entry was found.
! 458: */
! 459: if ((flags & ISLASTCN) && nameiop == LOOKUP)
! 460: dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1);
! 461:
! 462: /*
! 463: * If deleting, and at end of pathname, return
! 464: * parameters which can be used to remove file.
! 465: * If the wantparent flag isn't set, we return only
! 466: * the directory (in ndp->ni_dvp), otherwise we go
! 467: * on and lock the inode, being careful with ".".
! 468: */
! 469: if (nameiop == DELETE && (flags & ISLASTCN)) {
! 470: /*
! 471: * Write access to directory required to delete files.
! 472: */
! 473: error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc);
! 474: if (error)
! 475: return (error);
! 476: /*
! 477: * Return pointer to current entry in dp->i_offset,
! 478: * and distance past previous entry (if there
! 479: * is a previous entry in this block) in dp->i_count.
! 480: * Save directory inode pointer in ndp->ni_dvp for dirremove().
! 481: */
! 482: if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
! 483: dp->i_count = 0;
! 484: else
! 485: dp->i_count = dp->i_offset - prevoff;
! 486: if (dp->i_number == dp->i_ino) {
! 487: VREF(vdp);
! 488: *vpp = vdp;
! 489: return (0);
! 490: }
! 491: error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp);
! 492: if (error)
! 493: return (error);
! 494: /*
! 495: * If directory is "sticky", then user must own
! 496: * the directory, or the file in it, else she
! 497: * may not delete it (unless she's root). This
! 498: * implements append-only directories.
! 499: */
! 500: if ((DIP(dp, mode) & ISVTX) &&
! 501: cred->cr_uid != 0 &&
! 502: cred->cr_uid != DIP(dp, uid) &&
! 503: DIP(VTOI(tdp), uid) != cred->cr_uid) {
! 504: vput(tdp);
! 505: return (EPERM);
! 506: }
! 507: *vpp = tdp;
! 508: if (!lockparent) {
! 509: VOP_UNLOCK(vdp, 0, p);
! 510: cnp->cn_flags |= PDIRUNLOCK;
! 511: }
! 512: return (0);
! 513: }
! 514:
! 515: /*
! 516: * If rewriting (RENAME), return the inode and the
! 517: * information required to rewrite the present directory
! 518: * Must get inode of directory entry to verify it's a
! 519: * regular file, or empty directory.
! 520: */
! 521: if (nameiop == RENAME && wantparent &&
! 522: (flags & ISLASTCN)) {
! 523: error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc);
! 524: if (error)
! 525: return (error);
! 526: /*
! 527: * Careful about locking second inode.
! 528: * This can only occur if the target is ".".
! 529: */
! 530: if (dp->i_number == dp->i_ino)
! 531: return (EISDIR);
! 532: error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp);
! 533: if (error)
! 534: return (error);
! 535: *vpp = tdp;
! 536: cnp->cn_flags |= SAVENAME;
! 537: if (!lockparent) {
! 538: VOP_UNLOCK(vdp, 0, p);
! 539: cnp->cn_flags |= PDIRUNLOCK;
! 540: }
! 541: return (0);
! 542: }
! 543:
! 544: /*
! 545: * Step through the translation in the name. We do not `vput' the
! 546: * directory because we may need it again if a symbolic link
! 547: * is relative to the current directory. Instead we save it
! 548: * unlocked as "pdp". We must get the target inode before unlocking
! 549: * the directory to insure that the inode will not be removed
! 550: * before we get it. We prevent deadlock by always fetching
! 551: * inodes from the root, moving down the directory tree. Thus
! 552: * when following backward pointers ".." we must unlock the
! 553: * parent directory before getting the requested directory.
! 554: * There is a potential race condition here if both the current
! 555: * and parent directories are removed before the VFS_VGET for the
! 556: * inode associated with ".." returns. We hope that this occurs
! 557: * infrequently since we cannot avoid this race condition without
! 558: * implementing a sophisticated deadlock detection algorithm.
! 559: * Note also that this simple deadlock detection scheme will not
! 560: * work if the file system has any hard links other than ".."
! 561: * that point backwards in the directory structure.
! 562: */
! 563: pdp = vdp;
! 564: if (flags & ISDOTDOT) {
! 565: VOP_UNLOCK(pdp, 0, p); /* race to get the inode */
! 566: cnp->cn_flags |= PDIRUNLOCK;
! 567: error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp);
! 568: if (error) {
! 569: if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p) == 0)
! 570: cnp->cn_flags &= ~PDIRUNLOCK;
! 571: return (error);
! 572: }
! 573: if (lockparent && (flags & ISLASTCN)) {
! 574: if ((error = vn_lock(pdp, LK_EXCLUSIVE, p))) {
! 575: vput(tdp);
! 576: return (error);
! 577: }
! 578: cnp->cn_flags &= ~PDIRUNLOCK;
! 579: }
! 580: *vpp = tdp;
! 581: } else if (dp->i_number == dp->i_ino) {
! 582: VREF(vdp); /* we want ourself, ie "." */
! 583: *vpp = vdp;
! 584: } else {
! 585: error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp);
! 586: if (error)
! 587: return (error);
! 588: if (!lockparent || !(flags & ISLASTCN)) {
! 589: VOP_UNLOCK(pdp, 0, p);
! 590: cnp->cn_flags |= PDIRUNLOCK;
! 591: }
! 592: *vpp = tdp;
! 593: }
! 594:
! 595: /*
! 596: * Insert name into cache if appropriate.
! 597: */
! 598: if (cnp->cn_flags & MAKEENTRY)
! 599: cache_enter(vdp, *vpp, cnp);
! 600: return (0);
! 601: }
! 602:
! 603: void
! 604: ufs_dirbad(struct inode *ip, doff_t offset, char *how)
! 605: {
! 606: struct mount *mp;
! 607:
! 608: mp = ITOV(ip)->v_mount;
! 609: (void)printf("%s: bad dir ino %d at offset %d: %s\n",
! 610: mp->mnt_stat.f_mntonname, ip->i_number, offset, how);
! 611: if ((mp->mnt_stat.f_flags & MNT_RDONLY) == 0)
! 612: panic("bad dir");
! 613: }
! 614:
! 615: /*
! 616: * Do consistency checking on a directory entry:
! 617: * record length must be multiple of 4
! 618: * entry must fit in rest of its DIRBLKSIZ block
! 619: * record must be large enough to contain entry
! 620: * name is not longer than MAXNAMLEN
! 621: * name must be as long as advertised, and null terminated
! 622: */
! 623: int
! 624: ufs_dirbadentry(struct vnode *dp, struct direct *ep, int entryoffsetinblock)
! 625: {
! 626: int i;
! 627: int namlen;
! 628:
! 629: # if (BYTE_ORDER == LITTLE_ENDIAN)
! 630: if (dp->v_mount->mnt_maxsymlinklen > 0)
! 631: namlen = ep->d_namlen;
! 632: else
! 633: namlen = ep->d_type;
! 634: # else
! 635: namlen = ep->d_namlen;
! 636: # endif
! 637: if ((ep->d_reclen & 0x3) != 0 ||
! 638: ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) ||
! 639: ep->d_reclen < DIRSIZ(FSFMT(dp), ep) || namlen > MAXNAMLEN) {
! 640: /*return (1); */
! 641: printf("First bad\n");
! 642: goto bad;
! 643: }
! 644: if (ep->d_ino == 0)
! 645: return (0);
! 646: for (i = 0; i < namlen; i++)
! 647: if (ep->d_name[i] == '\0') {
! 648: /*return (1); */
! 649: printf("Second bad\n");
! 650: goto bad;
! 651: }
! 652: if (ep->d_name[i])
! 653: goto bad;
! 654: return (0);
! 655: bad:
! 656: return (1);
! 657: }
! 658:
! 659: /*
! 660: * Construct a new directory entry after a call to namei, using the
! 661: * parameters that it left in the componentname argument cnp. The
! 662: * argument ip is the inode to which the new directory entry will refer.
! 663: */
! 664: void
! 665: ufs_makedirentry(struct inode *ip, struct componentname *cnp,
! 666: struct direct *newdirp)
! 667: {
! 668: #ifdef DIAGNOSTIC
! 669: if ((cnp->cn_flags & SAVENAME) == 0)
! 670: panic("ufs_makedirentry: missing name");
! 671: #endif
! 672: newdirp->d_ino = ip->i_number;
! 673: newdirp->d_namlen = cnp->cn_namelen;
! 674: bcopy(cnp->cn_nameptr, newdirp->d_name, (unsigned)cnp->cn_namelen + 1);
! 675: if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0)
! 676: newdirp->d_type = IFTODT(DIP(ip, mode));
! 677: else {
! 678: newdirp->d_type = 0;
! 679: # if (BYTE_ORDER == LITTLE_ENDIAN)
! 680: { u_char tmp = newdirp->d_namlen;
! 681: newdirp->d_namlen = newdirp->d_type;
! 682: newdirp->d_type = tmp; }
! 683: # endif
! 684: }
! 685: }
! 686:
! 687: /*
! 688: * Write a directory entry after a call to namei, using the parameters
! 689: * that it left in nameidata. The argument dirp is the new directory
! 690: * entry contents. Dvp is a pointer to the directory to be written,
! 691: * which was left locked by namei. Remaining parameters (dp->i_offset,
! 692: * dp->i_count) indicate how the space for the new entry is to be obtained.
! 693: * Non-null bp indicates that a directory is being created (for the
! 694: * soft dependency code).
! 695: */
! 696: int
! 697: ufs_direnter(struct vnode *dvp, struct vnode *tvp, struct direct *dirp,
! 698: struct componentname *cnp, struct buf *newdirbp)
! 699: {
! 700: struct ucred *cr;
! 701: struct proc *p;
! 702: int newentrysize;
! 703: struct inode *dp;
! 704: struct buf *bp;
! 705: u_int dsize;
! 706: struct direct *ep, *nep;
! 707: int error, ret, blkoff, loc, spacefree, flags;
! 708: char *dirbuf;
! 709:
! 710: error = 0;
! 711: cr = cnp->cn_cred;
! 712: p = cnp->cn_proc;
! 713: dp = VTOI(dvp);
! 714: newentrysize = DIRSIZ(FSFMT(dvp), dirp);
! 715:
! 716: if (dp->i_count == 0) {
! 717: /*
! 718: * If dp->i_count is 0, then namei could find no
! 719: * space in the directory. Here, dp->i_offset will
! 720: * be on a directory block boundary and we will write the
! 721: * new entry into a fresh block.
! 722: */
! 723: if (dp->i_offset & (DIRBLKSIZ - 1))
! 724: panic("ufs_direnter: newblk");
! 725: flags = B_CLRBUF;
! 726: if (!DOINGSOFTDEP(dvp))
! 727: flags |= B_SYNC;
! 728: if ((error = UFS_BUF_ALLOC(dp, (off_t)dp->i_offset, DIRBLKSIZ,
! 729: cr, flags, &bp)) != 0) {
! 730: if (DOINGSOFTDEP(dvp) && newdirbp != NULL)
! 731: bdwrite(newdirbp);
! 732: return (error);
! 733: }
! 734: DIP_ASSIGN(dp, size, dp->i_offset + DIRBLKSIZ);
! 735: dp->i_flag |= IN_CHANGE | IN_UPDATE;
! 736: uvm_vnp_setsize(dvp, DIP(dp, size));
! 737: dirp->d_reclen = DIRBLKSIZ;
! 738: blkoff = dp->i_offset &
! 739: (VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_iosize - 1);
! 740: bcopy((caddr_t)dirp, (caddr_t)bp->b_data + blkoff,newentrysize);
! 741:
! 742: #ifdef UFS_DIRHASH
! 743: if (dp->i_dirhash != NULL) {
! 744: ufsdirhash_newblk(dp, dp->i_offset);
! 745: ufsdirhash_add(dp, dirp, dp->i_offset);
! 746: ufsdirhash_checkblock(dp, (char *)bp->b_data + blkoff,
! 747: dp->i_offset);
! 748: }
! 749: #endif
! 750:
! 751: if (DOINGSOFTDEP(dvp)) {
! 752: /*
! 753: * Ensure that the entire newly allocated block is a
! 754: * valid directory so that future growth within the
! 755: * block does not have to ensure that the block is
! 756: * written before the inode.
! 757: */
! 758: blkoff += DIRBLKSIZ;
! 759: while (blkoff < bp->b_bcount) {
! 760: ((struct direct *)
! 761: (bp->b_data + blkoff))->d_reclen = DIRBLKSIZ;
! 762: blkoff += DIRBLKSIZ;
! 763: }
! 764: if (softdep_setup_directory_add(bp, dp, dp->i_offset,
! 765: dirp->d_ino, newdirbp, 1) == 0) {
! 766: bdwrite(bp);
! 767: return (UFS_UPDATE(dp, 0));
! 768: }
! 769: /* We have just allocated a directory block in an
! 770: * indirect block. Rather than tracking when it gets
! 771: * claimed by the inode, we simply do a VOP_FSYNC
! 772: * now to ensure that it is there (in case the user
! 773: * does a future fsync). Note that we have to unlock
! 774: * the inode for the entry that we just entered, as
! 775: * the VOP_FSYNC may need to lock other inodes which
! 776: * can lead to deadlock if we also hold a lock on
! 777: * the newly entered node.
! 778: */
! 779: if ((error = VOP_BWRITE(bp)))
! 780: return (error);
! 781: if (tvp != NULL)
! 782: VOP_UNLOCK(tvp, 0, p);
! 783: error = VOP_FSYNC(dvp, p->p_ucred, MNT_WAIT, p);
! 784: if (tvp != NULL)
! 785: vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p);
! 786: return (error);
! 787: }
! 788: error = VOP_BWRITE(bp);
! 789: ret = UFS_UPDATE(dp, !DOINGSOFTDEP(dvp));
! 790: if (error == 0)
! 791: return (ret);
! 792: return (error);
! 793: }
! 794:
! 795: /*
! 796: * If dp->i_count is non-zero, then namei found space for the new
! 797: * entry in the range dp->i_offset to dp->i_offset + dp->i_count
! 798: * in the directory. To use this space, we may have to compact
! 799: * the entries located there, by copying them together towards the
! 800: * beginning of the block, leaving the free space in one usable
! 801: * chunk at the end.
! 802: */
! 803:
! 804: /*
! 805: * Increase size of directory if entry eats into new space.
! 806: * This should never push the size past a new multiple of
! 807: * DIRBLKSIZE.
! 808: *
! 809: * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
! 810: */
! 811: if (dp->i_offset + dp->i_count > DIP(dp, size))
! 812: DIP_ASSIGN(dp, size, dp->i_offset + dp->i_count);
! 813: /*
! 814: * Get the block containing the space for the new directory entry.
! 815: */
! 816: if ((error = UFS_BUFATOFF(dp, (off_t)dp->i_offset, &dirbuf, &bp))
! 817: != 0) {
! 818: if (DOINGSOFTDEP(dvp) && newdirbp != NULL)
! 819: bdwrite(newdirbp);
! 820: return (error);
! 821: }
! 822: /*
! 823: * Find space for the new entry. In the simple case, the entry at
! 824: * offset base will have the space. If it does not, then namei
! 825: * arranged that compacting the region dp->i_offset to
! 826: * dp->i_offset + dp->i_count would yield the space.
! 827: */
! 828: ep = (struct direct *)dirbuf;
! 829: dsize = ep->d_ino ? DIRSIZ(FSFMT(dvp), ep) : 0;
! 830: spacefree = ep->d_reclen - dsize;
! 831: for (loc = ep->d_reclen; loc < dp->i_count; ) {
! 832: nep = (struct direct *)(dirbuf + loc);
! 833:
! 834: /* Trim the existing slot (NB: dsize may be zero). */
! 835: ep->d_reclen = dsize;
! 836: ep = (struct direct *)((char *)ep + dsize);
! 837:
! 838: /* Read nep->d_reclen now as the bcopy() may clobber it. */
! 839: loc += nep->d_reclen;
! 840: if (nep->d_ino == 0) {
! 841: /*
! 842: * A mid-block unused entry. Such entries are
! 843: * never created by the kernel, but fsck_ffs
! 844: * can create them (and it doesn't fix them).
! 845: *
! 846: * Add up the free space, and initialise the
! 847: * relocated entry since we don't bcopy it.
! 848: */
! 849: spacefree += nep->d_reclen;
! 850: ep->d_ino = 0;
! 851: dsize = 0;
! 852: continue;
! 853: }
! 854: dsize = DIRSIZ(FSFMT(dvp), nep);
! 855: spacefree += nep->d_reclen - dsize;
! 856: #ifdef UFS_DIRHASH
! 857: if (dp->i_dirhash != NULL)
! 858: ufsdirhash_move(dp, nep,
! 859: dp->i_offset + ((char *)nep - dirbuf),
! 860: dp->i_offset + ((char *)ep - dirbuf));
! 861: #endif
! 862: if (DOINGSOFTDEP(dvp))
! 863: softdep_change_directoryentry_offset(dp, dirbuf,
! 864: (caddr_t)nep, (caddr_t)ep, dsize);
! 865: else
! 866: bcopy((caddr_t)nep, (caddr_t)ep, dsize);
! 867: }
! 868: /*
! 869: * Here, `ep' points to a directory entry containing `dsize' in-use
! 870: * bytes followed by `spacefree' unused bytes. If ep->d_ino == 0,
! 871: * then the entry is completely unused (dsize == 0). The value
! 872: * of ep->d_reclen is always indeterminate.
! 873: *
! 874: * Update the pointer fields in the previous entry (if any),
! 875: * copy in the new entry, and write out the block.
! 876: */
! 877: if (ep->d_ino == 0) {
! 878: if (spacefree + dsize < newentrysize)
! 879: panic("ufs_direnter: compact1");
! 880: dirp->d_reclen = spacefree + dsize;
! 881: } else {
! 882: if (spacefree < newentrysize)
! 883: panic("ufs_direnter: compact2");
! 884: dirp->d_reclen = spacefree;
! 885: ep->d_reclen = dsize;
! 886: ep = (struct direct *)((char *)ep + dsize);
! 887: }
! 888:
! 889: #ifdef UFS_DIRHASH
! 890: if (dp->i_dirhash != NULL && (ep->d_ino == 0 ||
! 891: dirp->d_reclen == spacefree))
! 892: ufsdirhash_add(dp, dirp, dp->i_offset + ((char *)ep - dirbuf));
! 893: #endif
! 894: bcopy((caddr_t)dirp, (caddr_t)ep, (u_int)newentrysize);
! 895: #ifdef UFS_DIRHASH
! 896: if (dp->i_dirhash != NULL)
! 897: ufsdirhash_checkblock(dp, dirbuf -
! 898: (dp->i_offset & (DIRBLKSIZ - 1)),
! 899: dp->i_offset & ~(DIRBLKSIZ - 1));
! 900: #endif
! 901:
! 902: if (DOINGSOFTDEP(dvp)) {
! 903: (void)softdep_setup_directory_add(bp, dp,
! 904: dp->i_offset + (caddr_t)ep - dirbuf,
! 905: dirp->d_ino, newdirbp, 0);
! 906: bdwrite(bp);
! 907: } else {
! 908: error = VOP_BWRITE(bp);
! 909: }
! 910: dp->i_flag |= IN_CHANGE | IN_UPDATE;
! 911:
! 912: /*
! 913: * If all went well, and the directory can be shortened, proceed
! 914: * with the truncation. Note that we have to unlock the inode for
! 915: * the entry that we just entered, as the truncation may need to
! 916: * lock other inodes which can lead to deadlock if we also hold a
! 917: * lock on the newly entered node.
! 918: */
! 919:
! 920: if (error == 0 && dp->i_endoff && dp->i_endoff < DIP(dp, size)) {
! 921: if (tvp != NULL)
! 922: VOP_UNLOCK(tvp, 0, p);
! 923: #ifdef UFS_DIRHASH
! 924: if (dp->i_dirhash != NULL)
! 925: ufsdirhash_dirtrunc(dp, dp->i_endoff);
! 926: #endif
! 927:
! 928:
! 929: error = UFS_TRUNCATE(dp, (off_t)dp->i_endoff, IO_SYNC, cr);
! 930:
! 931: if (tvp != NULL)
! 932: vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p);
! 933: }
! 934: return (error);
! 935: }
! 936:
! 937: /*
! 938: * Remove a directory entry after a call to namei, using
! 939: * the parameters which it left in nameidata. The entry
! 940: * dp->i_offset contains the offset into the directory of the
! 941: * entry to be eliminated. The dp->i_count field contains the
! 942: * size of the previous record in the directory. If this
! 943: * is 0, the first entry is being deleted, so we need only
! 944: * zero the inode number to mark the entry as free. If the
! 945: * entry is not the first in the directory, we must reclaim
! 946: * the space of the now empty record by adding the record size
! 947: * to the size of the previous entry.
! 948: */
! 949: int
! 950: ufs_dirremove(struct vnode *dvp, struct inode *ip, int flags, int isrmdir)
! 951: {
! 952: struct inode *dp;
! 953: struct direct *ep;
! 954: struct buf *bp;
! 955: int error;
! 956:
! 957: dp = VTOI(dvp);
! 958:
! 959: if ((error = UFS_BUFATOFF(dp,
! 960: (off_t)(dp->i_offset - dp->i_count), (char **)&ep, &bp)) != 0)
! 961: return (error);
! 962: #ifdef UFS_DIRHASH
! 963: /*
! 964: * Remove the dirhash entry. This is complicated by the fact
! 965: * that `ep' is the previous entry when dp->i_count != 0.
! 966: */
! 967: if (dp->i_dirhash != NULL)
! 968: ufsdirhash_remove(dp, (dp->i_count == 0) ? ep :
! 969: (struct direct *)((char *)ep + ep->d_reclen), dp->i_offset);
! 970: #endif
! 971:
! 972: if (dp->i_count == 0) {
! 973: /*
! 974: * First entry in block: set d_ino to zero.
! 975: */
! 976: ep->d_ino = 0;
! 977: } else {
! 978: /*
! 979: * Collapse new free space into previous entry.
! 980: */
! 981: ep->d_reclen += dp->i_reclen;
! 982: }
! 983: #ifdef UFS_DIRHASH
! 984: if (dp->i_dirhash != NULL)
! 985: ufsdirhash_checkblock(dp, (char *)ep -
! 986: ((dp->i_offset - dp->i_count) & (DIRBLKSIZ - 1)),
! 987: dp->i_offset & ~(DIRBLKSIZ - 1));
! 988: #endif
! 989: if (DOINGSOFTDEP(dvp)) {
! 990: if (ip) {
! 991: ip->i_effnlink--;
! 992: softdep_change_linkcnt(ip, 0);
! 993: softdep_setup_remove(bp, dp, ip, isrmdir);
! 994: }
! 995: if (softdep_slowdown(dvp)) {
! 996: error = bwrite(bp);
! 997: } else {
! 998: bdwrite(bp);
! 999: error = 0;
! 1000: }
! 1001: } else {
! 1002: if (ip) {
! 1003: ip->i_effnlink--;
! 1004: DIP_ADD(ip, nlink, -1);
! 1005: ip->i_flag |= IN_CHANGE;
! 1006: }
! 1007: if (DOINGASYNC(dvp) && dp->i_count != 0) {
! 1008: bdwrite(bp);
! 1009: error = 0;
! 1010: } else
! 1011: error = bwrite(bp);
! 1012: }
! 1013: dp->i_flag |= IN_CHANGE | IN_UPDATE;
! 1014: return (error);
! 1015: }
! 1016:
! 1017: /*
! 1018: * Rewrite an existing directory entry to point at the inode
! 1019: * supplied. The parameters describing the directory entry are
! 1020: * set up by a call to namei.
! 1021: */
! 1022: int
! 1023: ufs_dirrewrite(struct inode *dp, struct inode *oip, ino_t newinum, int newtype,
! 1024: int isrmdir)
! 1025: {
! 1026: struct buf *bp;
! 1027: struct direct *ep;
! 1028: struct vnode *vdp = ITOV(dp);
! 1029: int error;
! 1030:
! 1031: error = UFS_BUFATOFF(dp, (off_t)dp->i_offset, (char **)&ep, &bp);
! 1032: if (error)
! 1033: return (error);
! 1034: ep->d_ino = newinum;
! 1035: if (vdp->v_mount->mnt_maxsymlinklen > 0)
! 1036: ep->d_type = newtype;
! 1037: oip->i_effnlink--;
! 1038: if (DOINGSOFTDEP(vdp)) {
! 1039: softdep_change_linkcnt(oip, 0);
! 1040: softdep_setup_directory_change(bp, dp, oip, newinum, isrmdir);
! 1041: bdwrite(bp);
! 1042: } else {
! 1043: DIP_ADD(oip, nlink, -1);
! 1044: oip->i_flag |= IN_CHANGE;
! 1045: if (DOINGASYNC(vdp)) {
! 1046: bdwrite(bp);
! 1047: error = 0;
! 1048: } else {
! 1049: error = VOP_BWRITE(bp);
! 1050: }
! 1051: }
! 1052: dp->i_flag |= IN_CHANGE | IN_UPDATE;
! 1053: return (error);
! 1054: }
! 1055:
! 1056: /*
! 1057: * Check if a directory is empty or not.
! 1058: * Inode supplied must be locked.
! 1059: *
! 1060: * Using a struct dirtemplate here is not precisely
! 1061: * what we want, but better than using a struct direct.
! 1062: *
! 1063: * NB: does not handle corrupted directories.
! 1064: */
! 1065: int
! 1066: ufs_dirempty(struct inode *ip, ino_t parentino, struct ucred *cred)
! 1067: {
! 1068: off_t off, m;
! 1069: struct dirtemplate dbuf;
! 1070: struct direct *dp = (struct direct *)&dbuf;
! 1071: int error, namlen;
! 1072: size_t count;
! 1073: #define MINDIRSIZ (sizeof (struct dirtemplate) / 2)
! 1074:
! 1075: m = DIP(ip, size);
! 1076: for (off = 0; off < m; off += dp->d_reclen) {
! 1077: error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off,
! 1078: UIO_SYSSPACE, IO_NODELOCKED, cred, &count, (struct proc *)0);
! 1079: /*
! 1080: * Since we read MINDIRSIZ, residual must
! 1081: * be 0 unless we're at end of file.
! 1082: */
! 1083: if (error || count != 0)
! 1084: return (0);
! 1085: /* avoid infinite loops */
! 1086: if (dp->d_reclen == 0)
! 1087: return (0);
! 1088: /* skip empty entries */
! 1089: if (dp->d_ino == 0)
! 1090: continue;
! 1091: /* accept only "." and ".." */
! 1092: # if (BYTE_ORDER == LITTLE_ENDIAN)
! 1093: if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0)
! 1094: namlen = dp->d_namlen;
! 1095: else
! 1096: namlen = dp->d_type;
! 1097: # else
! 1098: namlen = dp->d_namlen;
! 1099: # endif
! 1100: if (namlen > 2)
! 1101: return (0);
! 1102: if (dp->d_name[0] != '.')
! 1103: return (0);
! 1104: /*
! 1105: * At this point namlen must be 1 or 2.
! 1106: * 1 implies ".", 2 implies ".." if second
! 1107: * char is also "."
! 1108: */
! 1109: if (namlen == 1 && dp->d_ino == ip->i_number)
! 1110: continue;
! 1111: if (dp->d_name[1] == '.' && dp->d_ino == parentino)
! 1112: continue;
! 1113: return (0);
! 1114: }
! 1115: return (1);
! 1116: }
! 1117:
! 1118: /*
! 1119: * Check if source directory is in the path of the target directory.
! 1120: * Target is supplied locked, source is unlocked.
! 1121: * The target is always vput before returning.
! 1122: */
! 1123: int
! 1124: ufs_checkpath(struct inode *source, struct inode *target, struct ucred *cred)
! 1125: {
! 1126: struct vnode *vp;
! 1127: int error, rootino, namlen;
! 1128: struct dirtemplate dirbuf;
! 1129:
! 1130: vp = ITOV(target);
! 1131: if (target->i_number == source->i_number) {
! 1132: error = EEXIST;
! 1133: goto out;
! 1134: }
! 1135: rootino = ROOTINO;
! 1136: error = 0;
! 1137: if (target->i_number == rootino)
! 1138: goto out;
! 1139:
! 1140: for (;;) {
! 1141: if (vp->v_type != VDIR) {
! 1142: error = ENOTDIR;
! 1143: break;
! 1144: }
! 1145: error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
! 1146: sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
! 1147: IO_NODELOCKED, cred, NULL, (struct proc *)0);
! 1148: if (error != 0)
! 1149: break;
! 1150: # if (BYTE_ORDER == LITTLE_ENDIAN)
! 1151: if (vp->v_mount->mnt_maxsymlinklen > 0)
! 1152: namlen = dirbuf.dotdot_namlen;
! 1153: else
! 1154: namlen = dirbuf.dotdot_type;
! 1155: # else
! 1156: namlen = dirbuf.dotdot_namlen;
! 1157: # endif
! 1158: if (namlen != 2 ||
! 1159: dirbuf.dotdot_name[0] != '.' ||
! 1160: dirbuf.dotdot_name[1] != '.') {
! 1161: error = ENOTDIR;
! 1162: break;
! 1163: }
! 1164: if (dirbuf.dotdot_ino == source->i_number) {
! 1165: error = EINVAL;
! 1166: break;
! 1167: }
! 1168: if (dirbuf.dotdot_ino == rootino)
! 1169: break;
! 1170: vput(vp);
! 1171: error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, &vp);
! 1172: if (error) {
! 1173: vp = NULL;
! 1174: break;
! 1175: }
! 1176: }
! 1177:
! 1178: out:
! 1179: if (error == ENOTDIR)
! 1180: printf("checkpath: .. not a directory\n");
! 1181: if (vp != NULL)
! 1182: vput(vp);
! 1183: return (error);
! 1184: }
CVSweb