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