1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1980, 1986, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #if 0 33 #endif 34 #include <sys/cdefs.h> 35 #include <sys/param.h> 36 #include <sys/time.h> 37 #include <sys/types.h> 38 #include <sys/sysctl.h> 39 40 #include <ufs/ufs/dinode.h> 41 #include <ufs/ufs/dir.h> 42 #include <ufs/ffs/fs.h> 43 44 #include <err.h> 45 #include <string.h> 46 47 #include "fsck.h" 48 49 static struct dirtemplate emptydir = { 50 0, DIRBLKSIZ, DT_UNKNOWN, 0, "", 51 0, 0, DT_UNKNOWN, 0, "" 52 }; 53 static struct dirtemplate dirhead = { 54 0, 12, DT_DIR, 1, ".", 55 0, DIRBLKSIZ - 12, DT_DIR, 2, ".." 56 }; 57 58 static int chgino(struct inodesc *); 59 static int dircheck(struct inodesc *, struct bufarea *, struct direct *); 60 static int expanddir(struct inode *ip, char *name); 61 static struct direct *fsck_readdir(struct inodesc *); 62 static struct bufarea *getdirblk(ufs2_daddr_t blkno, long size); 63 static int lftempname(char *bufp, ino_t ino); 64 static int mkentry(struct inodesc *); 65 66 /* 67 * Propagate connected state through the tree. 68 */ 69 void 70 propagate(void) 71 { 72 struct inoinfo **inpp, *inp; 73 struct inoinfo **inpend; 74 long change; 75 76 inpend = &inpsort[inplast]; 77 do { 78 change = 0; 79 for (inpp = inpsort; inpp < inpend; inpp++) { 80 inp = *inpp; 81 if (inp->i_parent == 0) 82 continue; 83 if (inoinfo(inp->i_parent)->ino_state == DFOUND && 84 INO_IS_DUNFOUND(inp->i_number)) { 85 inoinfo(inp->i_number)->ino_state = DFOUND; 86 check_dirdepth(inp); 87 change++; 88 } 89 } 90 } while (change > 0); 91 } 92 93 /* 94 * Check that the recorded depth of the directory is correct. 95 */ 96 void 97 check_dirdepth(struct inoinfo *inp) 98 { 99 struct inoinfo *parentinp; 100 struct inode ip; 101 union dinode *dp; 102 int saveresolved; 103 size_t size; 104 static int updateasked, dirdepthupdate; 105 106 if ((parentinp = getinoinfo(inp->i_parent)) == NULL) { 107 pfatal("check_dirdepth: UNKNOWN PARENT DIR"); 108 return; 109 } 110 /* 111 * If depth is correct, nothing to do. 112 */ 113 if (parentinp->i_depth + 1 == inp->i_depth) 114 return; 115 /* 116 * Only the root inode should have depth of 0, so if any other 117 * directory has a depth of 0 then this is an old filesystem 118 * that has not been tracking directory depth. Ask just once 119 * whether it should start tracking directory depth. 120 */ 121 if (inp->i_depth == 0 && updateasked == 0) { 122 updateasked = 1; 123 if (preen) { 124 pwarn("UPDATING FILESYSTEM TO TRACK DIRECTORY DEPTH\n"); 125 dirdepthupdate = 1; 126 } else { 127 /* 128 * The file system can be marked clean even if 129 * a directory does not have the right depth. 130 * Hence, resolved should not be cleared when 131 * the filesystem does not update directory depths. 132 */ 133 saveresolved = resolved; 134 dirdepthupdate = 135 reply("UPDATE FILESYSTEM TO TRACK DIRECTORY DEPTH"); 136 resolved = saveresolved; 137 } 138 } 139 /* 140 * If we are not converting or we are running in no-write mode 141 * there is nothing more to do. 142 */ 143 if ((inp->i_depth == 0 && dirdepthupdate == 0) || 144 (fswritefd < 0 && bkgrdflag == 0)) 145 return; 146 /* 147 * Individual directory at wrong depth. Report it and correct if 148 * in preen mode or ask if in interactive mode. Note that if a 149 * directory is renamed to a new location that is at a different 150 * level in the tree, its depth will be recalculated, but none of 151 * the directories that it contains will be updated. Thus it is 152 * not unexpected to find directories with incorrect depths. No 153 * operational harm will come from this though new directory 154 * placement in the subtree may not be as optimal until the depths 155 * of the affected directories are corrected. 156 * 157 * To avoid much spurious output on otherwise clean filesystems 158 * we only generate detailed output when the debug flag is given. 159 */ 160 ginode(inp->i_number, &ip); 161 dp = ip.i_dp; 162 if (inp->i_depth != 0 && debug) { 163 pwarn("DIRECTORY"); 164 prtinode(&ip); 165 printf(" DEPTH %d SHOULD BE %d", inp->i_depth, 166 parentinp->i_depth + 1); 167 if (preen == 0 && reply("ADJUST") == 0) { 168 irelse(&ip); 169 return; 170 } 171 if (preen) 172 printf(" (ADJUSTED)\n"); 173 } 174 inp->i_depth = parentinp->i_depth + 1; 175 if (bkgrdflag == 0) { 176 DIP_SET(dp, di_dirdepth, inp->i_depth); 177 inodirty(&ip); 178 } else { 179 cmd.value = inp->i_number; 180 cmd.size = (int64_t)inp->i_depth - DIP(dp, di_dirdepth); 181 if (debug) 182 printf("adjdepth ino %ld amt %jd\n", (long)cmd.value, 183 (intmax_t)cmd.size); 184 size = MIBSIZE; 185 if (sysctlnametomib("vfs.ffs.adjdepth", adjdepth, &size) < 0 || 186 sysctl(adjdepth, MIBSIZE, 0, 0, &cmd, sizeof cmd) == -1) 187 rwerror("ADJUST INODE DEPTH", cmd.value); 188 } 189 irelse(&ip); 190 } 191 192 /* 193 * Scan each entry in a directory block. 194 */ 195 int 196 dirscan(struct inodesc *idesc) 197 { 198 struct direct *dp; 199 struct bufarea *bp; 200 u_int dsize, n; 201 long blksiz; 202 char dbuf[DIRBLKSIZ]; 203 204 if (idesc->id_type != DATA) 205 errx(EEXIT, "wrong type to dirscan %d", idesc->id_type); 206 if (idesc->id_entryno == 0 && 207 (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) 208 idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); 209 blksiz = idesc->id_numfrags * sblock.fs_fsize; 210 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 211 idesc->id_filesize -= blksiz; 212 return (SKIP); 213 } 214 idesc->id_loc = 0; 215 for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 216 dsize = dp->d_reclen; 217 if (dsize > sizeof(dbuf)) 218 dsize = sizeof(dbuf); 219 memmove(dbuf, dp, (size_t)dsize); 220 idesc->id_dirp = (struct direct *)dbuf; 221 if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 222 bp = getdirblk(idesc->id_blkno, blksiz); 223 if (bp->b_errs != 0) 224 return (STOP); 225 memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf, 226 (size_t)dsize); 227 dirty(bp); 228 sbdirty(); 229 } 230 if (n & STOP) 231 return (n); 232 } 233 return (idesc->id_filesize > 0 ? KEEPON : STOP); 234 } 235 236 /* 237 * Get and verify the next entry in a directory. 238 * We also verify that if there is another entry in the block that it is 239 * valid, so if it is not valid it can be subsumed into the current entry. 240 */ 241 static struct direct * 242 fsck_readdir(struct inodesc *idesc) 243 { 244 struct direct *dp, *ndp; 245 struct bufarea *bp; 246 long size, blksiz, subsume_ndp; 247 248 subsume_ndp = 0; 249 blksiz = idesc->id_numfrags * sblock.fs_fsize; 250 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 251 return (NULL); 252 bp = getdirblk(idesc->id_blkno, blksiz); 253 if (bp->b_errs != 0) 254 return (NULL); 255 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 256 /* 257 * Only need to check current entry if it is the first in the 258 * the block, as later entries will have been checked in the 259 * previous call to this function. 260 */ 261 if (idesc->id_loc % DIRBLKSIZ != 0 || dircheck(idesc, bp, dp) != 0) { 262 /* 263 * Current entry is good, update to point at next. 264 */ 265 idesc->id_loc += dp->d_reclen; 266 idesc->id_filesize -= dp->d_reclen; 267 /* 268 * If at end of directory block, just return this entry. 269 */ 270 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz || 271 idesc->id_loc % DIRBLKSIZ == 0) 272 return (dp); 273 /* 274 * If the next entry good, return this entry. 275 */ 276 ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 277 if (dircheck(idesc, bp, ndp) != 0) 278 return (dp); 279 /* 280 * The next entry is bad, so subsume it and the remainder 281 * of this directory block into this entry. 282 */ 283 subsume_ndp = 1; 284 } 285 /* 286 * Current or next entry is bad. Zap current entry or 287 * subsume next entry into current entry as appropriate. 288 */ 289 size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 290 idesc->id_loc += size; 291 idesc->id_filesize -= size; 292 if (idesc->id_fix == IGNORE) 293 return (NULL); 294 if (subsume_ndp) { 295 memset(ndp, 0, size); 296 dp->d_reclen += size; 297 } else { 298 memset(dp, 0, size); 299 dp->d_reclen = size; 300 } 301 if (dofix(idesc, "DIRECTORY CORRUPTED")) 302 dirty(bp); 303 return (dp); 304 } 305 306 /* 307 * Verify that a directory entry is valid. 308 * This is a superset of the checks made in the kernel. 309 * Also optionally clears padding and unused directory space. 310 * 311 * Returns 0 if the entry is bad, 1 if the entry is good. 312 */ 313 static int 314 dircheck(struct inodesc *idesc, struct bufarea *bp, struct direct *dp) 315 { 316 size_t size; 317 char *cp; 318 u_int8_t namlen; 319 int spaceleft, modified, unused; 320 321 spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 322 size = DIRSIZ(0, dp); 323 if (dp->d_reclen == 0 || 324 dp->d_reclen > spaceleft || 325 dp->d_reclen < size || 326 idesc->id_filesize < size || 327 (dp->d_reclen & (DIR_ROUNDUP - 1)) != 0) 328 goto bad; 329 modified = 0; 330 if (dp->d_ino == 0) { 331 if (!zflag || fswritefd < 0) 332 return (1); 333 /* 334 * Special case of an unused directory entry. Normally only 335 * occurs at the beginning of a directory block when the block 336 * contains no entries. Other than the first entry in a 337 * directory block, the kernel coalesces unused space with 338 * the previous entry by extending its d_reclen. However, 339 * when cleaning up a directory, fsck may set d_ino to zero 340 * in the middle of a directory block. If we're clearing out 341 * directory cruft (-z flag), then make sure that all directory 342 * space in entries with d_ino == 0 gets fully cleared. 343 */ 344 if (dp->d_type != 0) { 345 dp->d_type = 0; 346 modified = 1; 347 } 348 if (dp->d_namlen != 0) { 349 dp->d_namlen = 0; 350 modified = 1; 351 } 352 unused = dp->d_reclen - __offsetof(struct direct, d_name); 353 for (cp = dp->d_name; unused > 0; unused--, cp++) { 354 if (*cp != '\0') { 355 *cp = '\0'; 356 modified = 1; 357 } 358 } 359 if (modified) 360 dirty(bp); 361 return (1); 362 } 363 /* 364 * The d_type field should not be tested here. A bad type is an error 365 * in the entry itself but is not a corruption of the directory 366 * structure itself. So blowing away all the remaining entries in the 367 * directory block is inappropriate. Rather the type error should be 368 * checked in pass1 and fixed there. 369 * 370 * The name validation should also be done in pass1 although the 371 * check to see if the name is longer than fits in the space 372 * allocated for it (i.e., the *cp != '\0' fails after exiting the 373 * loop below) then it really is a structural error that requires 374 * the stronger action taken here. 375 */ 376 namlen = dp->d_namlen; 377 if (namlen == 0 || dp->d_type > 15) 378 goto bad; 379 for (cp = dp->d_name, size = 0; size < namlen; size++) { 380 if (*cp == '\0' || *cp++ == '/') 381 goto bad; 382 } 383 if (*cp != '\0') 384 goto bad; 385 if (zflag && fswritefd >= 0) { 386 /* 387 * Clear unused directory entry space, including the d_name 388 * padding. 389 */ 390 /* First figure the number of pad bytes. */ 391 unused = roundup2(namlen + 1, DIR_ROUNDUP) - (namlen + 1); 392 393 /* Add in the free space to the end of the record. */ 394 unused += dp->d_reclen - DIRSIZ(0, dp); 395 396 /* 397 * Now clear out the unused space, keeping track if we actually 398 * changed anything. 399 */ 400 for (cp = &dp->d_name[namlen + 1]; unused > 0; unused--, cp++) { 401 if (*cp != '\0') { 402 *cp = '\0'; 403 modified = 1; 404 } 405 } 406 407 if (modified) 408 dirty(bp); 409 } 410 return (1); 411 412 bad: 413 if (debug) 414 printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n", 415 dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_type, 416 dp->d_name); 417 return (0); 418 } 419 420 void 421 direrror(ino_t ino, const char *errmesg) 422 { 423 424 fileerror(ino, ino, errmesg); 425 } 426 427 void 428 fileerror(ino_t cwd, ino_t ino, const char *errmesg) 429 { 430 struct inode ip; 431 union dinode *dp; 432 char pathbuf[MAXPATHLEN + 1]; 433 434 pwarn("%s ", errmesg); 435 if (ino < UFS_ROOTINO || ino >= maxino) { 436 pfatal("out-of-range inode number %ju", (uintmax_t)ino); 437 return; 438 } 439 ginode(ino, &ip); 440 dp = ip.i_dp; 441 prtinode(&ip); 442 printf("\n"); 443 getpathname(pathbuf, cwd, ino); 444 if (ftypeok(dp)) 445 pfatal("%s=%s\n", 446 (DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE", 447 pathbuf); 448 else 449 pfatal("NAME=%s\n", pathbuf); 450 irelse(&ip); 451 } 452 453 void 454 adjust(struct inodesc *idesc, int lcnt) 455 { 456 struct inode ip; 457 union dinode *dp; 458 int saveresolved; 459 460 ginode(idesc->id_number, &ip); 461 dp = ip.i_dp; 462 if (DIP(dp, di_nlink) == lcnt) { 463 /* 464 * If we have not hit any unresolved problems, are running 465 * in preen mode, and are on a file system using soft updates, 466 * then just toss any partially allocated files. 467 */ 468 if (resolved && (preen || bkgrdflag) && usedsoftdep) { 469 clri(idesc, "UNREF", 1); 470 irelse(&ip); 471 return; 472 } else { 473 /* 474 * The file system can be marked clean even if 475 * a file is not linked up, but is cleared. 476 * Hence, resolved should not be cleared when 477 * linkup is answered no, but clri is answered yes. 478 */ 479 saveresolved = resolved; 480 if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) { 481 resolved = saveresolved; 482 clri(idesc, "UNREF", 0); 483 irelse(&ip); 484 return; 485 } 486 /* 487 * Account for the new reference created by linkup(). 488 */ 489 lcnt--; 490 } 491 } 492 if (lcnt != 0) { 493 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 494 ((DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE")); 495 prtinode(&ip); 496 printf(" COUNT %d SHOULD BE %d", 497 DIP(dp, di_nlink), DIP(dp, di_nlink) - lcnt); 498 if (preen || usedsoftdep) { 499 if (lcnt < 0) { 500 printf("\n"); 501 pfatal("LINK COUNT INCREASING"); 502 } 503 if (preen) 504 printf(" (ADJUSTED)\n"); 505 } 506 if (preen || reply("ADJUST") == 1) { 507 if (bkgrdflag == 0) { 508 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - lcnt); 509 inodirty(&ip); 510 } else { 511 cmd.value = idesc->id_number; 512 cmd.size = -lcnt; 513 if (debug) 514 printf("adjrefcnt ino %ld amt %lld\n", 515 (long)cmd.value, 516 (long long)cmd.size); 517 if (sysctl(adjrefcnt, MIBSIZE, 0, 0, 518 &cmd, sizeof cmd) == -1) 519 rwerror("ADJUST INODE LINK COUNT", 520 cmd.value); 521 } 522 } 523 } 524 irelse(&ip); 525 } 526 527 static int 528 mkentry(struct inodesc *idesc) 529 { 530 struct direct *dirp = idesc->id_dirp; 531 struct direct newent; 532 int newlen, oldlen; 533 534 newent.d_namlen = strlen(idesc->id_name); 535 newlen = DIRSIZ(0, &newent); 536 if (dirp->d_ino != 0) 537 oldlen = DIRSIZ(0, dirp); 538 else 539 oldlen = 0; 540 if (dirp->d_reclen - oldlen < newlen) 541 return (KEEPON); 542 newent.d_reclen = dirp->d_reclen - oldlen; 543 dirp->d_reclen = oldlen; 544 dirp = (struct direct *)(((char *)dirp) + oldlen); 545 dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ 546 dirp->d_reclen = newent.d_reclen; 547 dirp->d_type = inoinfo(idesc->id_parent)->ino_type; 548 dirp->d_namlen = newent.d_namlen; 549 memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1); 550 return (ALTERED|STOP); 551 } 552 553 static int 554 chgino(struct inodesc *idesc) 555 { 556 struct direct *dirp = idesc->id_dirp; 557 558 if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) 559 return (KEEPON); 560 dirp->d_ino = idesc->id_parent; 561 dirp->d_type = inoinfo(idesc->id_parent)->ino_type; 562 return (ALTERED|STOP); 563 } 564 565 int 566 linkup(ino_t orphan, ino_t parentdir, char *name) 567 { 568 struct inode ip; 569 union dinode *dp; 570 int lostdir, depth; 571 ino_t oldlfdir; 572 struct inoinfo *inp; 573 struct inodesc idesc; 574 char tempname[BUFSIZ]; 575 576 memset(&idesc, 0, sizeof(struct inodesc)); 577 ginode(orphan, &ip); 578 dp = ip.i_dp; 579 lostdir = (DIP(dp, di_mode) & IFMT) == IFDIR; 580 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 581 prtinode(&ip); 582 printf("\n"); 583 if (preen && DIP(dp, di_size) == 0) { 584 irelse(&ip); 585 return (0); 586 } 587 irelse(&ip); 588 if (cursnapshot != 0) { 589 pfatal("FILE LINKUP IN SNAPSHOT"); 590 return (0); 591 } 592 if (preen) 593 printf(" (RECONNECTED)\n"); 594 else if (reply("RECONNECT") == 0) 595 return (0); 596 if (lfdir == 0) { 597 ginode(UFS_ROOTINO, &ip); 598 idesc.id_name = strdup(lfname); 599 idesc.id_type = DATA; 600 idesc.id_func = findino; 601 idesc.id_number = UFS_ROOTINO; 602 if ((ckinode(ip.i_dp, &idesc) & FOUND) != 0) { 603 lfdir = idesc.id_parent; 604 } else { 605 pwarn("NO lost+found DIRECTORY"); 606 if (preen || reply("CREATE")) { 607 lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode); 608 if (lfdir != 0) { 609 if (makeentry(UFS_ROOTINO, lfdir, 610 lfname) != 0) { 611 numdirs++; 612 if (preen) 613 printf(" (CREATED)\n"); 614 } else { 615 freedirino(lfdir, UFS_ROOTINO); 616 lfdir = 0; 617 if (preen) 618 printf("\n"); 619 } 620 } 621 } 622 } 623 irelse(&ip); 624 free(idesc.id_name); 625 if (lfdir == 0) { 626 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 627 printf("\n\n"); 628 return (0); 629 } 630 } 631 ginode(lfdir, &ip); 632 dp = ip.i_dp; 633 if ((DIP(dp, di_mode) & IFMT) != IFDIR) { 634 pfatal("lost+found IS NOT A DIRECTORY"); 635 if (reply("REALLOCATE") == 0) { 636 irelse(&ip); 637 return (0); 638 } 639 oldlfdir = lfdir; 640 if ((lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode)) == 0) { 641 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 642 irelse(&ip); 643 return (0); 644 } 645 if ((changeino(UFS_ROOTINO, lfname, lfdir, 1) & ALTERED) == 0) { 646 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 647 irelse(&ip); 648 return (0); 649 } 650 idesc.id_type = inoinfo(oldlfdir)->ino_idtype; 651 idesc.id_func = freeblock; 652 idesc.id_number = oldlfdir; 653 adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1); 654 inoinfo(oldlfdir)->ino_linkcnt = 0; 655 inodirty(&ip); 656 irelse(&ip); 657 ginode(lfdir, &ip); 658 dp = ip.i_dp; 659 } 660 if (inoinfo(lfdir)->ino_state != DFOUND) { 661 pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 662 irelse(&ip); 663 return (0); 664 } 665 (void)lftempname(tempname, orphan); 666 if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) { 667 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 668 printf("\n\n"); 669 irelse(&ip); 670 return (0); 671 } 672 inoinfo(orphan)->ino_linkcnt--; 673 if (lostdir) { 674 depth = DIP(dp, di_dirdepth) + 1; 675 if ((changeino(orphan, "..", lfdir, depth) & ALTERED) == 0 && 676 parentdir != (ino_t)-1) 677 (void)makeentry(orphan, lfdir, ".."); 678 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 679 inodirty(&ip); 680 inoinfo(lfdir)->ino_linkcnt++; 681 pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan); 682 inp = getinoinfo(parentdir); 683 if (parentdir != (ino_t)-1 && inp != NULL) { 684 printf("PARENT WAS I=%lu\n", (u_long)parentdir); 685 /* 686 * If the parent directory did not have to 687 * be replaced then because of the ordering 688 * guarantees, has had the link count incremented 689 * for the child, but no entry was made. This 690 * fixes the parent link count so that fsck does 691 * not need to be rerun. 692 */ 693 if ((inp->i_flags & INFO_NEW) != 0) 694 inoinfo(parentdir)->ino_linkcnt++; 695 } 696 if (preen == 0) 697 printf("\n"); 698 } 699 irelse(&ip); 700 return (1); 701 } 702 703 /* 704 * fix an entry in a directory. 705 */ 706 int 707 changeino(ino_t dir, const char *name, ino_t newnum, int depth) 708 { 709 struct inodesc idesc; 710 struct inode ip; 711 int error; 712 713 memset(&idesc, 0, sizeof(struct inodesc)); 714 idesc.id_type = DATA; 715 idesc.id_func = chgino; 716 idesc.id_number = dir; 717 idesc.id_fix = DONTKNOW; 718 idesc.id_name = strdup(name); 719 idesc.id_parent = newnum; /* new value for name */ 720 ginode(dir, &ip); 721 if (((error = ckinode(ip.i_dp, &idesc)) & ALTERED) && newnum != 0) { 722 DIP_SET(ip.i_dp, di_dirdepth, depth); 723 inodirty(&ip); 724 getinoinfo(dir)->i_depth = depth; 725 } 726 free(idesc.id_name); 727 irelse(&ip); 728 return (error); 729 } 730 731 /* 732 * make an entry in a directory 733 */ 734 int 735 makeentry(ino_t parent, ino_t ino, const char *name) 736 { 737 struct inode ip; 738 union dinode *dp; 739 struct inodesc idesc; 740 int retval; 741 char pathbuf[MAXPATHLEN + 1]; 742 743 if (parent < UFS_ROOTINO || parent >= maxino || 744 ino < UFS_ROOTINO || ino >= maxino) 745 return (0); 746 memset(&idesc, 0, sizeof(struct inodesc)); 747 idesc.id_type = DATA; 748 idesc.id_func = mkentry; 749 idesc.id_number = parent; 750 idesc.id_parent = ino; /* this is the inode to enter */ 751 idesc.id_fix = DONTKNOW; 752 idesc.id_name = strdup(name); 753 ginode(parent, &ip); 754 dp = ip.i_dp; 755 if (DIP(dp, di_size) % DIRBLKSIZ) { 756 DIP_SET(dp, di_size, roundup(DIP(dp, di_size), DIRBLKSIZ)); 757 inodirty(&ip); 758 } 759 if ((ckinode(dp, &idesc) & ALTERED) != 0) { 760 irelse(&ip); 761 free(idesc.id_name); 762 return (1); 763 } 764 getpathname(pathbuf, parent, parent); 765 if (expanddir(&ip, pathbuf) == 0) { 766 irelse(&ip); 767 free(idesc.id_name); 768 return (0); 769 } 770 retval = ckinode(dp, &idesc) & ALTERED; 771 irelse(&ip); 772 free(idesc.id_name); 773 return (retval); 774 } 775 776 /* 777 * Attempt to expand the size of a directory 778 */ 779 static int 780 expanddir(struct inode *ip, char *name) 781 { 782 ufs2_daddr_t lastlbn, oldblk, newblk, indirblk; 783 size_t filesize, lastlbnsize; 784 struct bufarea *bp, *nbp; 785 struct inodesc idesc; 786 union dinode *dp; 787 long cg, indiralloced; 788 char *cp; 789 790 nbp = NULL; 791 indiralloced = newblk = indirblk = 0; 792 memset(&idesc, 0, sizeof(struct inodesc)); 793 idesc.id_type = ADDR; 794 pwarn("NO SPACE LEFT IN %s", name); 795 if (!preen && reply("EXPAND") == 0) 796 return (0); 797 cg = ino_to_cg(&sblock, ip->i_number); 798 dp = ip->i_dp; 799 filesize = DIP(dp, di_size); 800 lastlbn = lblkno(&sblock, filesize); 801 /* 802 * We only expand lost+found to a single indirect block. 803 */ 804 if ((DIP(dp, di_mode) & IFMT) != IFDIR || filesize == 0 || 805 lastlbn >= UFS_NDADDR + NINDIR(&sblock)) 806 goto bad; 807 /* 808 * If last block is a fragment, expand it to a full size block. 809 */ 810 lastlbnsize = sblksize(&sblock, filesize, lastlbn); 811 if (lastlbnsize > 0 && lastlbnsize < sblock.fs_bsize) { 812 oldblk = DIP(dp, di_db[lastlbn]); 813 bp = getdirblk(oldblk, lastlbnsize); 814 if (bp->b_errs) 815 goto bad; 816 newblk = allocblk(cg, sblock.fs_frag, std_checkblkavail); 817 if (newblk == 0) 818 goto bad; 819 nbp = getdatablk(newblk, sblock.fs_bsize, BT_DIRDATA); 820 if (nbp->b_errs) 821 goto bad; 822 DIP_SET(dp, di_db[lastlbn], newblk); 823 DIP_SET(dp, di_size, filesize + sblock.fs_bsize - lastlbnsize); 824 DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + 825 btodb(sblock.fs_bsize - lastlbnsize)); 826 inodirty(ip); 827 memmove(nbp->b_un.b_buf, bp->b_un.b_buf, lastlbnsize); 828 memset(&nbp->b_un.b_buf[lastlbnsize], 0, 829 sblock.fs_bsize - lastlbnsize); 830 for (cp = &nbp->b_un.b_buf[lastlbnsize]; 831 cp < &nbp->b_un.b_buf[sblock.fs_bsize]; 832 cp += DIRBLKSIZ) 833 memmove(cp, &emptydir, sizeof emptydir); 834 dirty(nbp); 835 brelse(nbp); 836 binval(bp); 837 idesc.id_blkno = oldblk; 838 idesc.id_numfrags = numfrags(&sblock, lastlbnsize); 839 (void)freeblock(&idesc); 840 if (preen) 841 printf(" (EXPANDED)\n"); 842 return (1); 843 } 844 if ((newblk = allocblk(cg, sblock.fs_frag, std_checkblkavail)) == 0) 845 goto bad; 846 bp = getdirblk(newblk, sblock.fs_bsize); 847 if (bp->b_errs) 848 goto bad; 849 memset(bp->b_un.b_buf, 0, sblock.fs_bsize); 850 for (cp = bp->b_un.b_buf; 851 cp < &bp->b_un.b_buf[sblock.fs_bsize]; 852 cp += DIRBLKSIZ) 853 memmove(cp, &emptydir, sizeof emptydir); 854 dirty(bp); 855 if (lastlbn < UFS_NDADDR) { 856 DIP_SET(dp, di_db[lastlbn], newblk); 857 } else { 858 /* 859 * Allocate indirect block if needed. 860 */ 861 if ((indirblk = DIP(dp, di_ib[0])) == 0) { 862 indirblk = allocblk(cg, sblock.fs_frag, 863 std_checkblkavail); 864 if (indirblk == 0) { 865 binval(bp); 866 goto bad; 867 } 868 indiralloced = 1; 869 } 870 nbp = getdatablk(indirblk, sblock.fs_bsize, BT_LEVEL1); 871 if (nbp->b_errs) 872 goto bad; 873 if (indiralloced) { 874 memset(nbp->b_un.b_buf, 0, sblock.fs_bsize); 875 DIP_SET(dp, di_ib[0], indirblk); 876 DIP_SET(dp, di_blocks, 877 DIP(dp, di_blocks) + btodb(sblock.fs_bsize)); 878 inodirty(ip); 879 } 880 IBLK_SET(nbp, lastlbn - UFS_NDADDR, newblk); 881 dirty(nbp); 882 brelse(nbp); 883 } 884 DIP_SET(dp, di_size, filesize + sblock.fs_bsize); 885 DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize)); 886 inodirty(ip); 887 if (preen) 888 printf(" (EXPANDED)\n"); 889 return (1); 890 bad: 891 pfatal(" (EXPANSION FAILED)\n"); 892 if (nbp != NULL) { 893 binval(bp); 894 brelse(nbp); 895 } 896 if (newblk != 0) { 897 idesc.id_blkno = newblk; 898 idesc.id_numfrags = sblock.fs_frag; 899 (void)freeblock(&idesc); 900 } 901 if (indiralloced) { 902 idesc.id_blkno = indirblk; 903 idesc.id_numfrags = sblock.fs_frag; 904 (void)freeblock(&idesc); 905 } 906 return (0); 907 } 908 909 /* 910 * allocate a new directory 911 */ 912 ino_t 913 allocdir(ino_t parent, ino_t request, int mode) 914 { 915 ino_t ino; 916 char *cp; 917 struct inode ip; 918 union dinode *dp; 919 struct bufarea *bp; 920 struct dirtemplate *dirp; 921 struct inoinfo *inp, *parentinp; 922 923 ino = allocino(request, IFDIR|mode); 924 if (ino == 0) 925 return (0); 926 dirp = &dirhead; 927 dirp->dot_ino = ino; 928 dirp->dotdot_ino = parent; 929 ginode(ino, &ip); 930 dp = ip.i_dp; 931 bp = getdirblk(DIP(dp, di_db[0]), sblock.fs_fsize); 932 if (bp->b_errs) { 933 freeino(ino); 934 irelse(&ip); 935 return (0); 936 } 937 memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate)); 938 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 939 cp < &bp->b_un.b_buf[sblock.fs_fsize]; 940 cp += DIRBLKSIZ) 941 memmove(cp, &emptydir, sizeof emptydir); 942 dirty(bp); 943 DIP_SET(dp, di_nlink, 2); 944 inodirty(&ip); 945 if (ino == UFS_ROOTINO) { 946 inp = cacheino(dp, ino); 947 inp->i_parent = parent; 948 inp->i_dotdot = parent; 949 inp->i_flags |= INFO_NEW; 950 inoinfo(ino)->ino_type = DT_DIR; 951 inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 952 irelse(&ip); 953 return(ino); 954 } 955 if (!INO_IS_DVALID(parent)) { 956 freeino(ino); 957 irelse(&ip); 958 return (0); 959 } 960 inp = cacheino(dp, ino); 961 inp->i_parent = parent; 962 inp->i_dotdot = parent; 963 inp->i_flags |= INFO_NEW; 964 if ((parentinp = getinoinfo(inp->i_parent)) == NULL) { 965 pfatal("allocdir: UNKNOWN PARENT DIR"); 966 } else { 967 inp->i_depth = parentinp->i_depth + 1; 968 DIP_SET(dp, di_dirdepth, inp->i_depth); 969 inodirty(&ip); 970 } 971 inoinfo(ino)->ino_type = DT_DIR; 972 inoinfo(ino)->ino_state = inoinfo(parent)->ino_state; 973 if (inoinfo(ino)->ino_state == DSTATE) { 974 inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 975 inoinfo(parent)->ino_linkcnt++; 976 } 977 irelse(&ip); 978 ginode(parent, &ip); 979 dp = ip.i_dp; 980 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 981 inodirty(&ip); 982 irelse(&ip); 983 return (ino); 984 } 985 986 /* 987 * free a directory inode 988 */ 989 void 990 freedirino(ino_t ino, ino_t parent) 991 { 992 struct inode ip; 993 union dinode *dp; 994 995 if (ino != parent) { 996 ginode(parent, &ip); 997 dp = ip.i_dp; 998 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - 1); 999 inodirty(&ip); 1000 irelse(&ip); 1001 } 1002 removecachedino(ino); 1003 freeino(ino); 1004 } 1005 1006 /* 1007 * generate a temporary name for the lost+found directory. 1008 */ 1009 static int 1010 lftempname(char *bufp, ino_t ino) 1011 { 1012 ino_t in; 1013 char *cp; 1014 int namlen; 1015 1016 cp = bufp + 2; 1017 for (in = maxino; in > 0; in /= 10) 1018 cp++; 1019 *--cp = 0; 1020 namlen = cp - bufp; 1021 in = ino; 1022 while (cp > bufp) { 1023 *--cp = (in % 10) + '0'; 1024 in /= 10; 1025 } 1026 *cp = '#'; 1027 return (namlen); 1028 } 1029 1030 /* 1031 * Get a directory block. 1032 * Insure that it is held until another is requested. 1033 */ 1034 static struct bufarea * 1035 getdirblk(ufs2_daddr_t blkno, long size) 1036 { 1037 1038 if (pdirbp != NULL && pdirbp->b_errs == 0) 1039 brelse(pdirbp); 1040 pdirbp = getdatablk(blkno, size, BT_DIRDATA); 1041 return (pdirbp); 1042 } 1043