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 inodirty(&ip); 729 getinoinfo(dir)->i_depth = depth; 730 } 731 free(idesc.id_name); 732 irelse(&ip); 733 return (error); 734 } 735 736 /* 737 * make an entry in a directory 738 */ 739 int 740 makeentry(ino_t parent, ino_t ino, const char *name) 741 { 742 struct inode ip; 743 union dinode *dp; 744 struct inodesc idesc; 745 int retval; 746 char pathbuf[MAXPATHLEN + 1]; 747 748 if (parent < UFS_ROOTINO || parent >= maxino || 749 ino < UFS_ROOTINO || ino >= maxino) 750 return (0); 751 memset(&idesc, 0, sizeof(struct inodesc)); 752 idesc.id_type = DATA; 753 idesc.id_func = mkentry; 754 idesc.id_number = parent; 755 idesc.id_parent = ino; /* this is the inode to enter */ 756 idesc.id_fix = DONTKNOW; 757 idesc.id_name = strdup(name); 758 ginode(parent, &ip); 759 dp = ip.i_dp; 760 if (DIP(dp, di_size) % DIRBLKSIZ) { 761 DIP_SET(dp, di_size, roundup(DIP(dp, di_size), DIRBLKSIZ)); 762 inodirty(&ip); 763 } 764 if ((ckinode(dp, &idesc) & ALTERED) != 0) { 765 irelse(&ip); 766 free(idesc.id_name); 767 return (1); 768 } 769 getpathname(pathbuf, parent, parent); 770 if (expanddir(&ip, pathbuf) == 0) { 771 irelse(&ip); 772 free(idesc.id_name); 773 return (0); 774 } 775 retval = ckinode(dp, &idesc) & ALTERED; 776 irelse(&ip); 777 free(idesc.id_name); 778 return (retval); 779 } 780 781 /* 782 * Attempt to expand the size of a directory 783 */ 784 static int 785 expanddir(struct inode *ip, char *name) 786 { 787 ufs2_daddr_t lastlbn, oldblk, newblk, indirblk; 788 size_t filesize, lastlbnsize; 789 struct bufarea *bp, *nbp; 790 struct inodesc idesc; 791 union dinode *dp; 792 long cg, indiralloced; 793 char *cp; 794 795 nbp = NULL; 796 indiralloced = newblk = indirblk = 0; 797 memset(&idesc, 0, sizeof(struct inodesc)); 798 idesc.id_type = ADDR; 799 pwarn("NO SPACE LEFT IN %s", name); 800 if (!preen && reply("EXPAND") == 0) 801 return (0); 802 cg = ino_to_cg(&sblock, ip->i_number); 803 dp = ip->i_dp; 804 filesize = DIP(dp, di_size); 805 lastlbn = lblkno(&sblock, filesize); 806 /* 807 * We only expand lost+found to a single indirect block. 808 */ 809 if ((DIP(dp, di_mode) & IFMT) != IFDIR || filesize == 0 || 810 lastlbn >= UFS_NDADDR + NINDIR(&sblock)) 811 goto bad; 812 /* 813 * If last block is a fragment, expand it to a full size block. 814 */ 815 lastlbnsize = sblksize(&sblock, filesize, lastlbn); 816 if (lastlbnsize > 0 && lastlbnsize < sblock.fs_bsize) { 817 oldblk = DIP(dp, di_db[lastlbn]); 818 bp = getdirblk(oldblk, lastlbnsize); 819 if (bp->b_errs) 820 goto bad; 821 newblk = allocblk(cg, sblock.fs_frag, std_checkblkavail); 822 if (newblk == 0) 823 goto bad; 824 nbp = getdatablk(newblk, sblock.fs_bsize, BT_DIRDATA); 825 if (nbp->b_errs) 826 goto bad; 827 DIP_SET(dp, di_db[lastlbn], newblk); 828 DIP_SET(dp, di_size, filesize + sblock.fs_bsize - lastlbnsize); 829 DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + 830 btodb(sblock.fs_bsize - lastlbnsize)); 831 inodirty(ip); 832 memmove(nbp->b_un.b_buf, bp->b_un.b_buf, lastlbnsize); 833 memset(&nbp->b_un.b_buf[lastlbnsize], 0, 834 sblock.fs_bsize - lastlbnsize); 835 for (cp = &nbp->b_un.b_buf[lastlbnsize]; 836 cp < &nbp->b_un.b_buf[sblock.fs_bsize]; 837 cp += DIRBLKSIZ) 838 memmove(cp, &emptydir, sizeof emptydir); 839 dirty(nbp); 840 brelse(nbp); 841 binval(bp); 842 idesc.id_blkno = oldblk; 843 idesc.id_numfrags = numfrags(&sblock, lastlbnsize); 844 (void)freeblock(&idesc); 845 if (preen) 846 printf(" (EXPANDED)\n"); 847 return (1); 848 } 849 if ((newblk = allocblk(cg, sblock.fs_frag, std_checkblkavail)) == 0) 850 goto bad; 851 bp = getdirblk(newblk, sblock.fs_bsize); 852 if (bp->b_errs) 853 goto bad; 854 memset(bp->b_un.b_buf, 0, sblock.fs_bsize); 855 for (cp = bp->b_un.b_buf; 856 cp < &bp->b_un.b_buf[sblock.fs_bsize]; 857 cp += DIRBLKSIZ) 858 memmove(cp, &emptydir, sizeof emptydir); 859 dirty(bp); 860 if (lastlbn < UFS_NDADDR) { 861 DIP_SET(dp, di_db[lastlbn], newblk); 862 } else { 863 /* 864 * Allocate indirect block if needed. 865 */ 866 if ((indirblk = DIP(dp, di_ib[0])) == 0) { 867 indirblk = allocblk(cg, sblock.fs_frag, 868 std_checkblkavail); 869 if (indirblk == 0) { 870 binval(bp); 871 goto bad; 872 } 873 indiralloced = 1; 874 } 875 nbp = getdatablk(indirblk, sblock.fs_bsize, BT_LEVEL1); 876 if (nbp->b_errs) 877 goto bad; 878 if (indiralloced) { 879 memset(nbp->b_un.b_buf, 0, sblock.fs_bsize); 880 DIP_SET(dp, di_ib[0], indirblk); 881 DIP_SET(dp, di_blocks, 882 DIP(dp, di_blocks) + btodb(sblock.fs_bsize)); 883 inodirty(ip); 884 } 885 IBLK_SET(nbp, lastlbn - UFS_NDADDR, newblk); 886 dirty(nbp); 887 brelse(nbp); 888 } 889 DIP_SET(dp, di_size, filesize + sblock.fs_bsize); 890 DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize)); 891 inodirty(ip); 892 if (preen) 893 printf(" (EXPANDED)\n"); 894 return (1); 895 bad: 896 pfatal(" (EXPANSION FAILED)\n"); 897 if (nbp != NULL) { 898 binval(bp); 899 brelse(nbp); 900 } 901 if (newblk != 0) { 902 idesc.id_blkno = newblk; 903 idesc.id_numfrags = sblock.fs_frag; 904 (void)freeblock(&idesc); 905 } 906 if (indiralloced) { 907 idesc.id_blkno = indirblk; 908 idesc.id_numfrags = sblock.fs_frag; 909 (void)freeblock(&idesc); 910 } 911 return (0); 912 } 913 914 /* 915 * allocate a new directory 916 */ 917 ino_t 918 allocdir(ino_t parent, ino_t request, int mode) 919 { 920 ino_t ino; 921 char *cp; 922 struct inode ip; 923 union dinode *dp; 924 struct bufarea *bp; 925 struct dirtemplate *dirp; 926 struct inoinfo *inp, *parentinp; 927 928 ino = allocino(request, IFDIR|mode); 929 if (ino == 0) 930 return (0); 931 dirp = &dirhead; 932 dirp->dot_ino = ino; 933 dirp->dotdot_ino = parent; 934 ginode(ino, &ip); 935 dp = ip.i_dp; 936 bp = getdirblk(DIP(dp, di_db[0]), sblock.fs_fsize); 937 if (bp->b_errs) { 938 freeino(ino); 939 irelse(&ip); 940 return (0); 941 } 942 memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate)); 943 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 944 cp < &bp->b_un.b_buf[sblock.fs_fsize]; 945 cp += DIRBLKSIZ) 946 memmove(cp, &emptydir, sizeof emptydir); 947 dirty(bp); 948 DIP_SET(dp, di_nlink, 2); 949 inodirty(&ip); 950 if (ino == UFS_ROOTINO) { 951 inp = cacheino(dp, ino); 952 inp->i_parent = parent; 953 inp->i_dotdot = parent; 954 inp->i_flags |= INFO_NEW; 955 inoinfo(ino)->ino_type = DT_DIR; 956 inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 957 irelse(&ip); 958 return(ino); 959 } 960 if (!INO_IS_DVALID(parent)) { 961 freeino(ino); 962 irelse(&ip); 963 return (0); 964 } 965 inp = cacheino(dp, ino); 966 inp->i_parent = parent; 967 inp->i_dotdot = parent; 968 inp->i_flags |= INFO_NEW; 969 if ((parentinp = getinoinfo(inp->i_parent)) == NULL) { 970 pfatal("allocdir: UNKNOWN PARENT DIR"); 971 } else { 972 inp->i_depth = parentinp->i_depth + 1; 973 DIP_SET(dp, di_dirdepth, inp->i_depth); 974 inodirty(&ip); 975 } 976 inoinfo(ino)->ino_type = DT_DIR; 977 inoinfo(ino)->ino_state = inoinfo(parent)->ino_state; 978 if (inoinfo(ino)->ino_state == DSTATE) { 979 inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 980 inoinfo(parent)->ino_linkcnt++; 981 } 982 irelse(&ip); 983 ginode(parent, &ip); 984 dp = ip.i_dp; 985 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 986 inodirty(&ip); 987 irelse(&ip); 988 return (ino); 989 } 990 991 /* 992 * free a directory inode 993 */ 994 void 995 freedirino(ino_t ino, ino_t parent) 996 { 997 struct inode ip; 998 union dinode *dp; 999 1000 if (ino != parent) { 1001 ginode(parent, &ip); 1002 dp = ip.i_dp; 1003 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - 1); 1004 inodirty(&ip); 1005 irelse(&ip); 1006 } 1007 removecachedino(ino); 1008 freeino(ino); 1009 } 1010 1011 /* 1012 * generate a temporary name for the lost+found directory. 1013 */ 1014 static int 1015 lftempname(char *bufp, ino_t ino) 1016 { 1017 ino_t in; 1018 char *cp; 1019 int namlen; 1020 1021 cp = bufp + 2; 1022 for (in = maxino; in > 0; in /= 10) 1023 cp++; 1024 *--cp = 0; 1025 namlen = cp - bufp; 1026 in = ino; 1027 while (cp > bufp) { 1028 *--cp = (in % 10) + '0'; 1029 in /= 10; 1030 } 1031 *cp = '#'; 1032 return (namlen); 1033 } 1034 1035 /* 1036 * Get a directory block. 1037 * Insure that it is held until another is requested. 1038 */ 1039 static struct bufarea * 1040 getdirblk(ufs2_daddr_t blkno, long size) 1041 { 1042 1043 if (pdirbp != NULL && pdirbp->b_errs == 0) 1044 brelse(pdirbp); 1045 pdirbp = getdatablk(blkno, size, BT_DIRDATA); 1046 return (pdirbp); 1047 } 1048