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