|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1989 The Regents of the University of California. ! 3: * All rights reserved. ! 4: * ! 5: * Redistribution is only permitted until one year after the first shipment ! 6: * of 4.4BSD by the Regents. Otherwise, redistribution and use in source and ! 7: * binary forms are permitted provided that: (1) source distributions retain ! 8: * this entire copyright notice and comment, and (2) distributions including ! 9: * binaries display the following acknowledgement: This product includes ! 10: * software developed by the University of California, Berkeley and its ! 11: * contributors'' in the documentation or other materials provided with the ! 12: * distribution and in all advertising materials mentioning features or use ! 13: * of this software. Neither the name of the University nor the names of ! 14: * its contributors may be used to endorse or promote products derived from ! 15: * this software without specific prior written permission. ! 16: * THIS SOFTWARE IS PROVIDED AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED ! 17: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF ! 18: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ! 19: * ! 20: * @(#)ufs_lookup.c 7.23 (Berkeley) 6/28/90 ! 21: */ ! 22: ! 23: #include "param.h" ! 24: #include "user.h" ! 25: #include "buf.h" ! 26: #include "file.h" ! 27: #include "vnode.h" ! 28: #include "../ufs/quota.h" ! 29: #include "../ufs/inode.h" ! 30: #include "../ufs/fs.h" ! 31: ! 32: struct nchstats nchstats; ! 33: int dirchk = 1; ! 34: ! 35: /* ! 36: * Convert a component of a pathname into a pointer to a locked inode. ! 37: * This is a very central and rather complicated routine. ! 38: * If the file system is not maintained in a strict tree hierarchy, ! 39: * this can result in a deadlock situation (see comments in code below). ! 40: * ! 41: * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on ! 42: * whether the name is to be looked up, created, renamed, or deleted. ! 43: * When CREATE, RENAME, or DELETE is specified, information usable in ! 44: * creating, renaming, or deleting a directory entry may be calculated. ! 45: * If flag has LOCKPARENT or'ed into it and the target of the pathname ! 46: * exists, lookup returns both the target and its parent directory locked. ! 47: * When creating or renaming and LOCKPARENT is specified, the target may ! 48: * not be ".". When deleting and LOCKPARENT is specified, the target may ! 49: * be "."., but the caller must check to ensure it does an vrele and iput ! 50: * instead of two iputs. ! 51: * ! 52: * Overall outline of ufs_lookup: ! 53: * ! 54: * check accessibility of directory ! 55: * look for name in cache, if found, then if at end of path ! 56: * and deleting or creating, drop it, else return name ! 57: * search for name in directory, to found or notfound ! 58: * notfound: ! 59: * if creating, return locked directory, leaving info on available slots ! 60: * else return error ! 61: * found: ! 62: * if at end of path and deleting, return information to allow delete ! 63: * if at end of path and rewriting (RENAME and LOCKPARENT), lock target ! 64: * inode and return info to allow rewrite ! 65: * if not at end, add name to cache; if at end and neither creating ! 66: * nor deleting, add name to cache ! 67: * ! 68: * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode unlocked. ! 69: */ ! 70: ufs_lookup(vdp, ndp) ! 71: register struct vnode *vdp; ! 72: register struct nameidata *ndp; ! 73: { ! 74: register struct inode *dp; /* the directory we are searching */ ! 75: register struct fs *fs; /* file system that directory is in */ ! 76: struct buf *bp = 0; /* a buffer of directory entries */ ! 77: register struct direct *ep; /* the current directory entry */ ! 78: int entryoffsetinblock; /* offset of ep in bp's buffer */ ! 79: enum {NONE, COMPACT, FOUND} slotstatus; ! 80: int slotoffset = -1; /* offset of area with free space */ ! 81: int slotsize; /* size of area at slotoffset */ ! 82: int slotfreespace; /* amount of space free in slot */ ! 83: int slotneeded; /* size of the entry we're seeking */ ! 84: int numdirpasses; /* strategy for directory search */ ! 85: int endsearch; /* offset to end directory search */ ! 86: int prevoff; /* ndp->ni_offset of previous entry */ ! 87: struct inode *pdp; /* saved dp during symlink work */ ! 88: struct inode *tdp; /* returned by iget */ ! 89: off_t enduseful; /* pointer past last used dir slot */ ! 90: int flag; /* LOOKUP, CREATE, RENAME, or DELETE */ ! 91: int lockparent; /* 1 => lockparent flag is set */ ! 92: int wantparent; /* 1 => wantparent or lockparent flag */ ! 93: int error; ! 94: ! 95: ndp->ni_dvp = vdp; ! 96: ndp->ni_vp = NULL; ! 97: dp = VTOI(vdp); ! 98: fs = dp->i_fs; ! 99: lockparent = ndp->ni_nameiop & LOCKPARENT; ! 100: flag = ndp->ni_nameiop & OPFLAG; ! 101: wantparent = ndp->ni_nameiop & (LOCKPARENT|WANTPARENT); ! 102: ! 103: /* ! 104: * Check accessiblity of directory. ! 105: */ ! 106: if ((dp->i_mode&IFMT) != IFDIR) ! 107: return (ENOTDIR); ! 108: if (error = ufs_access(vdp, VEXEC, ndp->ni_cred)) ! 109: return (error); ! 110: ! 111: /* ! 112: * We now have a segment name to search for, and a directory to search. ! 113: * ! 114: * Before tediously performing a linear scan of the directory, ! 115: * check the name cache to see if the directory/name pair ! 116: * we are looking for is known already. ! 117: */ ! 118: if (error = cache_lookup(ndp)) { ! 119: int vpid; /* capability number of vnode */ ! 120: ! 121: if (error == ENOENT) ! 122: return (error); ! 123: if (vdp == ndp->ni_rdir && ndp->ni_isdotdot) ! 124: panic("ufs_lookup: .. through root"); ! 125: /* ! 126: * Get the next vnode in the path. ! 127: * See comment below starting `Step through' for ! 128: * an explaination of the locking protocol. ! 129: */ ! 130: pdp = dp; ! 131: dp = VTOI(ndp->ni_vp); ! 132: vdp = ndp->ni_vp; ! 133: vpid = vdp->v_id; ! 134: if (pdp == dp) { ! 135: VREF(vdp); ! 136: error = 0; ! 137: } else if (ndp->ni_isdotdot) { ! 138: IUNLOCK(pdp); ! 139: error = vget(vdp); ! 140: } else { ! 141: error = vget(vdp); ! 142: IUNLOCK(pdp); ! 143: } ! 144: /* ! 145: * Check that the capability number did not change ! 146: * while we were waiting for the lock. ! 147: */ ! 148: if (!error) { ! 149: if (vpid == vdp->v_id) ! 150: return (0); ! 151: else ! 152: iput(dp); ! 153: } ! 154: ILOCK(pdp); ! 155: dp = pdp; ! 156: vdp = ITOV(dp); ! 157: ndp->ni_vp = NULL; ! 158: } ! 159: ! 160: /* ! 161: * Suppress search for slots unless creating ! 162: * file and at end of pathname, in which case ! 163: * we watch for a place to put the new file in ! 164: * case it doesn't already exist. ! 165: */ ! 166: slotstatus = FOUND; ! 167: if ((flag == CREATE || flag == RENAME) && *ndp->ni_next == 0) { ! 168: slotstatus = NONE; ! 169: slotfreespace = 0; ! 170: slotneeded = DIRSIZ(&ndp->ni_dent); ! 171: } ! 172: ! 173: /* ! 174: * If there is cached information on a previous search of ! 175: * this directory, pick up where we last left off. ! 176: * We cache only lookups as these are the most common ! 177: * and have the greatest payoff. Caching CREATE has little ! 178: * benefit as it usually must search the entire directory ! 179: * to determine that the entry does not exist. Caching the ! 180: * location of the last DELETE or RENAME has not reduced ! 181: * profiling time and hence has been removed in the interest ! 182: * of simplicity. ! 183: */ ! 184: if (flag != LOOKUP || dp->i_diroff == 0 || dp->i_diroff > dp->i_size) { ! 185: ndp->ni_offset = 0; ! 186: numdirpasses = 1; ! 187: } else { ! 188: ndp->ni_offset = dp->i_diroff; ! 189: entryoffsetinblock = blkoff(fs, ndp->ni_offset); ! 190: if (entryoffsetinblock != 0) { ! 191: error = blkatoff(dp, ndp->ni_offset, (char **)0, &bp); ! 192: if (error) ! 193: return (error); ! 194: } ! 195: numdirpasses = 2; ! 196: nchstats.ncs_2passes++; ! 197: } ! 198: endsearch = roundup(dp->i_size, DIRBLKSIZ); ! 199: enduseful = 0; ! 200: ! 201: searchloop: ! 202: while (ndp->ni_offset < endsearch) { ! 203: /* ! 204: * If offset is on a block boundary, ! 205: * read the next directory block. ! 206: * Release previous if it exists. ! 207: */ ! 208: if (blkoff(fs, ndp->ni_offset) == 0) { ! 209: if (bp != NULL) ! 210: brelse(bp); ! 211: error = blkatoff(dp, ndp->ni_offset, (char **)0, &bp); ! 212: if (error) ! 213: return (error); ! 214: entryoffsetinblock = 0; ! 215: } ! 216: /* ! 217: * If still looking for a slot, and at a DIRBLKSIZE ! 218: * boundary, have to start looking for free space again. ! 219: */ ! 220: if (slotstatus == NONE && ! 221: (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) { ! 222: slotoffset = -1; ! 223: slotfreespace = 0; ! 224: } ! 225: /* ! 226: * Get pointer to next entry. ! 227: * Full validation checks are slow, so we only check ! 228: * enough to insure forward progress through the ! 229: * directory. Complete checks can be run by patching ! 230: * "dirchk" to be true. ! 231: */ ! 232: ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock); ! 233: if (ep->d_reclen == 0 || ! 234: dirchk && dirbadentry(ep, entryoffsetinblock)) { ! 235: int i; ! 236: ! 237: dirbad(dp, ndp->ni_offset, "mangled entry"); ! 238: i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); ! 239: ndp->ni_offset += i; ! 240: entryoffsetinblock += i; ! 241: continue; ! 242: } ! 243: ! 244: /* ! 245: * If an appropriate sized slot has not yet been found, ! 246: * check to see if one is available. Also accumulate space ! 247: * in the current block so that we can determine if ! 248: * compaction is viable. ! 249: */ ! 250: if (slotstatus != FOUND) { ! 251: int size = ep->d_reclen; ! 252: ! 253: if (ep->d_ino != 0) ! 254: size -= DIRSIZ(ep); ! 255: if (size > 0) { ! 256: if (size >= slotneeded) { ! 257: slotstatus = FOUND; ! 258: slotoffset = ndp->ni_offset; ! 259: slotsize = ep->d_reclen; ! 260: } else if (slotstatus == NONE) { ! 261: slotfreespace += size; ! 262: if (slotoffset == -1) ! 263: slotoffset = ndp->ni_offset; ! 264: if (slotfreespace >= slotneeded) { ! 265: slotstatus = COMPACT; ! 266: slotsize = ndp->ni_offset + ! 267: ep->d_reclen - slotoffset; ! 268: } ! 269: } ! 270: } ! 271: } ! 272: ! 273: /* ! 274: * Check for a name match. ! 275: */ ! 276: if (ep->d_ino) { ! 277: if (ep->d_namlen == ndp->ni_dent.d_namlen && ! 278: !bcmp(ndp->ni_ptr, ep->d_name, ! 279: (unsigned)ep->d_namlen)) { ! 280: /* ! 281: * Save directory entry's inode number and ! 282: * reclen in ndp->ni_dent, and release ! 283: * directory buffer. ! 284: */ ! 285: ndp->ni_dent.d_ino = ep->d_ino; ! 286: ndp->ni_dent.d_reclen = ep->d_reclen; ! 287: brelse(bp); ! 288: goto found; ! 289: } ! 290: } ! 291: prevoff = ndp->ni_offset; ! 292: ndp->ni_offset += ep->d_reclen; ! 293: entryoffsetinblock += ep->d_reclen; ! 294: if (ep->d_ino) ! 295: enduseful = ndp->ni_offset; ! 296: } ! 297: /* notfound: */ ! 298: /* ! 299: * If we started in the middle of the directory and failed ! 300: * to find our target, we must check the beginning as well. ! 301: */ ! 302: if (numdirpasses == 2) { ! 303: numdirpasses--; ! 304: ndp->ni_offset = 0; ! 305: endsearch = dp->i_diroff; ! 306: goto searchloop; ! 307: } ! 308: if (bp != NULL) ! 309: brelse(bp); ! 310: /* ! 311: * If creating, and at end of pathname and current ! 312: * directory has not been removed, then can consider ! 313: * allowing file to be created. ! 314: */ ! 315: if ((flag == CREATE || flag == RENAME) && ! 316: *ndp->ni_next == 0 && dp->i_nlink != 0) { ! 317: /* ! 318: * Access for write is interpreted as allowing ! 319: * creation of files in the directory. ! 320: */ ! 321: if (error = ufs_access(vdp, VWRITE, ndp->ni_cred)) ! 322: return (error); ! 323: /* ! 324: * Return an indication of where the new directory ! 325: * entry should be put. If we didn't find a slot, ! 326: * then set ndp->ni_count to 0 indicating that the new ! 327: * slot belongs at the end of the directory. If we found ! 328: * a slot, then the new entry can be put in the range ! 329: * [ndp->ni_offset .. ndp->ni_offset + ndp->ni_count) ! 330: */ ! 331: if (slotstatus == NONE) { ! 332: ndp->ni_offset = roundup(dp->i_size, DIRBLKSIZ); ! 333: ndp->ni_count = 0; ! 334: enduseful = ndp->ni_offset; ! 335: } else { ! 336: ndp->ni_offset = slotoffset; ! 337: ndp->ni_count = slotsize; ! 338: if (enduseful < slotoffset + slotsize) ! 339: enduseful = slotoffset + slotsize; ! 340: } ! 341: ndp->ni_endoff = roundup(enduseful, DIRBLKSIZ); ! 342: dp->i_flag |= IUPD|ICHG; ! 343: /* ! 344: * We return with the directory locked, so that ! 345: * the parameters we set up above will still be ! 346: * valid if we actually decide to do a direnter(). ! 347: * We return ni_vp == NULL to indicate that the entry ! 348: * does not currently exist; we leave a pointer to ! 349: * the (locked) directory inode in ndp->ni_dvp. ! 350: * ! 351: * NB - if the directory is unlocked, then this ! 352: * information cannot be used. ! 353: */ ! 354: if (!lockparent) ! 355: IUNLOCK(dp); ! 356: } ! 357: /* ! 358: * Insert name into cache (as non-existent) if appropriate. ! 359: */ ! 360: if (ndp->ni_makeentry) ! 361: cache_enter(ndp); ! 362: return (ENOENT); ! 363: ! 364: found: ! 365: if (numdirpasses == 2) ! 366: nchstats.ncs_pass2++; ! 367: /* ! 368: * Check that directory length properly reflects presence ! 369: * of this entry. ! 370: */ ! 371: if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) { ! 372: dirbad(dp, ndp->ni_offset, "i_size too small"); ! 373: dp->i_size = entryoffsetinblock + DIRSIZ(ep); ! 374: dp->i_flag |= IUPD|ICHG; ! 375: } ! 376: ! 377: /* ! 378: * Found component in pathname. ! 379: * If the final component of path name, save information ! 380: * in the cache as to where the entry was found. ! 381: */ ! 382: if (*ndp->ni_next == '\0' && flag == LOOKUP) ! 383: dp->i_diroff = ndp->ni_offset &~ (DIRBLKSIZ - 1); ! 384: ! 385: /* ! 386: * If deleting, and at end of pathname, return ! 387: * parameters which can be used to remove file. ! 388: * If the wantparent flag isn't set, we return only ! 389: * the directory (in ndp->ni_dvp), otherwise we go ! 390: * on and lock the inode, being careful with ".". ! 391: */ ! 392: if (flag == DELETE && *ndp->ni_next == 0) { ! 393: /* ! 394: * Write access to directory required to delete files. ! 395: */ ! 396: if (error = ufs_access(vdp, VWRITE, ndp->ni_cred)) ! 397: return (error); ! 398: /* ! 399: * Return pointer to current entry in ndp->ni_offset, ! 400: * and distance past previous entry (if there ! 401: * is a previous entry in this block) in ndp->ni_count. ! 402: * Save directory inode pointer in ndp->ni_dvp for dirremove(). ! 403: */ ! 404: if ((ndp->ni_offset&(DIRBLKSIZ-1)) == 0) ! 405: ndp->ni_count = 0; ! 406: else ! 407: ndp->ni_count = ndp->ni_offset - prevoff; ! 408: if (dp->i_number == ndp->ni_dent.d_ino) { ! 409: VREF(vdp); ! 410: ndp->ni_vp = vdp; ! 411: return (0); ! 412: } ! 413: if (error = iget(dp, ndp->ni_dent.d_ino, &tdp)) ! 414: return (error); ! 415: /* ! 416: * If directory is "sticky", then user must own ! 417: * the directory, or the file in it, else she ! 418: * may not delete it (unless she's root). This ! 419: * implements append-only directories. ! 420: */ ! 421: if ((dp->i_mode & ISVTX) && ! 422: ndp->ni_cred->cr_uid != 0 && ! 423: ndp->ni_cred->cr_uid != dp->i_uid && ! 424: tdp->i_uid != ndp->ni_cred->cr_uid) { ! 425: iput(tdp); ! 426: return (EPERM); ! 427: } ! 428: ndp->ni_vp = ITOV(tdp); ! 429: if (!lockparent) ! 430: IUNLOCK(dp); ! 431: return (0); ! 432: } ! 433: ! 434: /* ! 435: * If rewriting (RENAME), return the inode and the ! 436: * information required to rewrite the present directory ! 437: * Must get inode of directory entry to verify it's a ! 438: * regular file, or empty directory. ! 439: */ ! 440: if (flag == RENAME && wantparent && *ndp->ni_next == 0) { ! 441: if (error = ufs_access(vdp, VWRITE, ndp->ni_cred)) ! 442: return (error); ! 443: /* ! 444: * Careful about locking second inode. ! 445: * This can only occur if the target is ".". ! 446: */ ! 447: if (dp->i_number == ndp->ni_dent.d_ino) ! 448: return (EISDIR); ! 449: if (error = iget(dp, ndp->ni_dent.d_ino, &tdp)) ! 450: return (error); ! 451: ndp->ni_vp = ITOV(tdp); ! 452: if (!lockparent) ! 453: IUNLOCK(dp); ! 454: return (0); ! 455: } ! 456: ! 457: /* ! 458: * Step through the translation in the name. We do not `iput' the ! 459: * directory because we may need it again if a symbolic link ! 460: * is relative to the current directory. Instead we save it ! 461: * unlocked as "pdp". We must get the target inode before unlocking ! 462: * the directory to insure that the inode will not be removed ! 463: * before we get it. We prevent deadlock by always fetching ! 464: * inodes from the root, moving down the directory tree. Thus ! 465: * when following backward pointers ".." we must unlock the ! 466: * parent directory before getting the requested directory. ! 467: * There is a potential race condition here if both the current ! 468: * and parent directories are removed before the `iget' for the ! 469: * inode associated with ".." returns. We hope that this occurs ! 470: * infrequently since we cannot avoid this race condition without ! 471: * implementing a sophisticated deadlock detection algorithm. ! 472: * Note also that this simple deadlock detection scheme will not ! 473: * work if the file system has any hard links other than ".." ! 474: * that point backwards in the directory structure. ! 475: */ ! 476: pdp = dp; ! 477: if (ndp->ni_isdotdot) { ! 478: IUNLOCK(pdp); /* race to get the inode */ ! 479: if (error = iget(dp, ndp->ni_dent.d_ino, &tdp)) { ! 480: ILOCK(pdp); ! 481: return (error); ! 482: } ! 483: if (lockparent && *ndp->ni_next == '\0') ! 484: ILOCK(pdp); ! 485: ndp->ni_vp = ITOV(tdp); ! 486: } else if (dp->i_number == ndp->ni_dent.d_ino) { ! 487: VREF(vdp); /* we want ourself, ie "." */ ! 488: ndp->ni_vp = vdp; ! 489: } else { ! 490: if (error = iget(dp, ndp->ni_dent.d_ino, &tdp)) ! 491: return (error); ! 492: if (!lockparent || *ndp->ni_next != '\0') ! 493: IUNLOCK(pdp); ! 494: ndp->ni_vp = ITOV(tdp); ! 495: } ! 496: ! 497: /* ! 498: * Insert name into cache if appropriate. ! 499: */ ! 500: if (ndp->ni_makeentry) ! 501: cache_enter(ndp); ! 502: return (0); ! 503: } ! 504: ! 505: ! 506: dirbad(ip, offset, how) ! 507: struct inode *ip; ! 508: off_t offset; ! 509: char *how; ! 510: { ! 511: ! 512: printf("%s: bad dir ino %d at offset %d: %s\n", ! 513: ip->i_fs->fs_fsmnt, ip->i_number, offset, how); ! 514: panic("bad dir"); ! 515: } ! 516: ! 517: /* ! 518: * Do consistency checking on a directory entry: ! 519: * record length must be multiple of 4 ! 520: * entry must fit in rest of its DIRBLKSIZ block ! 521: * record must be large enough to contain entry ! 522: * name is not longer than MAXNAMLEN ! 523: * name must be as long as advertised, and null terminated ! 524: */ ! 525: dirbadentry(ep, entryoffsetinblock) ! 526: register struct direct *ep; ! 527: int entryoffsetinblock; ! 528: { ! 529: register int i; ! 530: ! 531: if ((ep->d_reclen & 0x3) != 0 || ! 532: ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) || ! 533: ep->d_reclen < DIRSIZ(ep) || ep->d_namlen > MAXNAMLEN) ! 534: return (1); ! 535: for (i = 0; i < ep->d_namlen; i++) ! 536: if (ep->d_name[i] == '\0') ! 537: return (1); ! 538: return (ep->d_name[i]); ! 539: } ! 540: ! 541: /* ! 542: * Write a directory entry after a call to namei, using the parameters ! 543: * which it left in nameidata. The argument ip is the inode which the ! 544: * new directory entry will refer to. The nameidata field ndp->ni_dvp ! 545: * is a pointer to the directory to be written, which was left locked by ! 546: * namei. Remaining parameters (ndp->ni_offset, ndp->ni_count) indicate ! 547: * how the space for the new entry is to be gotten. ! 548: */ ! 549: direnter(ip, ndp) ! 550: struct inode *ip; ! 551: register struct nameidata *ndp; ! 552: { ! 553: register struct direct *ep, *nep; ! 554: register struct inode *dp = VTOI(ndp->ni_dvp); ! 555: struct buf *bp; ! 556: int loc, spacefree, error = 0; ! 557: u_int dsize; ! 558: int newentrysize; ! 559: char *dirbuf; ! 560: ! 561: ndp->ni_dent.d_ino = ip->i_number; ! 562: newentrysize = DIRSIZ(&ndp->ni_dent); ! 563: if (ndp->ni_count == 0) { ! 564: /* ! 565: * If ndp->ni_count is 0, then namei could find no space in the ! 566: * directory. In this case ndp->ni_offset will be on a directory ! 567: * block boundary and we will write the new entry into a fresh ! 568: * block. ! 569: */ ! 570: if (ndp->ni_offset&(DIRBLKSIZ-1)) ! 571: panic("wdir: newblk"); ! 572: ndp->ni_dent.d_reclen = DIRBLKSIZ; ! 573: ndp->ni_count = newentrysize; ! 574: ndp->ni_resid = newentrysize; ! 575: ndp->ni_base = (caddr_t)&ndp->ni_dent; ! 576: ndp->ni_uioseg = UIO_SYSSPACE; ! 577: error = ! 578: ufs_write(ndp->ni_dvp, &ndp->ni_uio, IO_SYNC, ndp->ni_cred); ! 579: if (DIRBLKSIZ > dp->i_fs->fs_fsize) { ! 580: panic("wdir: blksize"); /* XXX - should grow w/balloc */ ! 581: } else { ! 582: dp->i_size = roundup(dp->i_size, DIRBLKSIZ); ! 583: dp->i_flag |= ICHG; ! 584: } ! 585: iput(dp); ! 586: return (error); ! 587: } ! 588: ! 589: /* ! 590: * If ndp->ni_count is non-zero, then namei found space for the new ! 591: * entry in the range ndp->ni_offset to ndp->ni_offset + ndp->ni_count. ! 592: * in the directory. To use this space, we may have to compact ! 593: * the entries located there, by copying them together towards ! 594: * the beginning of the block, leaving the free space in ! 595: * one usable chunk at the end. ! 596: */ ! 597: ! 598: /* ! 599: * Increase size of directory if entry eats into new space. ! 600: * This should never push the size past a new multiple of ! 601: * DIRBLKSIZE. ! 602: * ! 603: * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. ! 604: */ ! 605: if (ndp->ni_offset + ndp->ni_count > dp->i_size) ! 606: dp->i_size = ndp->ni_offset + ndp->ni_count; ! 607: /* ! 608: * Get the block containing the space for the new directory entry. ! 609: */ ! 610: if (error = blkatoff(dp, ndp->ni_offset, (char **)&dirbuf, &bp)) { ! 611: iput(dp); ! 612: return (error); ! 613: } ! 614: /* ! 615: * Find space for the new entry. In the simple case, the ! 616: * entry at offset base will have the space. If it does ! 617: * not, then namei arranged that compacting the region ! 618: * ndp->ni_offset to ndp->ni_offset+ndp->ni_count would yield the space. ! 619: */ ! 620: ep = (struct direct *)dirbuf; ! 621: dsize = DIRSIZ(ep); ! 622: spacefree = ep->d_reclen - dsize; ! 623: for (loc = ep->d_reclen; loc < ndp->ni_count; ) { ! 624: nep = (struct direct *)(dirbuf + loc); ! 625: if (ep->d_ino) { ! 626: /* trim the existing slot */ ! 627: ep->d_reclen = dsize; ! 628: ep = (struct direct *)((char *)ep + dsize); ! 629: } else { ! 630: /* overwrite; nothing there; header is ours */ ! 631: spacefree += dsize; ! 632: } ! 633: dsize = DIRSIZ(nep); ! 634: spacefree += nep->d_reclen - dsize; ! 635: loc += nep->d_reclen; ! 636: bcopy((caddr_t)nep, (caddr_t)ep, dsize); ! 637: } ! 638: /* ! 639: * Update the pointer fields in the previous entry (if any), ! 640: * copy in the new entry, and write out the block. ! 641: */ ! 642: if (ep->d_ino == 0) { ! 643: if (spacefree + dsize < newentrysize) ! 644: panic("wdir: compact1"); ! 645: ndp->ni_dent.d_reclen = spacefree + dsize; ! 646: } else { ! 647: if (spacefree < newentrysize) ! 648: panic("wdir: compact2"); ! 649: ndp->ni_dent.d_reclen = spacefree; ! 650: ep->d_reclen = dsize; ! 651: ep = (struct direct *)((char *)ep + dsize); ! 652: } ! 653: bcopy((caddr_t)&ndp->ni_dent, (caddr_t)ep, (u_int)newentrysize); ! 654: error = bwrite(bp); ! 655: dp->i_flag |= IUPD|ICHG; ! 656: if (!error && ndp->ni_endoff && ndp->ni_endoff < dp->i_size) ! 657: error = itrunc(dp, (u_long)ndp->ni_endoff, IO_SYNC); ! 658: iput(dp); ! 659: return (error); ! 660: } ! 661: ! 662: /* ! 663: * Remove a directory entry after a call to namei, using ! 664: * the parameters which it left in nameidata. The entry ! 665: * ni_offset contains the offset into the directory of the ! 666: * entry to be eliminated. The ni_count field contains the ! 667: * size of the previous record in the directory. If this ! 668: * is 0, the first entry is being deleted, so we need only ! 669: * zero the inode number to mark the entry as free. If the ! 670: * entry isn't the first in the directory, we must reclaim ! 671: * the space of the now empty record by adding the record size ! 672: * to the size of the previous entry. ! 673: */ ! 674: dirremove(ndp) ! 675: register struct nameidata *ndp; ! 676: { ! 677: register struct inode *dp = VTOI(ndp->ni_dvp); ! 678: struct direct *ep; ! 679: struct buf *bp; ! 680: int error; ! 681: ! 682: if (ndp->ni_count == 0) { ! 683: /* ! 684: * First entry in block: set d_ino to zero. ! 685: */ ! 686: ndp->ni_dent.d_ino = 0; ! 687: ndp->ni_count = ndp->ni_resid = DIRSIZ(&ndp->ni_dent); ! 688: ndp->ni_base = (caddr_t)&ndp->ni_dent; ! 689: ndp->ni_uioseg = UIO_SYSSPACE; ! 690: error = ! 691: ufs_write(ndp->ni_dvp, &ndp->ni_uio, IO_SYNC, ndp->ni_cred); ! 692: } else { ! 693: /* ! 694: * Collapse new free space into previous entry. ! 695: */ ! 696: if (error = blkatoff(dp, ndp->ni_offset - ndp->ni_count, ! 697: (char **)&ep, &bp)) { ! 698: return (error); ! 699: } ! 700: ep->d_reclen += ndp->ni_dent.d_reclen; ! 701: error = bwrite(bp); ! 702: dp->i_flag |= IUPD|ICHG; ! 703: } ! 704: return (error); ! 705: } ! 706: ! 707: /* ! 708: * Rewrite an existing directory entry to point at the inode ! 709: * supplied. The parameters describing the directory entry are ! 710: * set up by a call to namei. ! 711: */ ! 712: dirrewrite(dp, ip, ndp) ! 713: struct inode *dp, *ip; ! 714: struct nameidata *ndp; ! 715: { ! 716: ! 717: ndp->ni_dent.d_ino = ip->i_number; ! 718: ndp->ni_count = ndp->ni_resid = DIRSIZ(&ndp->ni_dent); ! 719: ndp->ni_base = (caddr_t)&ndp->ni_dent; ! 720: ndp->ni_uioseg = UIO_SYSSPACE; ! 721: return (ufs_write(ITOV(dp), &ndp->ni_uio, IO_SYNC, ndp->ni_cred)); ! 722: } ! 723: ! 724: /* ! 725: * Return buffer with contents of block "offset" ! 726: * from the beginning of directory "ip". If "res" ! 727: * is non-zero, fill it in with a pointer to the ! 728: * remaining space in the directory. ! 729: */ ! 730: blkatoff(ip, offset, res, bpp) ! 731: struct inode *ip; ! 732: off_t offset; ! 733: char **res; ! 734: struct buf **bpp; ! 735: { ! 736: register struct fs *fs = ip->i_fs; ! 737: daddr_t lbn = lblkno(fs, offset); ! 738: int bsize = blksize(fs, ip, lbn); ! 739: struct buf *bp; ! 740: daddr_t bn; ! 741: int error; ! 742: ! 743: *bpp = 0; ! 744: if (error = bread(ITOV(ip), lbn, bsize, NOCRED, &bp)) { ! 745: brelse(bp); ! 746: return (error); ! 747: } ! 748: if (res) ! 749: *res = bp->b_un.b_addr + blkoff(fs, offset); ! 750: *bpp = bp; ! 751: return (0); ! 752: } ! 753: ! 754: /* ! 755: * Check if a directory is empty or not. ! 756: * Inode supplied must be locked. ! 757: * ! 758: * Using a struct dirtemplate here is not precisely ! 759: * what we want, but better than using a struct direct. ! 760: * ! 761: * NB: does not handle corrupted directories. ! 762: */ ! 763: dirempty(ip, parentino, cred) ! 764: register struct inode *ip; ! 765: ino_t parentino; ! 766: struct ucred *cred; ! 767: { ! 768: register off_t off; ! 769: struct dirtemplate dbuf; ! 770: register struct direct *dp = (struct direct *)&dbuf; ! 771: int error, count; ! 772: #define MINDIRSIZ (sizeof (struct dirtemplate) / 2) ! 773: ! 774: for (off = 0; off < ip->i_size; off += dp->d_reclen) { ! 775: error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, ! 776: off, UIO_SYSSPACE, IO_NODELOCKED, cred, &count); ! 777: /* ! 778: * Since we read MINDIRSIZ, residual must ! 779: * be 0 unless we're at end of file. ! 780: */ ! 781: if (error || count != 0) ! 782: return (0); ! 783: /* avoid infinite loops */ ! 784: if (dp->d_reclen == 0) ! 785: return (0); ! 786: /* skip empty entries */ ! 787: if (dp->d_ino == 0) ! 788: continue; ! 789: /* accept only "." and ".." */ ! 790: if (dp->d_namlen > 2) ! 791: return (0); ! 792: if (dp->d_name[0] != '.') ! 793: return (0); ! 794: /* ! 795: * At this point d_namlen must be 1 or 2. ! 796: * 1 implies ".", 2 implies ".." if second ! 797: * char is also "." ! 798: */ ! 799: if (dp->d_namlen == 1) ! 800: continue; ! 801: if (dp->d_name[1] == '.' && dp->d_ino == parentino) ! 802: continue; ! 803: return (0); ! 804: } ! 805: return (1); ! 806: } ! 807: ! 808: /* ! 809: * Check if source directory is in the path of the target directory. ! 810: * Target is supplied locked, source is unlocked. ! 811: * The target is always iput() before returning. ! 812: */ ! 813: checkpath(source, target, cred) ! 814: struct inode *source, *target; ! 815: struct ucred *cred; ! 816: { ! 817: struct dirtemplate dirbuf; ! 818: struct inode *ip; ! 819: int error = 0; ! 820: ! 821: ip = target; ! 822: if (ip->i_number == source->i_number) { ! 823: error = EEXIST; ! 824: goto out; ! 825: } ! 826: if (ip->i_number == ROOTINO) ! 827: goto out; ! 828: ! 829: for (;;) { ! 830: if ((ip->i_mode&IFMT) != IFDIR) { ! 831: error = ENOTDIR; ! 832: break; ! 833: } ! 834: error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)&dirbuf, ! 835: sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, ! 836: IO_NODELOCKED, cred, (int *)0); ! 837: if (error != 0) ! 838: break; ! 839: if (dirbuf.dotdot_namlen != 2 || ! 840: dirbuf.dotdot_name[0] != '.' || ! 841: dirbuf.dotdot_name[1] != '.') { ! 842: error = ENOTDIR; ! 843: break; ! 844: } ! 845: if (dirbuf.dotdot_ino == source->i_number) { ! 846: error = EINVAL; ! 847: break; ! 848: } ! 849: if (dirbuf.dotdot_ino == ROOTINO) ! 850: break; ! 851: iput(ip); ! 852: if (error = iget(ip, dirbuf.dotdot_ino, &ip)) ! 853: break; ! 854: } ! 855: ! 856: out: ! 857: if (error == ENOTDIR) ! 858: printf("checkpath: .. not a directory\n"); ! 859: if (ip != NULL) ! 860: iput(ip); ! 861: return (error); ! 862: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.