1 /* 2 * Copyright (c) 1980, 1986, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)dir.c 8.3 (Berkeley) 10/27/94"; 10 #endif /* not lint */ 11 12 #include <sys/param.h> 13 #include <sys/time.h> 14 #include <ufs/ufs/dinode.h> 15 #include <ufs/ufs/dir.h> 16 #include <ufs/ffs/fs.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include "fsck.h" 20 21 char *lfname = "lost+found"; 22 int lfmode = 01777; 23 struct dirtemplate emptydir = { 0, DIRBLKSIZ }; 24 struct dirtemplate dirhead = { 25 0, 12, DT_DIR, 1, ".", 26 0, DIRBLKSIZ - 12, DT_DIR, 2, ".." 27 }; 28 struct odirtemplate odirhead = { 29 0, 12, 1, ".", 30 0, DIRBLKSIZ - 12, 2, ".." 31 }; 32 33 struct direct *fsck_readdir(); 34 struct bufarea *getdirblk(); 35 36 /* 37 * Propagate connected state through the tree. 38 */ 39 propagate() 40 { 41 register struct inoinfo **inpp, *inp; 42 struct inoinfo **inpend; 43 long change; 44 45 inpend = &inpsort[inplast]; 46 do { 47 change = 0; 48 for (inpp = inpsort; inpp < inpend; inpp++) { 49 inp = *inpp; 50 if (inp->i_parent == 0) 51 continue; 52 if (statemap[inp->i_parent] == DFOUND && 53 statemap[inp->i_number] == DSTATE) { 54 statemap[inp->i_number] = DFOUND; 55 change++; 56 } 57 } 58 } while (change > 0); 59 } 60 61 /* 62 * Scan each entry in a directory block. 63 */ 64 dirscan(idesc) 65 register struct inodesc *idesc; 66 { 67 register struct direct *dp; 68 register struct bufarea *bp; 69 int dsize, n; 70 long blksiz; 71 char dbuf[DIRBLKSIZ]; 72 73 if (idesc->id_type != DATA) 74 errexit("wrong type to dirscan %d\n", idesc->id_type); 75 if (idesc->id_entryno == 0 && 76 (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) 77 idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); 78 blksiz = idesc->id_numfrags * sblock.fs_fsize; 79 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 80 idesc->id_filesize -= blksiz; 81 return (SKIP); 82 } 83 idesc->id_loc = 0; 84 for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 85 dsize = dp->d_reclen; 86 bcopy((char *)dp, dbuf, (size_t)dsize); 87 # if (BYTE_ORDER == LITTLE_ENDIAN) 88 if (!newinofmt) { 89 struct direct *tdp = (struct direct *)dbuf; 90 u_char tmp; 91 92 tmp = tdp->d_namlen; 93 tdp->d_namlen = tdp->d_type; 94 tdp->d_type = tmp; 95 } 96 # endif 97 idesc->id_dirp = (struct direct *)dbuf; 98 if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 99 # if (BYTE_ORDER == LITTLE_ENDIAN) 100 if (!newinofmt && !doinglevel2) { 101 struct direct *tdp; 102 u_char tmp; 103 104 tdp = (struct direct *)dbuf; 105 tmp = tdp->d_namlen; 106 tdp->d_namlen = tdp->d_type; 107 tdp->d_type = tmp; 108 } 109 # endif 110 bp = getdirblk(idesc->id_blkno, blksiz); 111 bcopy(dbuf, bp->b_un.b_buf + idesc->id_loc - dsize, 112 (size_t)dsize); 113 dirty(bp); 114 sbdirty(); 115 } 116 if (n & STOP) 117 return (n); 118 } 119 return (idesc->id_filesize > 0 ? KEEPON : STOP); 120 } 121 122 /* 123 * get next entry in a directory. 124 */ 125 struct direct * 126 fsck_readdir(idesc) 127 register struct inodesc *idesc; 128 { 129 register struct direct *dp, *ndp; 130 register struct bufarea *bp; 131 long size, blksiz, fix, dploc; 132 133 blksiz = idesc->id_numfrags * sblock.fs_fsize; 134 bp = getdirblk(idesc->id_blkno, blksiz); 135 if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 && 136 idesc->id_loc < blksiz) { 137 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 138 if (dircheck(idesc, dp)) 139 goto dpok; 140 if (idesc->id_fix == IGNORE) 141 return (0); 142 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 143 bp = getdirblk(idesc->id_blkno, blksiz); 144 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 145 dp->d_reclen = DIRBLKSIZ; 146 dp->d_ino = 0; 147 dp->d_type = 0; 148 dp->d_namlen = 0; 149 dp->d_name[0] = '\0'; 150 if (fix) 151 dirty(bp); 152 idesc->id_loc += DIRBLKSIZ; 153 idesc->id_filesize -= DIRBLKSIZ; 154 return (dp); 155 } 156 dpok: 157 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 158 return NULL; 159 dploc = idesc->id_loc; 160 dp = (struct direct *)(bp->b_un.b_buf + dploc); 161 idesc->id_loc += dp->d_reclen; 162 idesc->id_filesize -= dp->d_reclen; 163 if ((idesc->id_loc % DIRBLKSIZ) == 0) 164 return (dp); 165 ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 166 if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && 167 dircheck(idesc, ndp) == 0) { 168 size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 169 idesc->id_loc += size; 170 idesc->id_filesize -= size; 171 if (idesc->id_fix == IGNORE) 172 return (0); 173 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 174 bp = getdirblk(idesc->id_blkno, blksiz); 175 dp = (struct direct *)(bp->b_un.b_buf + dploc); 176 dp->d_reclen += size; 177 if (fix) 178 dirty(bp); 179 } 180 return (dp); 181 } 182 183 /* 184 * Verify that a directory entry is valid. 185 * This is a superset of the checks made in the kernel. 186 */ 187 dircheck(idesc, dp) 188 struct inodesc *idesc; 189 register struct direct *dp; 190 { 191 register int size; 192 register char *cp; 193 u_char namlen, type; 194 int spaceleft; 195 196 size = DIRSIZ(!newinofmt, dp); 197 spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 198 # if (BYTE_ORDER == LITTLE_ENDIAN) 199 if (!newinofmt) { 200 type = dp->d_namlen; 201 namlen = dp->d_type; 202 } else { 203 namlen = dp->d_namlen; 204 type = dp->d_type; 205 } 206 # else 207 namlen = dp->d_namlen; 208 type = dp->d_type; 209 # endif 210 if (dp->d_ino < maxino && 211 dp->d_reclen != 0 && 212 dp->d_reclen <= spaceleft && 213 (dp->d_reclen & 0x3) == 0 && 214 dp->d_reclen >= size && 215 idesc->id_filesize >= size && 216 namlen <= MAXNAMLEN && 217 type <= 15) { 218 if (dp->d_ino == 0) 219 return (1); 220 for (cp = dp->d_name, size = 0; size < namlen; size++) 221 if (*cp == 0 || (*cp++ == '/')) 222 return (0); 223 if (*cp == 0) 224 return (1); 225 } 226 return (0); 227 } 228 229 direrror(ino, errmesg) 230 ino_t ino; 231 char *errmesg; 232 { 233 234 fileerror(ino, ino, errmesg); 235 } 236 237 fileerror(cwd, ino, errmesg) 238 ino_t cwd, ino; 239 char *errmesg; 240 { 241 register struct dinode *dp; 242 char pathbuf[MAXPATHLEN + 1]; 243 244 pwarn("%s ", errmesg); 245 pinode(ino); 246 printf("\n"); 247 getpathname(pathbuf, cwd, ino); 248 if (ino < ROOTINO || ino > maxino) { 249 pfatal("NAME=%s\n", pathbuf); 250 return; 251 } 252 dp = ginode(ino); 253 if (ftypeok(dp)) 254 pfatal("%s=%s\n", 255 (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf); 256 else 257 pfatal("NAME=%s\n", pathbuf); 258 } 259 260 adjust(idesc, lcnt) 261 register struct inodesc *idesc; 262 short lcnt; 263 { 264 register struct dinode *dp; 265 266 dp = ginode(idesc->id_number); 267 if (dp->di_nlink == lcnt) { 268 if (linkup(idesc->id_number, (ino_t)0) == 0) 269 clri(idesc, "UNREF", 0); 270 } else { 271 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 272 ((dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE")); 273 pinode(idesc->id_number); 274 printf(" COUNT %d SHOULD BE %d", 275 dp->di_nlink, dp->di_nlink - lcnt); 276 if (preen) { 277 if (lcnt < 0) { 278 printf("\n"); 279 pfatal("LINK COUNT INCREASING"); 280 } 281 printf(" (ADJUSTED)\n"); 282 } 283 if (preen || reply("ADJUST") == 1) { 284 dp->di_nlink -= lcnt; 285 inodirty(); 286 } 287 } 288 } 289 290 mkentry(idesc) 291 struct inodesc *idesc; 292 { 293 register struct direct *dirp = idesc->id_dirp; 294 struct direct newent; 295 int newlen, oldlen; 296 297 newent.d_namlen = strlen(idesc->id_name); 298 newlen = DIRSIZ(0, &newent); 299 if (dirp->d_ino != 0) 300 oldlen = DIRSIZ(0, dirp); 301 else 302 oldlen = 0; 303 if (dirp->d_reclen - oldlen < newlen) 304 return (KEEPON); 305 newent.d_reclen = dirp->d_reclen - oldlen; 306 dirp->d_reclen = oldlen; 307 dirp = (struct direct *)(((char *)dirp) + oldlen); 308 dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ 309 if (newinofmt) { 310 dirp->d_type = typemap[idesc->id_parent]; 311 dirp->d_namlen = newent.d_namlen; 312 } else { 313 # if (BYTE_ORDER == LITTLE_ENDIAN) 314 dirp->d_type = newent.d_namlen; 315 dirp->d_namlen = 0; 316 # else 317 dirp->d_type = 0; 318 dirp->d_namlen = newent.d_namlen; 319 # endif 320 } 321 dirp->d_reclen = newent.d_reclen; 322 bcopy(idesc->id_name, dirp->d_name, (size_t)newent.d_namlen + 1); 323 return (ALTERED|STOP); 324 } 325 326 chgino(idesc) 327 struct inodesc *idesc; 328 { 329 register struct direct *dirp = idesc->id_dirp; 330 331 if (bcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) 332 return (KEEPON); 333 dirp->d_ino = idesc->id_parent; 334 if (newinofmt) 335 dirp->d_type = typemap[idesc->id_parent]; 336 else 337 dirp->d_type = 0; 338 return (ALTERED|STOP); 339 } 340 341 linkup(orphan, parentdir) 342 ino_t orphan; 343 ino_t parentdir; 344 { 345 register struct dinode *dp; 346 int lostdir; 347 ino_t oldlfdir; 348 struct inodesc idesc; 349 char tempname[BUFSIZ]; 350 extern int pass4check(); 351 352 bzero((char *)&idesc, sizeof(struct inodesc)); 353 dp = ginode(orphan); 354 lostdir = (dp->di_mode & IFMT) == IFDIR; 355 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 356 pinode(orphan); 357 if (preen && dp->di_size == 0) 358 return (0); 359 if (preen) 360 printf(" (RECONNECTED)\n"); 361 else 362 if (reply("RECONNECT") == 0) 363 return (0); 364 if (lfdir == 0) { 365 dp = ginode(ROOTINO); 366 idesc.id_name = lfname; 367 idesc.id_type = DATA; 368 idesc.id_func = findino; 369 idesc.id_number = ROOTINO; 370 if ((ckinode(dp, &idesc) & FOUND) != 0) { 371 lfdir = idesc.id_parent; 372 } else { 373 pwarn("NO lost+found DIRECTORY"); 374 if (preen || reply("CREATE")) { 375 lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); 376 if (lfdir != 0) { 377 if (makeentry(ROOTINO, lfdir, lfname) != 0) { 378 if (preen) 379 printf(" (CREATED)\n"); 380 } else { 381 freedir(lfdir, ROOTINO); 382 lfdir = 0; 383 if (preen) 384 printf("\n"); 385 } 386 } 387 } 388 } 389 if (lfdir == 0) { 390 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 391 printf("\n\n"); 392 return (0); 393 } 394 } 395 dp = ginode(lfdir); 396 if ((dp->di_mode & IFMT) != IFDIR) { 397 pfatal("lost+found IS NOT A DIRECTORY"); 398 if (reply("REALLOCATE") == 0) 399 return (0); 400 oldlfdir = lfdir; 401 if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) { 402 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 403 return (0); 404 } 405 if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) { 406 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 407 return (0); 408 } 409 inodirty(); 410 idesc.id_type = ADDR; 411 idesc.id_func = pass4check; 412 idesc.id_number = oldlfdir; 413 adjust(&idesc, lncntp[oldlfdir] + 1); 414 lncntp[oldlfdir] = 0; 415 dp = ginode(lfdir); 416 } 417 if (statemap[lfdir] != DFOUND) { 418 pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 419 return (0); 420 } 421 (void)lftempname(tempname, orphan); 422 if (makeentry(lfdir, orphan, tempname) == 0) { 423 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 424 printf("\n\n"); 425 return (0); 426 } 427 lncntp[orphan]--; 428 if (lostdir) { 429 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 430 parentdir != (ino_t)-1) 431 (void)makeentry(orphan, lfdir, ".."); 432 dp = ginode(lfdir); 433 dp->di_nlink++; 434 inodirty(); 435 lncntp[lfdir]++; 436 pwarn("DIR I=%lu CONNECTED. ", orphan); 437 if (parentdir != (ino_t)-1) 438 printf("PARENT WAS I=%lu\n", parentdir); 439 if (preen == 0) 440 printf("\n"); 441 } 442 return (1); 443 } 444 445 /* 446 * fix an entry in a directory. 447 */ 448 changeino(dir, name, newnum) 449 ino_t dir; 450 char *name; 451 ino_t newnum; 452 { 453 struct inodesc idesc; 454 455 bzero((char *)&idesc, sizeof(struct inodesc)); 456 idesc.id_type = DATA; 457 idesc.id_func = chgino; 458 idesc.id_number = dir; 459 idesc.id_fix = DONTKNOW; 460 idesc.id_name = name; 461 idesc.id_parent = newnum; /* new value for name */ 462 return (ckinode(ginode(dir), &idesc)); 463 } 464 465 /* 466 * make an entry in a directory 467 */ 468 makeentry(parent, ino, name) 469 ino_t parent, ino; 470 char *name; 471 { 472 struct dinode *dp; 473 struct inodesc idesc; 474 char pathbuf[MAXPATHLEN + 1]; 475 476 if (parent < ROOTINO || parent >= maxino || 477 ino < ROOTINO || ino >= maxino) 478 return (0); 479 bzero((char *)&idesc, sizeof(struct inodesc)); 480 idesc.id_type = DATA; 481 idesc.id_func = mkentry; 482 idesc.id_number = parent; 483 idesc.id_parent = ino; /* this is the inode to enter */ 484 idesc.id_fix = DONTKNOW; 485 idesc.id_name = name; 486 dp = ginode(parent); 487 if (dp->di_size % DIRBLKSIZ) { 488 dp->di_size = roundup(dp->di_size, DIRBLKSIZ); 489 inodirty(); 490 } 491 if ((ckinode(dp, &idesc) & ALTERED) != 0) 492 return (1); 493 getpathname(pathbuf, parent, parent); 494 dp = ginode(parent); 495 if (expanddir(dp, pathbuf) == 0) 496 return (0); 497 return (ckinode(dp, &idesc) & ALTERED); 498 } 499 500 /* 501 * Attempt to expand the size of a directory 502 */ 503 expanddir(dp, name) 504 register struct dinode *dp; 505 char *name; 506 { 507 daddr_t lastbn, newblk; 508 register struct bufarea *bp; 509 char *cp, firstblk[DIRBLKSIZ]; 510 511 lastbn = lblkno(&sblock, dp->di_size); 512 if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0) 513 return (0); 514 if ((newblk = allocblk(sblock.fs_frag)) == 0) 515 return (0); 516 dp->di_db[lastbn + 1] = dp->di_db[lastbn]; 517 dp->di_db[lastbn] = newblk; 518 dp->di_size += sblock.fs_bsize; 519 dp->di_blocks += btodb(sblock.fs_bsize); 520 bp = getdirblk(dp->di_db[lastbn + 1], 521 (long)dblksize(&sblock, dp, lastbn + 1)); 522 if (bp->b_errs) 523 goto bad; 524 bcopy(bp->b_un.b_buf, firstblk, DIRBLKSIZ); 525 bp = getdirblk(newblk, sblock.fs_bsize); 526 if (bp->b_errs) 527 goto bad; 528 bcopy(firstblk, bp->b_un.b_buf, DIRBLKSIZ); 529 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 530 cp < &bp->b_un.b_buf[sblock.fs_bsize]; 531 cp += DIRBLKSIZ) 532 bcopy((char *)&emptydir, cp, sizeof emptydir); 533 dirty(bp); 534 bp = getdirblk(dp->di_db[lastbn + 1], 535 (long)dblksize(&sblock, dp, lastbn + 1)); 536 if (bp->b_errs) 537 goto bad; 538 bcopy((char *)&emptydir, bp->b_un.b_buf, sizeof emptydir); 539 pwarn("NO SPACE LEFT IN %s", name); 540 if (preen) 541 printf(" (EXPANDED)\n"); 542 else if (reply("EXPAND") == 0) 543 goto bad; 544 dirty(bp); 545 inodirty(); 546 return (1); 547 bad: 548 dp->di_db[lastbn] = dp->di_db[lastbn + 1]; 549 dp->di_db[lastbn + 1] = 0; 550 dp->di_size -= sblock.fs_bsize; 551 dp->di_blocks -= btodb(sblock.fs_bsize); 552 freeblk(newblk, sblock.fs_frag); 553 return (0); 554 } 555 556 /* 557 * allocate a new directory 558 */ 559 allocdir(parent, request, mode) 560 ino_t parent, request; 561 int mode; 562 { 563 ino_t ino; 564 char *cp; 565 struct dinode *dp; 566 register struct bufarea *bp; 567 struct dirtemplate *dirp; 568 569 ino = allocino(request, IFDIR|mode); 570 if (newinofmt) 571 dirp = &dirhead; 572 else 573 dirp = (struct dirtemplate *)&odirhead; 574 dirp->dot_ino = ino; 575 dirp->dotdot_ino = parent; 576 dp = ginode(ino); 577 bp = getdirblk(dp->di_db[0], sblock.fs_fsize); 578 if (bp->b_errs) { 579 freeino(ino); 580 return (0); 581 } 582 bcopy((char *)dirp, bp->b_un.b_buf, sizeof(struct dirtemplate)); 583 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 584 cp < &bp->b_un.b_buf[sblock.fs_fsize]; 585 cp += DIRBLKSIZ) 586 bcopy((char *)&emptydir, cp, sizeof emptydir); 587 dirty(bp); 588 dp->di_nlink = 2; 589 inodirty(); 590 if (ino == ROOTINO) { 591 lncntp[ino] = dp->di_nlink; 592 cacheino(dp, ino); 593 return(ino); 594 } 595 if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { 596 freeino(ino); 597 return (0); 598 } 599 cacheino(dp, ino); 600 statemap[ino] = statemap[parent]; 601 if (statemap[ino] == DSTATE) { 602 lncntp[ino] = dp->di_nlink; 603 lncntp[parent]++; 604 } 605 dp = ginode(parent); 606 dp->di_nlink++; 607 inodirty(); 608 return (ino); 609 } 610 611 /* 612 * free a directory inode 613 */ 614 freedir(ino, parent) 615 ino_t ino, parent; 616 { 617 struct dinode *dp; 618 619 if (ino != parent) { 620 dp = ginode(parent); 621 dp->di_nlink--; 622 inodirty(); 623 } 624 freeino(ino); 625 } 626 627 /* 628 * generate a temporary name for the lost+found directory. 629 */ 630 lftempname(bufp, ino) 631 char *bufp; 632 ino_t ino; 633 { 634 register ino_t in; 635 register char *cp; 636 int namlen; 637 638 cp = bufp + 2; 639 for (in = maxino; in > 0; in /= 10) 640 cp++; 641 *--cp = 0; 642 namlen = cp - bufp; 643 in = ino; 644 while (cp > bufp) { 645 *--cp = (in % 10) + '0'; 646 in /= 10; 647 } 648 *cp = '#'; 649 return (namlen); 650 } 651 652 /* 653 * Get a directory block. 654 * Insure that it is held until another is requested. 655 */ 656 struct bufarea * 657 getdirblk(blkno, size) 658 daddr_t blkno; 659 long size; 660 { 661 662 if (pdirbp != 0) 663 pdirbp->b_flags &= ~B_INUSE; 664 pdirbp = getdatablk(blkno, size); 665 return (pdirbp); 666 } 667