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