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.2 (Berkeley) 08/04/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 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 141 bp = getdirblk(idesc->id_blkno, blksiz); 142 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 143 dp->d_reclen = DIRBLKSIZ; 144 dp->d_ino = 0; 145 dp->d_type = 0; 146 dp->d_namlen = 0; 147 dp->d_name[0] = '\0'; 148 if (fix) 149 dirty(bp); 150 idesc->id_loc += DIRBLKSIZ; 151 idesc->id_filesize -= DIRBLKSIZ; 152 return (dp); 153 } 154 dpok: 155 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 156 return NULL; 157 dploc = idesc->id_loc; 158 dp = (struct direct *)(bp->b_un.b_buf + dploc); 159 idesc->id_loc += dp->d_reclen; 160 idesc->id_filesize -= dp->d_reclen; 161 if ((idesc->id_loc % DIRBLKSIZ) == 0) 162 return (dp); 163 ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 164 if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && 165 dircheck(idesc, ndp) == 0) { 166 size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 167 idesc->id_loc += size; 168 idesc->id_filesize -= size; 169 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 170 bp = getdirblk(idesc->id_blkno, blksiz); 171 dp = (struct direct *)(bp->b_un.b_buf + dploc); 172 dp->d_reclen += size; 173 if (fix) 174 dirty(bp); 175 } 176 return (dp); 177 } 178 179 /* 180 * Verify that a directory entry is valid. 181 * This is a superset of the checks made in the kernel. 182 */ 183 dircheck(idesc, dp) 184 struct inodesc *idesc; 185 register struct direct *dp; 186 { 187 register int size; 188 register char *cp; 189 u_char namlen, type; 190 int spaceleft; 191 192 size = DIRSIZ(!newinofmt, dp); 193 spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 194 # if (BYTE_ORDER == LITTLE_ENDIAN) 195 if (!newinofmt) { 196 type = dp->d_namlen; 197 namlen = dp->d_type; 198 } else { 199 namlen = dp->d_namlen; 200 type = dp->d_type; 201 } 202 # else 203 namlen = dp->d_namlen; 204 type = dp->d_type; 205 # endif 206 if (dp->d_ino < maxino && 207 dp->d_reclen != 0 && 208 dp->d_reclen <= spaceleft && 209 (dp->d_reclen & 0x3) == 0 && 210 dp->d_reclen >= size && 211 idesc->id_filesize >= size && 212 namlen <= MAXNAMLEN && 213 type <= 15) { 214 if (dp->d_ino == 0) 215 return (1); 216 for (cp = dp->d_name, size = 0; size < namlen; size++) 217 if (*cp == 0 || (*cp++ == '/')) 218 return (0); 219 if (*cp == 0) 220 return (1); 221 } 222 return (0); 223 } 224 225 direrror(ino, errmesg) 226 ino_t ino; 227 char *errmesg; 228 { 229 230 fileerror(ino, ino, errmesg); 231 } 232 233 fileerror(cwd, ino, errmesg) 234 ino_t cwd, ino; 235 char *errmesg; 236 { 237 register struct dinode *dp; 238 char pathbuf[MAXPATHLEN + 1]; 239 240 pwarn("%s ", errmesg); 241 pinode(ino); 242 printf("\n"); 243 getpathname(pathbuf, cwd, ino); 244 if (ino < ROOTINO || ino > maxino) { 245 pfatal("NAME=%s\n", pathbuf); 246 return; 247 } 248 dp = ginode(ino); 249 if (ftypeok(dp)) 250 pfatal("%s=%s\n", 251 (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf); 252 else 253 pfatal("NAME=%s\n", pathbuf); 254 } 255 256 adjust(idesc, lcnt) 257 register struct inodesc *idesc; 258 short lcnt; 259 { 260 register struct dinode *dp; 261 262 dp = ginode(idesc->id_number); 263 if (dp->di_nlink == lcnt) { 264 if (linkup(idesc->id_number, (ino_t)0) == 0) 265 clri(idesc, "UNREF", 0); 266 } else { 267 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 268 ((dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE")); 269 pinode(idesc->id_number); 270 printf(" COUNT %d SHOULD BE %d", 271 dp->di_nlink, dp->di_nlink - lcnt); 272 if (preen) { 273 if (lcnt < 0) { 274 printf("\n"); 275 pfatal("LINK COUNT INCREASING"); 276 } 277 printf(" (ADJUSTED)\n"); 278 } 279 if (preen || reply("ADJUST") == 1) { 280 dp->di_nlink -= lcnt; 281 inodirty(); 282 } 283 } 284 } 285 286 mkentry(idesc) 287 struct inodesc *idesc; 288 { 289 register struct direct *dirp = idesc->id_dirp; 290 struct direct newent; 291 int newlen, oldlen; 292 293 newent.d_namlen = strlen(idesc->id_name); 294 newlen = DIRSIZ(0, &newent); 295 if (dirp->d_ino != 0) 296 oldlen = DIRSIZ(0, dirp); 297 else 298 oldlen = 0; 299 if (dirp->d_reclen - oldlen < newlen) 300 return (KEEPON); 301 newent.d_reclen = dirp->d_reclen - oldlen; 302 dirp->d_reclen = oldlen; 303 dirp = (struct direct *)(((char *)dirp) + oldlen); 304 dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ 305 if (newinofmt) { 306 dirp->d_type = typemap[idesc->id_parent]; 307 dirp->d_namlen = newent.d_namlen; 308 } else { 309 # if (BYTE_ORDER == LITTLE_ENDIAN) 310 dirp->d_type = newent.d_namlen; 311 dirp->d_namlen = 0; 312 # else 313 dirp->d_type = 0; 314 dirp->d_namlen = newent.d_namlen; 315 # endif 316 } 317 dirp->d_reclen = newent.d_reclen; 318 bcopy(idesc->id_name, dirp->d_name, (size_t)newent.d_namlen + 1); 319 return (ALTERED|STOP); 320 } 321 322 chgino(idesc) 323 struct inodesc *idesc; 324 { 325 register struct direct *dirp = idesc->id_dirp; 326 327 if (bcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) 328 return (KEEPON); 329 dirp->d_ino = idesc->id_parent; 330 if (newinofmt) 331 dirp->d_type = typemap[idesc->id_parent]; 332 else 333 dirp->d_type = 0; 334 return (ALTERED|STOP); 335 } 336 337 linkup(orphan, parentdir) 338 ino_t orphan; 339 ino_t parentdir; 340 { 341 register struct dinode *dp; 342 int lostdir; 343 ino_t oldlfdir; 344 struct inodesc idesc; 345 char tempname[BUFSIZ]; 346 extern int pass4check(); 347 348 bzero((char *)&idesc, sizeof(struct inodesc)); 349 dp = ginode(orphan); 350 lostdir = (dp->di_mode & IFMT) == IFDIR; 351 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 352 pinode(orphan); 353 if (preen && dp->di_size == 0) 354 return (0); 355 if (preen) 356 printf(" (RECONNECTED)\n"); 357 else 358 if (reply("RECONNECT") == 0) 359 return (0); 360 if (lfdir == 0) { 361 dp = ginode(ROOTINO); 362 idesc.id_name = lfname; 363 idesc.id_type = DATA; 364 idesc.id_func = findino; 365 idesc.id_number = ROOTINO; 366 if ((ckinode(dp, &idesc) & FOUND) != 0) { 367 lfdir = idesc.id_parent; 368 } else { 369 pwarn("NO lost+found DIRECTORY"); 370 if (preen || reply("CREATE")) { 371 lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); 372 if (lfdir != 0) { 373 if (makeentry(ROOTINO, lfdir, lfname) != 0) { 374 if (preen) 375 printf(" (CREATED)\n"); 376 } else { 377 freedir(lfdir, ROOTINO); 378 lfdir = 0; 379 if (preen) 380 printf("\n"); 381 } 382 } 383 } 384 } 385 if (lfdir == 0) { 386 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 387 printf("\n\n"); 388 return (0); 389 } 390 } 391 dp = ginode(lfdir); 392 if ((dp->di_mode & IFMT) != IFDIR) { 393 pfatal("lost+found IS NOT A DIRECTORY"); 394 if (reply("REALLOCATE") == 0) 395 return (0); 396 oldlfdir = lfdir; 397 if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) { 398 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 399 return (0); 400 } 401 if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) { 402 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 403 return (0); 404 } 405 inodirty(); 406 idesc.id_type = ADDR; 407 idesc.id_func = pass4check; 408 idesc.id_number = oldlfdir; 409 adjust(&idesc, lncntp[oldlfdir] + 1); 410 lncntp[oldlfdir] = 0; 411 dp = ginode(lfdir); 412 } 413 if (statemap[lfdir] != DFOUND) { 414 pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 415 return (0); 416 } 417 (void)lftempname(tempname, orphan); 418 if (makeentry(lfdir, orphan, tempname) == 0) { 419 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 420 printf("\n\n"); 421 return (0); 422 } 423 lncntp[orphan]--; 424 if (lostdir) { 425 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 426 parentdir != (ino_t)-1) 427 (void)makeentry(orphan, lfdir, ".."); 428 dp = ginode(lfdir); 429 dp->di_nlink++; 430 inodirty(); 431 lncntp[lfdir]++; 432 pwarn("DIR I=%lu CONNECTED. ", orphan); 433 if (parentdir != (ino_t)-1) 434 printf("PARENT WAS I=%lu\n", parentdir); 435 if (preen == 0) 436 printf("\n"); 437 } 438 return (1); 439 } 440 441 /* 442 * fix an entry in a directory. 443 */ 444 changeino(dir, name, newnum) 445 ino_t dir; 446 char *name; 447 ino_t newnum; 448 { 449 struct inodesc idesc; 450 451 bzero((char *)&idesc, sizeof(struct inodesc)); 452 idesc.id_type = DATA; 453 idesc.id_func = chgino; 454 idesc.id_number = dir; 455 idesc.id_fix = DONTKNOW; 456 idesc.id_name = name; 457 idesc.id_parent = newnum; /* new value for name */ 458 return (ckinode(ginode(dir), &idesc)); 459 } 460 461 /* 462 * make an entry in a directory 463 */ 464 makeentry(parent, ino, name) 465 ino_t parent, ino; 466 char *name; 467 { 468 struct dinode *dp; 469 struct inodesc idesc; 470 char pathbuf[MAXPATHLEN + 1]; 471 472 if (parent < ROOTINO || parent >= maxino || 473 ino < ROOTINO || ino >= maxino) 474 return (0); 475 bzero((char *)&idesc, sizeof(struct inodesc)); 476 idesc.id_type = DATA; 477 idesc.id_func = mkentry; 478 idesc.id_number = parent; 479 idesc.id_parent = ino; /* this is the inode to enter */ 480 idesc.id_fix = DONTKNOW; 481 idesc.id_name = name; 482 dp = ginode(parent); 483 if (dp->di_size % DIRBLKSIZ) { 484 dp->di_size = roundup(dp->di_size, DIRBLKSIZ); 485 inodirty(); 486 } 487 if ((ckinode(dp, &idesc) & ALTERED) != 0) 488 return (1); 489 getpathname(pathbuf, parent, parent); 490 dp = ginode(parent); 491 if (expanddir(dp, pathbuf) == 0) 492 return (0); 493 return (ckinode(dp, &idesc) & ALTERED); 494 } 495 496 /* 497 * Attempt to expand the size of a directory 498 */ 499 expanddir(dp, name) 500 register struct dinode *dp; 501 char *name; 502 { 503 daddr_t lastbn, newblk; 504 register struct bufarea *bp; 505 char *cp, firstblk[DIRBLKSIZ]; 506 507 lastbn = lblkno(&sblock, dp->di_size); 508 if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0) 509 return (0); 510 if ((newblk = allocblk(sblock.fs_frag)) == 0) 511 return (0); 512 dp->di_db[lastbn + 1] = dp->di_db[lastbn]; 513 dp->di_db[lastbn] = newblk; 514 dp->di_size += sblock.fs_bsize; 515 dp->di_blocks += btodb(sblock.fs_bsize); 516 bp = getdirblk(dp->di_db[lastbn + 1], 517 (long)dblksize(&sblock, dp, lastbn + 1)); 518 if (bp->b_errs) 519 goto bad; 520 bcopy(bp->b_un.b_buf, firstblk, DIRBLKSIZ); 521 bp = getdirblk(newblk, sblock.fs_bsize); 522 if (bp->b_errs) 523 goto bad; 524 bcopy(firstblk, bp->b_un.b_buf, DIRBLKSIZ); 525 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 526 cp < &bp->b_un.b_buf[sblock.fs_bsize]; 527 cp += DIRBLKSIZ) 528 bcopy((char *)&emptydir, cp, sizeof emptydir); 529 dirty(bp); 530 bp = getdirblk(dp->di_db[lastbn + 1], 531 (long)dblksize(&sblock, dp, lastbn + 1)); 532 if (bp->b_errs) 533 goto bad; 534 bcopy((char *)&emptydir, bp->b_un.b_buf, sizeof emptydir); 535 pwarn("NO SPACE LEFT IN %s", name); 536 if (preen) 537 printf(" (EXPANDED)\n"); 538 else if (reply("EXPAND") == 0) 539 goto bad; 540 dirty(bp); 541 inodirty(); 542 return (1); 543 bad: 544 dp->di_db[lastbn] = dp->di_db[lastbn + 1]; 545 dp->di_db[lastbn + 1] = 0; 546 dp->di_size -= sblock.fs_bsize; 547 dp->di_blocks -= btodb(sblock.fs_bsize); 548 freeblk(newblk, sblock.fs_frag); 549 return (0); 550 } 551 552 /* 553 * allocate a new directory 554 */ 555 allocdir(parent, request, mode) 556 ino_t parent, request; 557 int mode; 558 { 559 ino_t ino; 560 char *cp; 561 struct dinode *dp; 562 register struct bufarea *bp; 563 struct dirtemplate *dirp; 564 565 ino = allocino(request, IFDIR|mode); 566 if (newinofmt) 567 dirp = &dirhead; 568 else 569 dirp = (struct dirtemplate *)&odirhead; 570 dirp->dot_ino = ino; 571 dirp->dotdot_ino = parent; 572 dp = ginode(ino); 573 bp = getdirblk(dp->di_db[0], sblock.fs_fsize); 574 if (bp->b_errs) { 575 freeino(ino); 576 return (0); 577 } 578 bcopy((char *)dirp, bp->b_un.b_buf, sizeof(struct dirtemplate)); 579 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 580 cp < &bp->b_un.b_buf[sblock.fs_fsize]; 581 cp += DIRBLKSIZ) 582 bcopy((char *)&emptydir, cp, sizeof emptydir); 583 dirty(bp); 584 dp->di_nlink = 2; 585 inodirty(); 586 if (ino == ROOTINO) { 587 lncntp[ino] = dp->di_nlink; 588 cacheino(dp, ino); 589 return(ino); 590 } 591 if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { 592 freeino(ino); 593 return (0); 594 } 595 cacheino(dp, ino); 596 statemap[ino] = statemap[parent]; 597 if (statemap[ino] == DSTATE) { 598 lncntp[ino] = dp->di_nlink; 599 lncntp[parent]++; 600 } 601 dp = ginode(parent); 602 dp->di_nlink++; 603 inodirty(); 604 return (ino); 605 } 606 607 /* 608 * free a directory inode 609 */ 610 freedir(ino, parent) 611 ino_t ino, parent; 612 { 613 struct dinode *dp; 614 615 if (ino != parent) { 616 dp = ginode(parent); 617 dp->di_nlink--; 618 inodirty(); 619 } 620 freeino(ino); 621 } 622 623 /* 624 * generate a temporary name for the lost+found directory. 625 */ 626 lftempname(bufp, ino) 627 char *bufp; 628 ino_t ino; 629 { 630 register ino_t in; 631 register char *cp; 632 int namlen; 633 634 cp = bufp + 2; 635 for (in = maxino; in > 0; in /= 10) 636 cp++; 637 *--cp = 0; 638 namlen = cp - bufp; 639 in = ino; 640 while (cp > bufp) { 641 *--cp = (in % 10) + '0'; 642 in /= 10; 643 } 644 *cp = '#'; 645 return (namlen); 646 } 647 648 /* 649 * Get a directory block. 650 * Insure that it is held until another is requested. 651 */ 652 struct bufarea * 653 getdirblk(blkno, size) 654 daddr_t blkno; 655 long size; 656 { 657 658 if (pdirbp != 0) 659 pdirbp->b_flags &= ~B_INUSE; 660 pdirbp = getdatablk(blkno, size); 661 return (pdirbp); 662 } 663