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.1 (Berkeley) 06/05/93"; 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 else 308 dirp->d_type = 0; 309 dirp->d_reclen = newent.d_reclen; 310 dirp->d_namlen = newent.d_namlen; 311 bcopy(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1); 312 return (ALTERED|STOP); 313 } 314 315 chgino(idesc) 316 struct inodesc *idesc; 317 { 318 register struct direct *dirp = idesc->id_dirp; 319 320 if (bcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) 321 return (KEEPON); 322 dirp->d_ino = idesc->id_parent; 323 if (newinofmt) 324 dirp->d_type = typemap[idesc->id_parent]; 325 else 326 dirp->d_type = 0; 327 return (ALTERED|STOP); 328 } 329 330 linkup(orphan, parentdir) 331 ino_t orphan; 332 ino_t parentdir; 333 { 334 register struct dinode *dp; 335 int lostdir; 336 ino_t oldlfdir; 337 struct inodesc idesc; 338 char tempname[BUFSIZ]; 339 extern int pass4check(); 340 341 bzero((char *)&idesc, sizeof(struct inodesc)); 342 dp = ginode(orphan); 343 lostdir = (dp->di_mode & IFMT) == IFDIR; 344 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 345 pinode(orphan); 346 if (preen && dp->di_size == 0) 347 return (0); 348 if (preen) 349 printf(" (RECONNECTED)\n"); 350 else 351 if (reply("RECONNECT") == 0) 352 return (0); 353 if (lfdir == 0) { 354 dp = ginode(ROOTINO); 355 idesc.id_name = lfname; 356 idesc.id_type = DATA; 357 idesc.id_func = findino; 358 idesc.id_number = ROOTINO; 359 if ((ckinode(dp, &idesc) & FOUND) != 0) { 360 lfdir = idesc.id_parent; 361 } else { 362 pwarn("NO lost+found DIRECTORY"); 363 if (preen || reply("CREATE")) { 364 lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); 365 if (lfdir != 0) { 366 if (makeentry(ROOTINO, lfdir, lfname) != 0) { 367 if (preen) 368 printf(" (CREATED)\n"); 369 } else { 370 freedir(lfdir, ROOTINO); 371 lfdir = 0; 372 if (preen) 373 printf("\n"); 374 } 375 } 376 } 377 } 378 if (lfdir == 0) { 379 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 380 printf("\n\n"); 381 return (0); 382 } 383 } 384 dp = ginode(lfdir); 385 if ((dp->di_mode & IFMT) != IFDIR) { 386 pfatal("lost+found IS NOT A DIRECTORY"); 387 if (reply("REALLOCATE") == 0) 388 return (0); 389 oldlfdir = lfdir; 390 if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) { 391 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 392 return (0); 393 } 394 if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) { 395 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 396 return (0); 397 } 398 inodirty(); 399 idesc.id_type = ADDR; 400 idesc.id_func = pass4check; 401 idesc.id_number = oldlfdir; 402 adjust(&idesc, lncntp[oldlfdir] + 1); 403 lncntp[oldlfdir] = 0; 404 dp = ginode(lfdir); 405 } 406 if (statemap[lfdir] != DFOUND) { 407 pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 408 return (0); 409 } 410 (void)lftempname(tempname, orphan); 411 if (makeentry(lfdir, orphan, tempname) == 0) { 412 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 413 printf("\n\n"); 414 return (0); 415 } 416 lncntp[orphan]--; 417 if (lostdir) { 418 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 419 parentdir != (ino_t)-1) 420 (void)makeentry(orphan, lfdir, ".."); 421 dp = ginode(lfdir); 422 dp->di_nlink++; 423 inodirty(); 424 lncntp[lfdir]++; 425 pwarn("DIR I=%lu CONNECTED. ", orphan); 426 if (parentdir != (ino_t)-1) 427 printf("PARENT WAS I=%lu\n", parentdir); 428 if (preen == 0) 429 printf("\n"); 430 } 431 return (1); 432 } 433 434 /* 435 * fix an entry in a directory. 436 */ 437 changeino(dir, name, newnum) 438 ino_t dir; 439 char *name; 440 ino_t newnum; 441 { 442 struct inodesc idesc; 443 444 bzero((char *)&idesc, sizeof(struct inodesc)); 445 idesc.id_type = DATA; 446 idesc.id_func = chgino; 447 idesc.id_number = dir; 448 idesc.id_fix = DONTKNOW; 449 idesc.id_name = name; 450 idesc.id_parent = newnum; /* new value for name */ 451 return (ckinode(ginode(dir), &idesc)); 452 } 453 454 /* 455 * make an entry in a directory 456 */ 457 makeentry(parent, ino, name) 458 ino_t parent, ino; 459 char *name; 460 { 461 struct dinode *dp; 462 struct inodesc idesc; 463 char pathbuf[MAXPATHLEN + 1]; 464 465 if (parent < ROOTINO || parent >= maxino || 466 ino < ROOTINO || ino >= maxino) 467 return (0); 468 bzero((char *)&idesc, sizeof(struct inodesc)); 469 idesc.id_type = DATA; 470 idesc.id_func = mkentry; 471 idesc.id_number = parent; 472 idesc.id_parent = ino; /* this is the inode to enter */ 473 idesc.id_fix = DONTKNOW; 474 idesc.id_name = name; 475 dp = ginode(parent); 476 if (dp->di_size % DIRBLKSIZ) { 477 dp->di_size = roundup(dp->di_size, DIRBLKSIZ); 478 inodirty(); 479 } 480 if ((ckinode(dp, &idesc) & ALTERED) != 0) 481 return (1); 482 getpathname(pathbuf, parent, parent); 483 dp = ginode(parent); 484 if (expanddir(dp, pathbuf) == 0) 485 return (0); 486 return (ckinode(dp, &idesc) & ALTERED); 487 } 488 489 /* 490 * Attempt to expand the size of a directory 491 */ 492 expanddir(dp, name) 493 register struct dinode *dp; 494 char *name; 495 { 496 daddr_t lastbn, newblk; 497 register struct bufarea *bp; 498 char *cp, firstblk[DIRBLKSIZ]; 499 500 lastbn = lblkno(&sblock, dp->di_size); 501 if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0) 502 return (0); 503 if ((newblk = allocblk(sblock.fs_frag)) == 0) 504 return (0); 505 dp->di_db[lastbn + 1] = dp->di_db[lastbn]; 506 dp->di_db[lastbn] = newblk; 507 dp->di_size += sblock.fs_bsize; 508 dp->di_blocks += btodb(sblock.fs_bsize); 509 bp = getdirblk(dp->di_db[lastbn + 1], 510 (long)dblksize(&sblock, dp, lastbn + 1)); 511 if (bp->b_errs) 512 goto bad; 513 bcopy(bp->b_un.b_buf, firstblk, DIRBLKSIZ); 514 bp = getdirblk(newblk, sblock.fs_bsize); 515 if (bp->b_errs) 516 goto bad; 517 bcopy(firstblk, bp->b_un.b_buf, DIRBLKSIZ); 518 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 519 cp < &bp->b_un.b_buf[sblock.fs_bsize]; 520 cp += DIRBLKSIZ) 521 bcopy((char *)&emptydir, cp, sizeof emptydir); 522 dirty(bp); 523 bp = getdirblk(dp->di_db[lastbn + 1], 524 (long)dblksize(&sblock, dp, lastbn + 1)); 525 if (bp->b_errs) 526 goto bad; 527 bcopy((char *)&emptydir, bp->b_un.b_buf, sizeof emptydir); 528 pwarn("NO SPACE LEFT IN %s", name); 529 if (preen) 530 printf(" (EXPANDED)\n"); 531 else if (reply("EXPAND") == 0) 532 goto bad; 533 dirty(bp); 534 inodirty(); 535 return (1); 536 bad: 537 dp->di_db[lastbn] = dp->di_db[lastbn + 1]; 538 dp->di_db[lastbn + 1] = 0; 539 dp->di_size -= sblock.fs_bsize; 540 dp->di_blocks -= btodb(sblock.fs_bsize); 541 freeblk(newblk, sblock.fs_frag); 542 return (0); 543 } 544 545 /* 546 * allocate a new directory 547 */ 548 allocdir(parent, request, mode) 549 ino_t parent, request; 550 int mode; 551 { 552 ino_t ino; 553 char *cp; 554 struct dinode *dp; 555 register struct bufarea *bp; 556 struct dirtemplate *dirp; 557 558 ino = allocino(request, IFDIR|mode); 559 if (newinofmt) 560 dirp = &dirhead; 561 else 562 dirp = (struct dirtemplate *)&odirhead; 563 dirp->dot_ino = ino; 564 dirp->dotdot_ino = parent; 565 dp = ginode(ino); 566 bp = getdirblk(dp->di_db[0], sblock.fs_fsize); 567 if (bp->b_errs) { 568 freeino(ino); 569 return (0); 570 } 571 bcopy((char *)dirp, bp->b_un.b_buf, sizeof(struct dirtemplate)); 572 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 573 cp < &bp->b_un.b_buf[sblock.fs_fsize]; 574 cp += DIRBLKSIZ) 575 bcopy((char *)&emptydir, cp, sizeof emptydir); 576 dirty(bp); 577 dp->di_nlink = 2; 578 inodirty(); 579 if (ino == ROOTINO) { 580 lncntp[ino] = dp->di_nlink; 581 cacheino(dp, ino); 582 return(ino); 583 } 584 if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { 585 freeino(ino); 586 return (0); 587 } 588 cacheino(dp, ino); 589 statemap[ino] = statemap[parent]; 590 if (statemap[ino] == DSTATE) { 591 lncntp[ino] = dp->di_nlink; 592 lncntp[parent]++; 593 } 594 dp = ginode(parent); 595 dp->di_nlink++; 596 inodirty(); 597 return (ino); 598 } 599 600 /* 601 * free a directory inode 602 */ 603 freedir(ino, parent) 604 ino_t ino, parent; 605 { 606 struct dinode *dp; 607 608 if (ino != parent) { 609 dp = ginode(parent); 610 dp->di_nlink--; 611 inodirty(); 612 } 613 freeino(ino); 614 } 615 616 /* 617 * generate a temporary name for the lost+found directory. 618 */ 619 lftempname(bufp, ino) 620 char *bufp; 621 ino_t ino; 622 { 623 register ino_t in; 624 register char *cp; 625 int namlen; 626 627 cp = bufp + 2; 628 for (in = maxino; in > 0; in /= 10) 629 cp++; 630 *--cp = 0; 631 namlen = cp - bufp; 632 in = ino; 633 while (cp > bufp) { 634 *--cp = (in % 10) + '0'; 635 in /= 10; 636 } 637 *cp = '#'; 638 return (namlen); 639 } 640 641 /* 642 * Get a directory block. 643 * Insure that it is held until another is requested. 644 */ 645 struct bufarea * 646 getdirblk(blkno, size) 647 daddr_t blkno; 648 long size; 649 { 650 651 if (pdirbp != 0) 652 pdirbp->b_flags &= ~B_INUSE; 653 pdirbp = getdatablk(blkno, size); 654 return (pdirbp); 655 } 656