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