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