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