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 direct *); 65 static int expanddir(union dinode *dp, 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 memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf, 130 (size_t)dsize); 131 dirty(bp); 132 sbdirty(); 133 rerun = 1; 134 } 135 if (n & STOP) 136 return (n); 137 } 138 return (idesc->id_filesize > 0 ? KEEPON : STOP); 139 } 140 141 /* 142 * get next entry in a directory. 143 */ 144 static struct direct * 145 fsck_readdir(struct inodesc *idesc) 146 { 147 struct direct *dp, *ndp; 148 struct bufarea *bp; 149 long size, blksiz, fix, dploc; 150 int dc; 151 152 blksiz = idesc->id_numfrags * sblock.fs_fsize; 153 bp = getdirblk(idesc->id_blkno, blksiz); 154 if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 && 155 idesc->id_loc < blksiz) { 156 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 157 if ((dc = dircheck(idesc, dp)) > 0) { 158 if (dc == 2) { 159 /* 160 * dircheck() cleared unused directory space. 161 * Mark the buffer as dirty to write it out. 162 */ 163 dirty(bp); 164 } 165 goto dpok; 166 } 167 if (idesc->id_fix == IGNORE) 168 return (0); 169 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 170 bp = getdirblk(idesc->id_blkno, blksiz); 171 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 172 dp->d_reclen = DIRBLKSIZ; 173 dp->d_ino = 0; 174 dp->d_type = 0; 175 dp->d_namlen = 0; 176 dp->d_name[0] = '\0'; 177 if (fix) 178 dirty(bp); 179 idesc->id_loc += DIRBLKSIZ; 180 idesc->id_filesize -= DIRBLKSIZ; 181 return (dp); 182 } 183 dpok: 184 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 185 return NULL; 186 dploc = idesc->id_loc; 187 dp = (struct direct *)(bp->b_un.b_buf + dploc); 188 idesc->id_loc += dp->d_reclen; 189 idesc->id_filesize -= dp->d_reclen; 190 if ((idesc->id_loc % DIRBLKSIZ) == 0) 191 return (dp); 192 ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 193 if (idesc->id_loc < blksiz && idesc->id_filesize > 0) { 194 if ((dc = dircheck(idesc, ndp)) == 0) { 195 size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 196 idesc->id_loc += size; 197 idesc->id_filesize -= size; 198 if (idesc->id_fix == IGNORE) 199 return (0); 200 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 201 bp = getdirblk(idesc->id_blkno, blksiz); 202 dp = (struct direct *)(bp->b_un.b_buf + dploc); 203 dp->d_reclen += size; 204 if (fix) 205 dirty(bp); 206 } else if (dc == 2) { 207 /* 208 * dircheck() cleared unused directory space. 209 * Mark the buffer as dirty to write it out. 210 */ 211 dirty(bp); 212 } 213 } 214 return (dp); 215 } 216 217 /* 218 * Verify that a directory entry is valid. 219 * This is a superset of the checks made in the kernel. 220 * Also optionally clears padding and unused directory space. 221 * 222 * Returns 0 if the entry is bad, 1 if the entry is good and no changes 223 * were made, and 2 if the entry is good but modified to clear out padding 224 * and unused space and needs to be written back to disk. 225 */ 226 static int 227 dircheck(struct inodesc *idesc, struct direct *dp) 228 { 229 size_t size; 230 char *cp; 231 u_char type; 232 u_int8_t namlen; 233 int spaceleft, modified, unused; 234 235 modified = 0; 236 spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 237 if (dp->d_reclen == 0 || 238 dp->d_reclen > spaceleft || 239 (dp->d_reclen & (DIR_ROUNDUP - 1)) != 0) 240 goto bad; 241 if (dp->d_ino == 0) { 242 /* 243 * Special case of an unused directory entry. Normally 244 * the kernel would coalesce unused space with the previous 245 * entry by extending its d_reclen, but there are situations 246 * (e.g. fsck) where that doesn't occur. 247 * If we're clearing out directory cruft (-z flag), then make 248 * sure this entry gets fully cleared as well. 249 */ 250 if (zflag && fswritefd >= 0) { 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 if (dp->d_name[0] != '\0') { 260 dp->d_name[0] = '\0'; 261 modified = 1; 262 } 263 } 264 goto good; 265 } 266 size = DIRSIZ(0, dp); 267 namlen = dp->d_namlen; 268 type = dp->d_type; 269 if (dp->d_reclen < size || 270 idesc->id_filesize < size || 271 namlen == 0 || 272 type > 15) 273 goto bad; 274 for (cp = dp->d_name, size = 0; size < namlen; size++) 275 if (*cp == '\0' || (*cp++ == '/')) 276 goto bad; 277 if (*cp != '\0') 278 goto bad; 279 280 good: 281 if (zflag && fswritefd >= 0) { 282 /* 283 * Clear unused directory entry space, including the d_name 284 * padding. 285 */ 286 /* First figure the number of pad bytes. */ 287 unused = roundup2(namlen + 1, DIR_ROUNDUP) - (namlen + 1); 288 289 /* Add in the free space to the end of the record. */ 290 unused += dp->d_reclen - DIRSIZ(0, dp); 291 292 /* 293 * Now clear out the unused space, keeping track if we actually 294 * changed anything. 295 */ 296 for (cp = &dp->d_name[namlen + 1]; unused > 0; unused--, cp++) { 297 if (*cp != '\0') { 298 *cp = '\0'; 299 modified = 1; 300 } 301 } 302 303 if (modified) { 304 return 2; 305 } 306 } 307 308 return (1); 309 310 bad: 311 if (debug) 312 printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n", 313 dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_type, 314 dp->d_name); 315 return (0); 316 } 317 318 void 319 direrror(ino_t ino, const char *errmesg) 320 { 321 322 fileerror(ino, ino, errmesg); 323 } 324 325 void 326 fileerror(ino_t cwd, ino_t ino, const char *errmesg) 327 { 328 union dinode *dp; 329 char pathbuf[MAXPATHLEN + 1]; 330 331 pwarn("%s ", errmesg); 332 if (ino < UFS_ROOTINO || ino > maxino) { 333 pfatal("out-of-range inode number %ju", (uintmax_t)ino); 334 return; 335 } 336 dp = ginode(ino); 337 prtinode(ino, dp); 338 printf("\n"); 339 getpathname(pathbuf, cwd, ino); 340 if (ftypeok(dp)) 341 pfatal("%s=%s\n", 342 (DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE", 343 pathbuf); 344 else 345 pfatal("NAME=%s\n", pathbuf); 346 } 347 348 void 349 adjust(struct inodesc *idesc, int lcnt) 350 { 351 union dinode *dp; 352 int saveresolved; 353 354 dp = ginode(idesc->id_number); 355 if (DIP(dp, di_nlink) == lcnt) { 356 /* 357 * If we have not hit any unresolved problems, are running 358 * in preen mode, and are on a file system using soft updates, 359 * then just toss any partially allocated files. 360 */ 361 if (resolved && (preen || bkgrdflag) && usedsoftdep) { 362 clri(idesc, "UNREF", 1); 363 return; 364 } else { 365 /* 366 * The file system can be marked clean even if 367 * a file is not linked up, but is cleared. 368 * Hence, resolved should not be cleared when 369 * linkup is answered no, but clri is answered yes. 370 */ 371 saveresolved = resolved; 372 if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) { 373 resolved = saveresolved; 374 clri(idesc, "UNREF", 0); 375 return; 376 } 377 /* 378 * Account for the new reference created by linkup(). 379 */ 380 dp = ginode(idesc->id_number); 381 lcnt--; 382 } 383 } 384 if (lcnt != 0) { 385 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 386 ((DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE")); 387 prtinode(idesc->id_number, dp); 388 printf(" COUNT %d SHOULD BE %d", 389 DIP(dp, di_nlink), DIP(dp, di_nlink) - lcnt); 390 if (preen || usedsoftdep) { 391 if (lcnt < 0) { 392 printf("\n"); 393 pfatal("LINK COUNT INCREASING"); 394 } 395 if (preen) 396 printf(" (ADJUSTED)\n"); 397 } 398 if (preen || reply("ADJUST") == 1) { 399 if (bkgrdflag == 0) { 400 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - lcnt); 401 inodirty(dp); 402 } else { 403 cmd.value = idesc->id_number; 404 cmd.size = -lcnt; 405 if (debug) 406 printf("adjrefcnt ino %ld amt %lld\n", 407 (long)cmd.value, 408 (long long)cmd.size); 409 if (sysctl(adjrefcnt, MIBSIZE, 0, 0, 410 &cmd, sizeof cmd) == -1) 411 rwerror("ADJUST INODE", cmd.value); 412 } 413 } 414 } 415 } 416 417 static int 418 mkentry(struct inodesc *idesc) 419 { 420 struct direct *dirp = idesc->id_dirp; 421 struct direct newent; 422 int newlen, oldlen; 423 424 newent.d_namlen = strlen(idesc->id_name); 425 newlen = DIRSIZ(0, &newent); 426 if (dirp->d_ino != 0) 427 oldlen = DIRSIZ(0, dirp); 428 else 429 oldlen = 0; 430 if (dirp->d_reclen - oldlen < newlen) 431 return (KEEPON); 432 newent.d_reclen = dirp->d_reclen - oldlen; 433 dirp->d_reclen = oldlen; 434 dirp = (struct direct *)(((char *)dirp) + oldlen); 435 dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ 436 dirp->d_reclen = newent.d_reclen; 437 dirp->d_type = inoinfo(idesc->id_parent)->ino_type; 438 dirp->d_namlen = newent.d_namlen; 439 memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1); 440 return (ALTERED|STOP); 441 } 442 443 static int 444 chgino(struct inodesc *idesc) 445 { 446 struct direct *dirp = idesc->id_dirp; 447 448 if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) 449 return (KEEPON); 450 dirp->d_ino = idesc->id_parent; 451 dirp->d_type = inoinfo(idesc->id_parent)->ino_type; 452 return (ALTERED|STOP); 453 } 454 455 int 456 linkup(ino_t orphan, ino_t parentdir, char *name) 457 { 458 union dinode *dp; 459 int lostdir; 460 ino_t oldlfdir; 461 struct inodesc idesc; 462 char tempname[BUFSIZ]; 463 464 memset(&idesc, 0, sizeof(struct inodesc)); 465 dp = ginode(orphan); 466 lostdir = (DIP(dp, di_mode) & IFMT) == IFDIR; 467 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 468 prtinode(orphan, dp); 469 printf("\n"); 470 if (preen && DIP(dp, di_size) == 0) 471 return (0); 472 if (cursnapshot != 0) { 473 pfatal("FILE LINKUP IN SNAPSHOT"); 474 return (0); 475 } 476 if (preen) 477 printf(" (RECONNECTED)\n"); 478 else 479 if (reply("RECONNECT") == 0) 480 return (0); 481 if (lfdir == 0) { 482 dp = ginode(UFS_ROOTINO); 483 idesc.id_name = strdup(lfname); 484 idesc.id_type = DATA; 485 idesc.id_func = findino; 486 idesc.id_number = UFS_ROOTINO; 487 if ((ckinode(dp, &idesc) & FOUND) != 0) { 488 lfdir = idesc.id_parent; 489 } else { 490 pwarn("NO lost+found DIRECTORY"); 491 if (preen || reply("CREATE")) { 492 lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode); 493 if (lfdir != 0) { 494 if (makeentry(UFS_ROOTINO, lfdir, 495 lfname) != 0) { 496 numdirs++; 497 if (preen) 498 printf(" (CREATED)\n"); 499 } else { 500 freedir(lfdir, UFS_ROOTINO); 501 lfdir = 0; 502 if (preen) 503 printf("\n"); 504 } 505 } 506 } 507 } 508 if (lfdir == 0) { 509 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 510 printf("\n\n"); 511 return (0); 512 } 513 } 514 dp = ginode(lfdir); 515 if ((DIP(dp, di_mode) & IFMT) != IFDIR) { 516 pfatal("lost+found IS NOT A DIRECTORY"); 517 if (reply("REALLOCATE") == 0) 518 return (0); 519 oldlfdir = lfdir; 520 if ((lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode)) == 0) { 521 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 522 return (0); 523 } 524 if ((changeino(UFS_ROOTINO, lfname, lfdir) & ALTERED) == 0) { 525 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 526 return (0); 527 } 528 inodirty(dp); 529 idesc.id_type = ADDR; 530 idesc.id_func = pass4check; 531 idesc.id_number = oldlfdir; 532 adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1); 533 inoinfo(oldlfdir)->ino_linkcnt = 0; 534 dp = ginode(lfdir); 535 } 536 if (inoinfo(lfdir)->ino_state != DFOUND) { 537 pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 538 return (0); 539 } 540 (void)lftempname(tempname, orphan); 541 if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) { 542 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 543 printf("\n\n"); 544 return (0); 545 } 546 inoinfo(orphan)->ino_linkcnt--; 547 if (lostdir) { 548 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 549 parentdir != (ino_t)-1) 550 (void)makeentry(orphan, lfdir, ".."); 551 dp = ginode(lfdir); 552 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 553 inodirty(dp); 554 inoinfo(lfdir)->ino_linkcnt++; 555 pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan); 556 if (parentdir != (ino_t)-1) { 557 printf("PARENT WAS I=%lu\n", (u_long)parentdir); 558 /* 559 * The parent directory, because of the ordering 560 * guarantees, has had the link count incremented 561 * for the child, but no entry was made. This 562 * fixes the parent link count so that fsck does 563 * not need to be rerun. 564 */ 565 inoinfo(parentdir)->ino_linkcnt++; 566 } 567 if (preen == 0) 568 printf("\n"); 569 } 570 return (1); 571 } 572 573 /* 574 * fix an entry in a directory. 575 */ 576 int 577 changeino(ino_t dir, const char *name, ino_t newnum) 578 { 579 struct inodesc idesc; 580 581 memset(&idesc, 0, sizeof(struct inodesc)); 582 idesc.id_type = DATA; 583 idesc.id_func = chgino; 584 idesc.id_number = dir; 585 idesc.id_fix = DONTKNOW; 586 idesc.id_name = strdup(name); 587 idesc.id_parent = newnum; /* new value for name */ 588 return (ckinode(ginode(dir), &idesc)); 589 } 590 591 /* 592 * make an entry in a directory 593 */ 594 int 595 makeentry(ino_t parent, ino_t ino, const char *name) 596 { 597 union dinode *dp; 598 struct inodesc idesc; 599 char pathbuf[MAXPATHLEN + 1]; 600 601 if (parent < UFS_ROOTINO || parent >= maxino || 602 ino < UFS_ROOTINO || ino >= maxino) 603 return (0); 604 memset(&idesc, 0, sizeof(struct inodesc)); 605 idesc.id_type = DATA; 606 idesc.id_func = mkentry; 607 idesc.id_number = parent; 608 idesc.id_parent = ino; /* this is the inode to enter */ 609 idesc.id_fix = DONTKNOW; 610 idesc.id_name = strdup(name); 611 dp = ginode(parent); 612 if (DIP(dp, di_size) % DIRBLKSIZ) { 613 DIP_SET(dp, di_size, roundup(DIP(dp, di_size), DIRBLKSIZ)); 614 inodirty(dp); 615 } 616 if ((ckinode(dp, &idesc) & ALTERED) != 0) 617 return (1); 618 getpathname(pathbuf, parent, parent); 619 dp = ginode(parent); 620 if (expanddir(dp, pathbuf) == 0) 621 return (0); 622 return (ckinode(dp, &idesc) & ALTERED); 623 } 624 625 /* 626 * Attempt to expand the size of a directory 627 */ 628 static int 629 expanddir(union dinode *dp, char *name) 630 { 631 ufs2_daddr_t lastbn, newblk; 632 struct bufarea *bp; 633 char *cp, firstblk[DIRBLKSIZ]; 634 635 lastbn = lblkno(&sblock, DIP(dp, di_size)); 636 if (lastbn >= UFS_NDADDR - 1 || DIP(dp, di_db[lastbn]) == 0 || 637 DIP(dp, di_size) == 0) 638 return (0); 639 if ((newblk = allocblk(sblock.fs_frag)) == 0) 640 return (0); 641 DIP_SET(dp, di_db[lastbn + 1], DIP(dp, di_db[lastbn])); 642 DIP_SET(dp, di_db[lastbn], newblk); 643 DIP_SET(dp, di_size, DIP(dp, di_size) + sblock.fs_bsize); 644 DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize)); 645 bp = getdirblk(DIP(dp, di_db[lastbn + 1]), 646 sblksize(&sblock, DIP(dp, di_size), lastbn + 1)); 647 if (bp->b_errs) 648 goto bad; 649 memmove(firstblk, bp->b_un.b_buf, DIRBLKSIZ); 650 bp = getdirblk(newblk, sblock.fs_bsize); 651 if (bp->b_errs) 652 goto bad; 653 memmove(bp->b_un.b_buf, firstblk, DIRBLKSIZ); 654 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 655 cp < &bp->b_un.b_buf[sblock.fs_bsize]; 656 cp += DIRBLKSIZ) 657 memmove(cp, &emptydir, sizeof emptydir); 658 dirty(bp); 659 bp = getdirblk(DIP(dp, di_db[lastbn + 1]), 660 sblksize(&sblock, DIP(dp, di_size), lastbn + 1)); 661 if (bp->b_errs) 662 goto bad; 663 memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir); 664 pwarn("NO SPACE LEFT IN %s", name); 665 if (preen) 666 printf(" (EXPANDED)\n"); 667 else if (reply("EXPAND") == 0) 668 goto bad; 669 dirty(bp); 670 inodirty(dp); 671 return (1); 672 bad: 673 DIP_SET(dp, di_db[lastbn], DIP(dp, di_db[lastbn + 1])); 674 DIP_SET(dp, di_db[lastbn + 1], 0); 675 DIP_SET(dp, di_size, DIP(dp, di_size) - sblock.fs_bsize); 676 DIP_SET(dp, di_blocks, DIP(dp, di_blocks) - btodb(sblock.fs_bsize)); 677 freeblk(newblk, sblock.fs_frag); 678 return (0); 679 } 680 681 /* 682 * allocate a new directory 683 */ 684 ino_t 685 allocdir(ino_t parent, ino_t request, int mode) 686 { 687 ino_t ino; 688 char *cp; 689 union dinode *dp; 690 struct bufarea *bp; 691 struct inoinfo *inp; 692 struct dirtemplate *dirp; 693 694 ino = allocino(request, IFDIR|mode); 695 dirp = &dirhead; 696 dirp->dot_ino = ino; 697 dirp->dotdot_ino = parent; 698 dp = ginode(ino); 699 bp = getdirblk(DIP(dp, di_db[0]), sblock.fs_fsize); 700 if (bp->b_errs) { 701 freeino(ino); 702 return (0); 703 } 704 memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate)); 705 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 706 cp < &bp->b_un.b_buf[sblock.fs_fsize]; 707 cp += DIRBLKSIZ) 708 memmove(cp, &emptydir, sizeof emptydir); 709 dirty(bp); 710 DIP_SET(dp, di_nlink, 2); 711 inodirty(dp); 712 if (ino == UFS_ROOTINO) { 713 inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 714 cacheino(dp, ino); 715 return(ino); 716 } 717 if (!INO_IS_DVALID(parent)) { 718 freeino(ino); 719 return (0); 720 } 721 cacheino(dp, ino); 722 inp = getinoinfo(ino); 723 inp->i_parent = parent; 724 inp->i_dotdot = parent; 725 inoinfo(ino)->ino_state = inoinfo(parent)->ino_state; 726 if (inoinfo(ino)->ino_state == DSTATE) { 727 inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 728 inoinfo(parent)->ino_linkcnt++; 729 } 730 dp = ginode(parent); 731 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 732 inodirty(dp); 733 return (ino); 734 } 735 736 /* 737 * free a directory inode 738 */ 739 static void 740 freedir(ino_t ino, ino_t parent) 741 { 742 union dinode *dp; 743 744 if (ino != parent) { 745 dp = ginode(parent); 746 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - 1); 747 inodirty(dp); 748 } 749 freeino(ino); 750 } 751 752 /* 753 * generate a temporary name for the lost+found directory. 754 */ 755 static int 756 lftempname(char *bufp, ino_t ino) 757 { 758 ino_t in; 759 char *cp; 760 int namlen; 761 762 cp = bufp + 2; 763 for (in = maxino; in > 0; in /= 10) 764 cp++; 765 *--cp = 0; 766 namlen = cp - bufp; 767 in = ino; 768 while (cp > bufp) { 769 *--cp = (in % 10) + '0'; 770 in /= 10; 771 } 772 *cp = '#'; 773 return (namlen); 774 } 775 776 /* 777 * Get a directory block. 778 * Insure that it is held until another is requested. 779 */ 780 static struct bufarea * 781 getdirblk(ufs2_daddr_t blkno, long size) 782 { 783 784 if (pdirbp != NULL) 785 pdirbp->b_flags &= ~B_INUSE; 786 pdirbp = getdatablk(blkno, size, BT_DIRDATA); 787 return (pdirbp); 788 } 789