1: /* 2: * Copyright (c) 1980 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #if !defined(lint) && defined(DOSCCS) 8: static char sccsid[] = "@(#)dir.c 5.1.1 (2.11BSD) 1996/5/8"; 9: #endif not lint 10: 11: #include <sys/param.h> 12: #include <sys/inode.h> 13: #include <sys/fs.h> 14: #include <sys/dir.h> 15: #include "fsck.h" 16: 17: #define MINDIRSIZE (sizeof (struct dirtemplate)) 18: 19: char *endpathname = &pathname[MAXPATHLEN - 2]; 20: char *lfname = "lost+found"; 21: struct dirtemplate emptydir = { 0, DIRBLKSIZ }; 22: /* 23: * The strange initialization is due to quoted strings causing problems when 24: * 'xstr' is used to preprocess the sources. The structure has two members 25: * as 'char dot_name[2]' and 'char dotdot_name[6]' which is NOT the same as 26: * 'char *'. 27: */ 28: struct dirtemplate dirhead = { 0, 8, 1, {'.'}, 0, DIRBLKSIZ - 8, 2, 29: {'.', '.' }}; 30: 31: DIRECT *fsck_readdir(); 32: 33: descend(parentino, inumber) 34: struct inodesc *parentino; 35: ino_t inumber; 36: { 37: register DINODE *dp; 38: struct inodesc curino; 39: 40: bzero((char *)&curino, sizeof(struct inodesc)); 41: if (getstate(inumber) != DSTATE) 42: errexit("BAD INODE %u TO DESCEND", getstate(inumber)); 43: setstate(inumber, DFOUND); 44: dp = ginode(inumber); 45: if (dp->di_size == 0) { 46: direrr(inumber, "ZERO LENGTH DIRECTORY"); 47: if (reply("REMOVE") == 1) 48: setstate(inumber, DCLEAR); 49: return; 50: } 51: if (dp->di_size < MINDIRSIZE) { 52: direrr(inumber, "DIRECTORY TOO SHORT"); 53: dp->di_size = MINDIRSIZE; 54: if (reply("FIX") == 1) 55: inodirty(); 56: } 57: if ((dp->di_size & (DIRBLKSIZ - 1)) != 0) { 58: pwarn("DIRECTORY %s: LENGTH %ld NOT MULTIPLE OF %d", 59: pathname, dp->di_size, DIRBLKSIZ); 60: dp->di_size = roundup(dp->di_size, DIRBLKSIZ); 61: if (preen) 62: printf(" (ADJUSTED)\n"); 63: if (preen || reply("ADJUST") == 1) 64: inodirty(); 65: } 66: curino.id_type = DATA; 67: curino.id_func = parentino->id_func; 68: curino.id_parent = parentino->id_number; 69: curino.id_number = inumber; 70: (void)ckinode(dp, &curino); 71: } 72: 73: dirscan(idesc) 74: register struct inodesc *idesc; 75: { 76: register DIRECT *dp; 77: int dsize, n, *ii; 78: char dbuf[DIRBLKSIZ]; 79: 80: if (idesc->id_type != DATA) 81: errexit("wrong type to dirscan %d\n", idesc->id_type); 82: if (idesc->id_entryno == 0 && 83: (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) 84: idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); 85: if (outrange(idesc->id_blkno)) { 86: idesc->id_filesize -= DEV_BSIZE; 87: return (SKIP); 88: } 89: idesc->id_loc = 0; 90: for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 91: dsize = dp->d_reclen; 92: bcopy((char *)dp, dbuf, dsize); 93: idesc->id_dirp = (DIRECT *)dbuf; 94: if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 95: getblk(&fileblk, idesc->id_blkno); 96: if (fileblk.b_errs != NULL) { 97: n &= ~ALTERED; 98: } else { 99: bcopy(dbuf, (char *)dp, dsize); 100: dirty(&fileblk); 101: sbdirty(); 102: } 103: } 104: if (n & STOP) 105: return (n); 106: } 107: return (idesc->id_filesize > 0 ? KEEPON : STOP); 108: } 109: 110: /* 111: * get next entry in a directory. 112: */ 113: DIRECT * 114: fsck_readdir(idesc) 115: register struct inodesc *idesc; 116: { 117: register DIRECT *dp, *ndp; 118: u_int size; 119: 120: getblk(&fileblk, idesc->id_blkno); 121: if (fileblk.b_errs != NULL) { 122: idesc->id_filesize -= DEV_BSIZE - idesc->id_loc; 123: return NULL; 124: } 125: if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 && 126: idesc->id_loc < DEV_BSIZE) { 127: dp = (DIRECT *)(dirblk.b_buf + idesc->id_loc); 128: if (dircheck(idesc, dp)) 129: goto dpok; 130: idesc->id_loc += DIRBLKSIZ; 131: idesc->id_filesize -= DIRBLKSIZ; 132: dp->d_reclen = DIRBLKSIZ; 133: dp->d_ino = 0; 134: dp->d_namlen = 0; 135: dp->d_name[0] = '\0'; 136: if (dofix(idesc, "DIRECTORY CORRUPTED")) 137: dirty(&fileblk); 138: return (dp); 139: } 140: dpok: 141: if (idesc->id_filesize <= 0 || idesc->id_loc >= DEV_BSIZE) 142: return NULL; 143: dp = (DIRECT *)(dirblk.b_buf + idesc->id_loc); 144: idesc->id_loc += dp->d_reclen; 145: idesc->id_filesize -= dp->d_reclen; 146: if ((idesc->id_loc % DIRBLKSIZ) == 0) 147: return (dp); 148: ndp = (DIRECT *)(dirblk.b_buf + idesc->id_loc); 149: if (idesc->id_loc < DEV_BSIZE && idesc->id_filesize > 0 && 150: dircheck(idesc, ndp) == 0) { 151: size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 152: dp->d_reclen += size; 153: idesc->id_loc += size; 154: idesc->id_filesize -= size; 155: if (dofix(idesc, "DIRECTORY CORRUPTED")) 156: dirty(&fileblk); 157: } 158: return (dp); 159: } 160: 161: /* 162: * Verify that a directory entry is valid. 163: * This is a superset of the checks made in the kernel. 164: */ 165: dircheck(idesc, dp) 166: struct inodesc *idesc; 167: register DIRECT *dp; 168: { 169: register int size; 170: register char *cp; 171: int spaceleft; 172: 173: size = DIRSIZ(dp); 174: spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 175: if (dp->d_ino < imax && 176: dp->d_reclen != 0 && 177: dp->d_reclen <= spaceleft && 178: (dp->d_reclen & 0x3) == 0 && 179: dp->d_reclen >= size && 180: idesc->id_filesize >= size && 181: dp->d_namlen <= MAXNAMLEN) { 182: if (dp->d_ino == 0) 183: return (1); 184: for (cp = dp->d_name, size = 0; size < dp->d_namlen; size++) 185: if (*cp == 0 || (*cp++ & 0200)) 186: return (0); 187: if (*cp == 0) 188: return (1); 189: } 190: return (0); 191: } 192: 193: direrr(ino, s) 194: ino_t ino; 195: char *s; 196: { 197: register DINODE *dp; 198: 199: pwarn("%s ", s); 200: pinode(ino); 201: printf("\n"); 202: if (ino < ROOTINO || ino > imax) { 203: pfatal("NAME=%s\n", pathname); 204: return; 205: } 206: dp = ginode(ino); 207: if (ftypeok(dp)) 208: pfatal("%s=%s\n", DIRCT(dp) ? "DIR" : "FILE", pathname); 209: else 210: pfatal("NAME=%s\n", pathname); 211: } 212: 213: adjust(idesc, lcnt) 214: register struct inodesc *idesc; 215: short lcnt; 216: { 217: register DINODE *dp; 218: 219: dp = ginode(idesc->id_number); 220: if (dp->di_nlink == lcnt) { 221: if (linkup(idesc->id_number, (ino_t)0) == 0) 222: clri(idesc, "UNREF", 0); 223: } else { 224: pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 225: (DIRCT(dp) ? "DIR" : "FILE")); 226: pinode(idesc->id_number); 227: printf(" COUNT %u SHOULD BE %u", 228: dp->di_nlink, dp->di_nlink-lcnt); 229: if (preen) { 230: if (lcnt < 0) { 231: printf("\n"); 232: pfatal("LINK COUNT INCREASING"); 233: } 234: printf(" (ADJUSTED)\n"); 235: } 236: if (preen || reply("ADJUST") == 1) { 237: dp->di_nlink -= lcnt; 238: inodirty(); 239: } 240: } 241: } 242: 243: mkentry(idesc) 244: struct inodesc *idesc; 245: { 246: register DIRECT *dirp = idesc->id_dirp; 247: DIRECT newent; 248: int newlen, oldlen; 249: 250: newent.d_namlen = 11; 251: newlen = DIRSIZ(&newent); 252: if (dirp->d_ino != 0) 253: oldlen = DIRSIZ(dirp); 254: else 255: oldlen = 0; 256: if (dirp->d_reclen - oldlen < newlen) 257: return (KEEPON); 258: newent.d_reclen = dirp->d_reclen - oldlen; 259: dirp->d_reclen = oldlen; 260: dirp = (struct direct *)(((char *)dirp) + oldlen); 261: dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ 262: dirp->d_reclen = newent.d_reclen; 263: dirp->d_namlen = strlen(idesc->id_name); 264: bcopy(idesc->id_name, dirp->d_name, dirp->d_namlen + 1); 265: return (ALTERED|STOP); 266: } 267: 268: chgino(idesc) 269: struct inodesc *idesc; 270: { 271: register DIRECT *dirp = idesc->id_dirp; 272: 273: if (bcmp(dirp->d_name, idesc->id_name, dirp->d_namlen + 1)) 274: return (KEEPON); 275: dirp->d_ino = idesc->id_parent;; 276: return (ALTERED|STOP); 277: } 278: 279: linkup(orphan, pdir) 280: ino_t orphan; 281: ino_t pdir; 282: { 283: register DINODE *dp; 284: int lostdir, len; 285: ino_t oldlfdir; 286: struct inodesc idesc; 287: char tempname[MAXPATHLEN]; 288: extern int pass4check(); 289: 290: bzero((char *)&idesc, sizeof(struct inodesc)); 291: dp = ginode(orphan); 292: lostdir = DIRCT(dp); 293: pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 294: pinode(orphan); 295: if (preen && dp->di_size == 0) 296: return (0); 297: if (preen) 298: printf(" (RECONNECTED)\n"); 299: else 300: if (reply("RECONNECT") == 0) 301: return (0); 302: pathp = pathname; 303: *pathp++ = '/'; 304: *pathp = '\0'; 305: if (lfdir == 0) { 306: dp = ginode(ROOTINO); 307: idesc.id_name = lfname; 308: idesc.id_type = DATA; 309: idesc.id_func = findino; 310: idesc.id_number = ROOTINO; 311: (void)ckinode(dp, &idesc); 312: if (idesc.id_parent >= ROOTINO && idesc.id_parent < imax) { 313: lfdir = idesc.id_parent; 314: } else { 315: pwarn("NO lost+found DIRECTORY"); 316: if (preen || reply("CREATE")) { 317: lfdir = allocdir(ROOTINO, 0); 318: if (lfdir != 0) { 319: if (makeentry(ROOTINO, lfdir, lfname) != 0) { 320: if (preen) 321: printf(" (CREATED)\n"); 322: } else { 323: freedir(lfdir, ROOTINO); 324: lfdir = 0; 325: if (preen) 326: printf("\n"); 327: } 328: } 329: } 330: } 331: if (lfdir == 0) { 332: pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 333: return (0); 334: } 335: } 336: dp = ginode(lfdir); 337: if (!DIRCT(dp)) { 338: pfatal("lost+found IS NOT A DIRECTORY"); 339: if (reply("REALLOCATE") == 0) 340: return (0); 341: oldlfdir = lfdir; 342: if ((lfdir = allocdir(ROOTINO, 0)) == 0) { 343: pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 344: return (0); 345: } 346: idesc.id_type = DATA; 347: idesc.id_func = chgino; 348: idesc.id_number = ROOTINO; 349: idesc.id_parent = lfdir; /* new inumber for lost+found */ 350: idesc.id_name = lfname; 351: if ((ckinode(ginode(ROOTINO), &idesc) & ALTERED) == 0) { 352: pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 353: return (0); 354: } 355: inodirty(); 356: idesc.id_type = ADDR; 357: idesc.id_func = pass4check; 358: idesc.id_number = oldlfdir; 359: adjust(&idesc, getlncnt(oldlfdir) + 1); 360: setlncnt(oldlfdir, 0); 361: dp = ginode(lfdir); 362: } 363: if (getstate(lfdir) != DFOUND) { 364: pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 365: return (0); 366: } 367: len = strlen(lfname); 368: bcopy(lfname, pathp, len + 1); 369: pathp += len; 370: len = lftempname(tempname, orphan); 371: if (makeentry(lfdir, orphan, tempname) == 0) { 372: pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 373: printf("\n\n"); 374: return (0); 375: } 376: declncnt(orphan); 377: *pathp++ = '/'; 378: bcopy(idesc.id_name, pathp, len + 1); 379: pathp += len; 380: if (lostdir) { 381: dp = ginode(orphan); 382: idesc.id_type = DATA; 383: idesc.id_func = chgino; 384: idesc.id_number = orphan; 385: idesc.id_fix = DONTKNOW; 386: idesc.id_name = ".."; 387: idesc.id_parent = lfdir; /* new value for ".." */ 388: (void)ckinode(dp, &idesc); 389: dp = ginode(lfdir); 390: dp->di_nlink++; 391: inodirty(); 392: inclncnt(lfdir); 393: pwarn("DIR I=%u CONNECTED. ", orphan); 394: printf("PARENT WAS I=%u\n", pdir); 395: if (preen == 0) 396: printf("\n"); 397: } 398: return (1); 399: } 400: 401: /* 402: * make an entry in a directory 403: */ 404: makeentry(parent, ino, name) 405: ino_t parent, ino; 406: char *name; 407: { 408: DINODE *dp; 409: struct inodesc idesc; 410: 411: if (parent < ROOTINO || parent >= imax || ino < ROOTINO || ino >= imax) 412: return (0); 413: bzero(&idesc, sizeof(struct inodesc)); 414: idesc.id_type = DATA; 415: idesc.id_func = mkentry; 416: idesc.id_number = parent; 417: idesc.id_parent = ino; /* this is the inode to enter */ 418: idesc.id_fix = DONTKNOW; 419: idesc.id_name = name; 420: dp = ginode(parent); 421: if (dp->di_size % DIRBLKSIZ) { 422: dp->di_size = roundup(dp->di_size, DIRBLKSIZ); 423: inodirty(); 424: } 425: if ((ckinode(dp, &idesc) & ALTERED) != 0) 426: return (1); 427: if (expanddir(dp) == 0) 428: return (0); 429: return (ckinode(dp, &idesc) & ALTERED); 430: } 431: 432: /* 433: * Attempt to expand the size of a directory 434: */ 435: expanddir(dp) 436: register DINODE *dp; 437: { 438: daddr_t lastbn, newblk; 439: char *cp, firstblk[DIRBLKSIZ]; 440: 441: lastbn = lblkno(dp->di_size); 442: if (lastbn >= NDADDR - 1) 443: return (0); 444: if ((newblk = allocblk()) == 0) 445: return (0); 446: dp->di_addr[lastbn + 1] = dp->di_addr[lastbn]; 447: dp->di_addr[lastbn] = newblk; 448: dp->di_size += DEV_BSIZE; 449: getblk(&fileblk, dp->di_addr[lastbn + 1]); 450: if (fileblk.b_errs != NULL) 451: goto bad; 452: bcopy(dirblk.b_buf, firstblk, DIRBLKSIZ); 453: getblk(&fileblk, newblk); 454: if (fileblk.b_errs != NULL) 455: goto bad; 456: bcopy(firstblk, dirblk.b_buf, DIRBLKSIZ); 457: for (cp = &dirblk.b_buf[DIRBLKSIZ]; 458: cp < &dirblk.b_buf[DEV_BSIZE]; 459: cp += DIRBLKSIZ) 460: bcopy((char *)&emptydir, cp, sizeof emptydir); 461: dirty(&fileblk); 462: getblk(&fileblk, dp->di_addr[lastbn + 1]); 463: if (fileblk.b_errs != NULL) 464: goto bad; 465: bcopy((char *)&emptydir, dirblk.b_buf, sizeof emptydir); 466: pwarn("NO SPACE LEFT IN %s", pathname); 467: if (preen) 468: printf(" (EXPANDED)\n"); 469: else if (reply("EXPAND") == 0) 470: goto bad; 471: dirty(&fileblk); 472: inodirty(); 473: return (1); 474: bad: 475: dp->di_addr[lastbn] = dp->di_addr[lastbn + 1]; 476: dp->di_addr[lastbn + 1] = 0; 477: dp->di_size -= DEV_BSIZE; 478: fr_blk(newblk); 479: return (0); 480: } 481: 482: /* 483: * allocate a new directory 484: */ 485: allocdir(parent, request) 486: ino_t parent, request; 487: { 488: ino_t ino; 489: char *cp; 490: DINODE *dp; 491: int st; 492: 493: ino = allocino(request, IFDIR|0755); 494: dirhead.dot_ino = ino; 495: dirhead.dotdot_ino = parent; 496: dp = ginode(ino); 497: getblk(&fileblk, dp->di_addr[0]); 498: if (fileblk.b_errs != NULL) { 499: freeino(ino); 500: return (0); 501: } 502: bcopy((char *)&dirhead, dirblk.b_buf, sizeof dirhead); 503: for (cp = &dirblk.b_buf[DIRBLKSIZ]; 504: cp < &dirblk.b_buf[DEV_BSIZE]; 505: cp += DIRBLKSIZ) 506: bcopy((char *)&emptydir, cp, sizeof emptydir); 507: dirty(&fileblk); 508: dp->di_nlink = 2; 509: inodirty(); 510: if (ino == ROOTINO) { 511: setlncnt(ino, dp->di_nlink); 512: return(ino); 513: } 514: st = getstate(parent); 515: if (st != DSTATE && st != DFOUND) { 516: freeino(ino); 517: return (0); 518: } 519: setstate(ino, st); 520: if (st == DSTATE) { 521: setlncnt(ino, dp->di_nlink); 522: inclncnt(parent); 523: } 524: dp = ginode(parent); 525: dp->di_nlink++; 526: inodirty(); 527: return (ino); 528: } 529: 530: /* 531: * free a directory inode 532: */ 533: freedir(ino, parent) 534: ino_t ino, parent; 535: { 536: DINODE *dp; 537: 538: if (ino != parent) { 539: dp = ginode(parent); 540: dp->di_nlink--; 541: inodirty(); 542: } 543: freeino(ino); 544: } 545: 546: /* 547: * generate a temporary name for the lost+found directory. 548: */ 549: lftempname(bufp, ino) 550: char *bufp; 551: ino_t ino; 552: { 553: register ino_t in; 554: register char *cp; 555: int namlen; 556: 557: cp = bufp + 2; 558: for (in = imax; in > 0; in /= 10) 559: cp++; 560: *--cp = 0; 561: namlen = cp - bufp; 562: in = ino; 563: while (cp > bufp) { 564: *--cp = (in % 10) + '0'; 565: in /= 10; 566: } 567: *cp = '#'; 568: return (namlen); 569: }