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