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