1/* 2 * Copyright (c) 1980, 1986, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#if 0 31#ifndef lint 32static const char sccsid[] = "@(#)dir.c 8.8 (Berkeley) 4/28/95"; 33#endif /* not lint */ 34#endif 35#include <sys/cdefs.h> 36__FBSDID("$FreeBSD: stable/11/sbin/fsck_ffs/dir.c 348260 2019-05-25 00:22:07Z mckusick $"); 37 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 52static struct dirtemplate emptydir = { 53 0, DIRBLKSIZ, DT_UNKNOWN, 0, "", 54 0, 0, DT_UNKNOWN, 0, "" 55}; 56static struct dirtemplate dirhead = { 57 0, 12, DT_DIR, 1, ".", 58 0, DIRBLKSIZ - 12, DT_DIR, 2, ".." 59}; 60 61static int chgino(struct inodesc *); 62static int dircheck(struct inodesc *, struct bufarea *, struct direct *); 63static int expanddir(union dinode *dp, char *name); 64static void freedir(ino_t ino, ino_t parent); 65static struct direct *fsck_readdir(struct inodesc *); 66static struct bufarea *getdirblk(ufs2_daddr_t blkno, long size); 67static int lftempname(char *bufp, ino_t ino); 68static int mkentry(struct inodesc *); 69 70/* 71 * Propagate connected state through the tree. 72 */ 73void 74propagate(void) 75{ 76 struct inoinfo **inpp, *inp; 77 struct inoinfo **inpend; 78 long change; 79 80 inpend = &inpsort[inplast]; 81 do { 82 change = 0; 83 for (inpp = inpsort; inpp < inpend; inpp++) { 84 inp = *inpp; 85 if (inp->i_parent == 0) 86 continue; 87 if (inoinfo(inp->i_parent)->ino_state == DFOUND && 88 INO_IS_DUNFOUND(inp->i_number)) { 89 inoinfo(inp->i_number)->ino_state = DFOUND; 90 change++; 91 } 92 } 93 } while (change > 0); 94} 95 96/* 97 * Scan each entry in a directory block. 98 */ 99int 100dirscan(struct inodesc *idesc) 101{ 102 struct direct *dp; 103 struct bufarea *bp; 104 u_int dsize, n; 105 long blksiz; 106 char dbuf[DIRBLKSIZ]; 107 108 if (idesc->id_type != DATA) 109 errx(EEXIT, "wrong type to dirscan %d", idesc->id_type); 110 if (idesc->id_entryno == 0 && 111 (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) 112 idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); 113 blksiz = idesc->id_numfrags * sblock.fs_fsize; 114 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 115 idesc->id_filesize -= blksiz; 116 return (SKIP); 117 } 118 idesc->id_loc = 0; 119 for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 120 dsize = dp->d_reclen; 121 if (dsize > sizeof(dbuf)) 122 dsize = sizeof(dbuf); 123 memmove(dbuf, dp, (size_t)dsize); 124 idesc->id_dirp = (struct direct *)dbuf; 125 if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 126 bp = getdirblk(idesc->id_blkno, blksiz); 127 memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf, 128 (size_t)dsize); 129 dirty(bp); 130 sbdirty(); 131 rerun = 1; 132 } 133 if (n & STOP) 134 return (n); 135 } 136 return (idesc->id_filesize > 0 ? KEEPON : STOP); 137} 138 139/* 140 * Get and verify the next entry in a directory. 141 * We also verify that if there is another entry in the block that it is 142 * valid, so if it is not valid it can be subsumed into the current entry. 143 */ 144static struct direct * 145fsck_readdir(struct inodesc *idesc) 146{ 147 struct direct *dp, *ndp; 148 struct bufarea *bp; 149 long size, blksiz, subsume_ndp; 150 151 subsume_ndp = 0; 152 blksiz = idesc->id_numfrags * sblock.fs_fsize; 153 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 154 return (NULL); 155 bp = getdirblk(idesc->id_blkno, blksiz); 156 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 157 /* 158 * Only need to check current entry if it is the first in the 159 * the block, as later entries will have been checked in the 160 * previous call to this function. 161 */ 162 if (idesc->id_loc % DIRBLKSIZ != 0 || dircheck(idesc, bp, dp) != 0) { 163 /* 164 * Current entry is good, update to point at next. 165 */ 166 idesc->id_loc += dp->d_reclen; 167 idesc->id_filesize -= dp->d_reclen; 168 /* 169 * If at end of directory block, just return this entry. 170 */ 171 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz || 172 idesc->id_loc % DIRBLKSIZ == 0) 173 return (dp); 174 /* 175 * If the next entry good, return this entry. 176 */ 177 ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 178 if (dircheck(idesc, bp, ndp) != 0) 179 return (dp); 180 /* 181 * The next entry is bad, so subsume it and the remainder 182 * of this directory block into this entry. 183 */ 184 subsume_ndp = 1; 185 } 186 /* 187 * Current or next entry is bad. Zap current entry or 188 * subsume next entry into current entry as appropriate. 189 */ 190 size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 191 idesc->id_loc += size; 192 idesc->id_filesize -= size; 193 if (idesc->id_fix == IGNORE) 194 return (NULL); 195 if (subsume_ndp) { 196 memset(ndp, 0, size); 197 dp->d_reclen += size; 198 } else { 199 memset(dp, 0, size); 200 dp->d_reclen = size; 201 } 202 if (dofix(idesc, "DIRECTORY CORRUPTED")) 203 dirty(bp); 204 return (dp); 205} 206 207/* 208 * Verify that a directory entry is valid. 209 * This is a superset of the checks made in the kernel. 210 * Also optionally clears padding and unused directory space. 211 * 212 * Returns 0 if the entry is bad, 1 if the entry is good. 213 */ 214static int 215dircheck(struct inodesc *idesc, struct bufarea *bp, struct direct *dp) 216{ 217 size_t size; 218 char *cp; 219 u_int8_t namlen; 220 int spaceleft, modified, unused; 221 222 spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 223 size = DIRSIZ(0, dp); 224 if (dp->d_reclen == 0 || 225 dp->d_reclen > spaceleft || 226 dp->d_reclen < size || 227 idesc->id_filesize < size || 228 (dp->d_reclen & (DIR_ROUNDUP - 1)) != 0) 229 goto bad; 230 modified = 0; 231 if (dp->d_ino == 0) { 232 if (!zflag || fswritefd < 0) 233 return (1); 234 /* 235 * Special case of an unused directory entry. Normally only 236 * occurs at the beginning of a directory block when the block 237 * contains no entries. Other than the first entry in a 238 * directory block, the kernel coalesces unused space with 239 * the previous entry by extending its d_reclen. However, 240 * when cleaning up a directory, fsck may set d_ino to zero 241 * in the middle of a directory block. If we're clearing out 242 * directory cruft (-z flag), then make sure that all directory 243 * space in entries with d_ino == 0 gets fully cleared. 244 */ 245 if (dp->d_type != 0) { 246 dp->d_type = 0; 247 modified = 1; 248 } 249 if (dp->d_namlen != 0) { 250 dp->d_namlen = 0; 251 modified = 1; 252 } 253 unused = dp->d_reclen - __offsetof(struct direct, d_name); 254 for (cp = dp->d_name; unused > 0; unused--, cp++) { 255 if (*cp != '\0') { 256 *cp = '\0'; 257 modified = 1; 258 } 259 } 260 if (modified) 261 dirty(bp); 262 return (1); 263 } 264 /* 265 * The d_type field should not be tested here. A bad type is an error 266 * in the entry itself but is not a corruption of the directory 267 * structure itself. So blowing away all the remaining entries in the 268 * directory block is inappropriate. Rather the type error should be 269 * checked in pass1 and fixed there. 270 * 271 * The name validation should also be done in pass1 although the 272 * check to see if the name is longer than fits in the space 273 * allocated for it (i.e., the *cp != '\0' fails after exiting the 274 * loop below) then it really is a structural error that requires 275 * the stronger action taken here. 276 */ 277 namlen = dp->d_namlen; 278 if (namlen == 0 || dp->d_type > 15) 279 goto bad; 280 for (cp = dp->d_name, size = 0; size < namlen; size++) { 281 if (*cp == '\0' || *cp++ == '/') 282 goto bad; 283 } 284 if (*cp != '\0') 285 goto bad; 286 if (zflag && fswritefd >= 0) { 287 /* 288 * Clear unused directory entry space, including the d_name 289 * padding. 290 */ 291 /* First figure the number of pad bytes. */ 292 unused = roundup2(namlen + 1, DIR_ROUNDUP) - (namlen + 1); 293 294 /* Add in the free space to the end of the record. */ 295 unused += dp->d_reclen - DIRSIZ(0, dp); 296 297 /* 298 * Now clear out the unused space, keeping track if we actually 299 * changed anything. 300 */ 301 for (cp = &dp->d_name[namlen + 1]; unused > 0; unused--, cp++) { 302 if (*cp != '\0') { 303 *cp = '\0'; 304 modified = 1; 305 } 306 } 307 308 if (modified) 309 dirty(bp); 310 } 311 return (1); 312 313bad: 314 if (debug) 315 printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n", 316 dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_type, 317 dp->d_name); 318 return (0); 319} 320 321void 322direrror(ino_t ino, const char *errmesg) 323{ 324 325 fileerror(ino, ino, errmesg); 326} 327 328void 329fileerror(ino_t cwd, ino_t ino, const char *errmesg) 330{ 331 union dinode *dp; 332 char pathbuf[MAXPATHLEN + 1]; 333 334 pwarn("%s ", errmesg); 335 pinode(ino); 336 printf("\n"); 337 getpathname(pathbuf, cwd, ino); 338 if (ino < ROOTINO || ino > maxino) { 339 pfatal("NAME=%s\n", pathbuf); 340 return; 341 } 342 dp = ginode(ino); 343 if (ftypeok(dp)) 344 pfatal("%s=%s\n", 345 (DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE", 346 pathbuf); 347 else 348 pfatal("NAME=%s\n", pathbuf); 349} 350 351void 352adjust(struct inodesc *idesc, int lcnt) 353{ 354 union dinode *dp; 355 int saveresolved; 356 357 dp = ginode(idesc->id_number); 358 if (DIP(dp, di_nlink) == lcnt) { 359 /* 360 * If we have not hit any unresolved problems, are running 361 * in preen mode, and are on a file system using soft updates, 362 * then just toss any partially allocated files. 363 */ 364 if (resolved && (preen || bkgrdflag) && usedsoftdep) { 365 clri(idesc, "UNREF", 1); 366 return; 367 } else { 368 /* 369 * The file system can be marked clean even if 370 * a file is not linked up, but is cleared. 371 * Hence, resolved should not be cleared when 372 * linkup is answered no, but clri is answered yes. 373 */ 374 saveresolved = resolved; 375 if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) { 376 resolved = saveresolved; 377 clri(idesc, "UNREF", 0); 378 return; 379 } 380 /* 381 * Account for the new reference created by linkup(). 382 */ 383 dp = ginode(idesc->id_number); 384 lcnt--; 385 } 386 } 387 if (lcnt != 0) { 388 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 389 ((DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE")); 390 pinode(idesc->id_number); 391 printf(" COUNT %d SHOULD BE %d", 392 DIP(dp, di_nlink), DIP(dp, di_nlink) - lcnt); 393 if (preen || usedsoftdep) { 394 if (lcnt < 0) { 395 printf("\n"); 396 pfatal("LINK COUNT INCREASING"); 397 } 398 if (preen) 399 printf(" (ADJUSTED)\n"); 400 } 401 if (preen || reply("ADJUST") == 1) { 402 if (bkgrdflag == 0) { 403 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - lcnt); 404 inodirty(dp); 405 } else { 406 cmd.value = idesc->id_number; 407 cmd.size = -lcnt; 408 if (debug) 409 printf("adjrefcnt ino %ld amt %lld\n", 410 (long)cmd.value, 411 (long long)cmd.size); 412 if (sysctl(adjrefcnt, MIBSIZE, 0, 0, 413 &cmd, sizeof cmd) == -1) 414 rwerror("ADJUST INODE", cmd.value); 415 } 416 } 417 } 418} 419 420static int 421mkentry(struct inodesc *idesc) 422{ 423 struct direct *dirp = idesc->id_dirp; 424 struct direct newent; 425 int newlen, oldlen; 426 427 newent.d_namlen = strlen(idesc->id_name); 428 newlen = DIRSIZ(0, &newent); 429 if (dirp->d_ino != 0) 430 oldlen = DIRSIZ(0, dirp); 431 else 432 oldlen = 0; 433 if (dirp->d_reclen - oldlen < newlen) 434 return (KEEPON); 435 newent.d_reclen = dirp->d_reclen - oldlen; 436 dirp->d_reclen = oldlen; 437 dirp = (struct direct *)(((char *)dirp) + oldlen); 438 dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ 439 dirp->d_reclen = newent.d_reclen; 440 dirp->d_type = inoinfo(idesc->id_parent)->ino_type; 441 dirp->d_namlen = newent.d_namlen; 442 memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1); 443 return (ALTERED|STOP); 444} 445 446static int 447chgino(struct inodesc *idesc) 448{ 449 struct direct *dirp = idesc->id_dirp; 450 451 if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) 452 return (KEEPON); 453 dirp->d_ino = idesc->id_parent; 454 dirp->d_type = inoinfo(idesc->id_parent)->ino_type; 455 return (ALTERED|STOP); 456} 457 458int 459linkup(ino_t orphan, ino_t parentdir, char *name) 460{ 461 union dinode *dp; 462 int lostdir; 463 ino_t oldlfdir; 464 struct inodesc idesc; 465 char tempname[BUFSIZ]; 466 467 memset(&idesc, 0, sizeof(struct inodesc)); 468 dp = ginode(orphan); 469 lostdir = (DIP(dp, di_mode) & IFMT) == IFDIR; 470 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 471 pinode(orphan); 472 if (preen && DIP(dp, di_size) == 0) 473 return (0); 474 if (cursnapshot != 0) { 475 pfatal("FILE LINKUP IN SNAPSHOT"); 476 return (0); 477 } 478 if (preen) 479 printf(" (RECONNECTED)\n"); 480 else 481 if (reply("RECONNECT") == 0) 482 return (0); 483 if (lfdir == 0) { 484 dp = ginode(ROOTINO); 485 idesc.id_name = strdup(lfname); 486 idesc.id_type = DATA; 487 idesc.id_func = findino; 488 idesc.id_number = ROOTINO; 489 if ((ckinode(dp, &idesc) & FOUND) != 0) { 490 lfdir = idesc.id_parent; 491 } else { 492 pwarn("NO lost+found DIRECTORY"); 493 if (preen || reply("CREATE")) { 494 lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); 495 if (lfdir != 0) { 496 if (makeentry(ROOTINO, lfdir, lfname) != 0) { 497 numdirs++; 498 if (preen) 499 printf(" (CREATED)\n"); 500 } else { 501 freedir(lfdir, ROOTINO); 502 lfdir = 0; 503 if (preen) 504 printf("\n"); 505 } 506 } 507 } 508 } 509 if (lfdir == 0) { 510 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 511 printf("\n\n"); 512 return (0); 513 } 514 } 515 dp = ginode(lfdir); 516 if ((DIP(dp, di_mode) & IFMT) != IFDIR) { 517 pfatal("lost+found IS NOT A DIRECTORY"); 518 if (reply("REALLOCATE") == 0) 519 return (0); 520 oldlfdir = lfdir; 521 if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) { 522 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 523 return (0); 524 } 525 if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) { 526 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 527 return (0); 528 } 529 inodirty(dp); 530 idesc.id_type = ADDR; 531 idesc.id_func = pass4check; 532 idesc.id_number = oldlfdir; 533 adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1); 534 inoinfo(oldlfdir)->ino_linkcnt = 0; 535 dp = ginode(lfdir); 536 } 537 if (inoinfo(lfdir)->ino_state != DFOUND) { 538 pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 539 return (0); 540 } 541 (void)lftempname(tempname, orphan); 542 if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) { 543 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 544 printf("\n\n"); 545 return (0); 546 } 547 inoinfo(orphan)->ino_linkcnt--; 548 if (lostdir) { 549 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 550 parentdir != (ino_t)-1) 551 (void)makeentry(orphan, lfdir, ".."); 552 dp = ginode(lfdir); 553 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 554 inodirty(dp); 555 inoinfo(lfdir)->ino_linkcnt++; 556 pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan); 557 if (parentdir != (ino_t)-1) { 558 printf("PARENT WAS I=%lu\n", (u_long)parentdir); 559 /* 560 * The parent directory, because of the ordering 561 * guarantees, has had the link count incremented 562 * for the child, but no entry was made. This 563 * fixes the parent link count so that fsck does 564 * not need to be rerun. 565 */ 566 inoinfo(parentdir)->ino_linkcnt++; 567 } 568 if (preen == 0) 569 printf("\n"); 570 } 571 return (1); 572} 573 574/* 575 * fix an entry in a directory. 576 */ 577int 578changeino(ino_t dir, const char *name, ino_t newnum) 579{ 580 struct inodesc idesc; 581 582 memset(&idesc, 0, sizeof(struct inodesc)); 583 idesc.id_type = DATA; 584 idesc.id_func = chgino; 585 idesc.id_number = dir; 586 idesc.id_fix = DONTKNOW; 587 idesc.id_name = strdup(name); 588 idesc.id_parent = newnum; /* new value for name */ 589 return (ckinode(ginode(dir), &idesc)); 590} 591 592/* 593 * make an entry in a directory 594 */ 595int 596makeentry(ino_t parent, ino_t ino, const char *name) 597{ 598 union dinode *dp; 599 struct inodesc idesc; 600 char pathbuf[MAXPATHLEN + 1]; 601 602 if (parent < ROOTINO || parent >= maxino || 603 ino < ROOTINO || ino >= maxino) 604 return (0); 605 memset(&idesc, 0, sizeof(struct inodesc)); 606 idesc.id_type = DATA; 607 idesc.id_func = mkentry; 608 idesc.id_number = parent; 609 idesc.id_parent = ino; /* this is the inode to enter */ 610 idesc.id_fix = DONTKNOW; 611 idesc.id_name = strdup(name); 612 dp = ginode(parent); 613 if (DIP(dp, di_size) % DIRBLKSIZ) { 614 DIP_SET(dp, di_size, roundup(DIP(dp, di_size), DIRBLKSIZ)); 615 inodirty(dp); 616 } 617 if ((ckinode(dp, &idesc) & ALTERED) != 0) 618 return (1); 619 getpathname(pathbuf, parent, parent); 620 dp = ginode(parent); 621 if (expanddir(dp, pathbuf) == 0) 622 return (0); 623 return (ckinode(dp, &idesc) & ALTERED); 624} 625 626/* 627 * Attempt to expand the size of a directory 628 */ 629static int 630expanddir(union dinode *dp, char *name) 631{ 632 ufs2_daddr_t lastbn, newblk; 633 struct bufarea *bp; 634 char *cp, firstblk[DIRBLKSIZ]; 635 636 lastbn = lblkno(&sblock, DIP(dp, di_size)); 637 if (lastbn >= NDADDR - 1 || DIP(dp, di_db[lastbn]) == 0 || 638 DIP(dp, di_size) == 0) 639 return (0); 640 if ((newblk = allocblk(sblock.fs_frag)) == 0) 641 return (0); 642 DIP_SET(dp, di_db[lastbn + 1], DIP(dp, di_db[lastbn])); 643 DIP_SET(dp, di_db[lastbn], newblk); 644 DIP_SET(dp, di_size, DIP(dp, di_size) + sblock.fs_bsize); 645 DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize)); 646 bp = getdirblk(DIP(dp, di_db[lastbn + 1]), 647 sblksize(&sblock, DIP(dp, di_size), lastbn + 1)); 648 if (bp->b_errs) 649 goto bad; 650 memmove(firstblk, bp->b_un.b_buf, DIRBLKSIZ); 651 bp = getdirblk(newblk, sblock.fs_bsize); 652 if (bp->b_errs) 653 goto bad; 654 memmove(bp->b_un.b_buf, firstblk, DIRBLKSIZ); 655 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 656 cp < &bp->b_un.b_buf[sblock.fs_bsize]; 657 cp += DIRBLKSIZ) 658 memmove(cp, &emptydir, sizeof emptydir); 659 dirty(bp); 660 bp = getdirblk(DIP(dp, di_db[lastbn + 1]), 661 sblksize(&sblock, DIP(dp, di_size), lastbn + 1)); 662 if (bp->b_errs) 663 goto bad; 664 memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir); 665 pwarn("NO SPACE LEFT IN %s", name); 666 if (preen) 667 printf(" (EXPANDED)\n"); 668 else if (reply("EXPAND") == 0) 669 goto bad; 670 dirty(bp); 671 inodirty(dp); 672 return (1); 673bad: 674 DIP_SET(dp, di_db[lastbn], DIP(dp, di_db[lastbn + 1])); 675 DIP_SET(dp, di_db[lastbn + 1], 0); 676 DIP_SET(dp, di_size, DIP(dp, di_size) - sblock.fs_bsize); 677 DIP_SET(dp, di_blocks, DIP(dp, di_blocks) - btodb(sblock.fs_bsize)); 678 freeblk(newblk, sblock.fs_frag); 679 return (0); 680} 681 682/* 683 * allocate a new directory 684 */ 685ino_t 686allocdir(ino_t parent, ino_t request, int mode) 687{ 688 ino_t ino; 689 char *cp; 690 union dinode *dp; 691 struct bufarea *bp; 692 struct inoinfo *inp; 693 struct dirtemplate *dirp; 694 695 ino = allocino(request, IFDIR|mode); 696 dirp = &dirhead; 697 dirp->dot_ino = ino; 698 dirp->dotdot_ino = parent; 699 dp = ginode(ino); 700 bp = getdirblk(DIP(dp, di_db[0]), sblock.fs_fsize); 701 if (bp->b_errs) { 702 freeino(ino); 703 return (0); 704 } 705 memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate)); 706 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 707 cp < &bp->b_un.b_buf[sblock.fs_fsize]; 708 cp += DIRBLKSIZ) 709 memmove(cp, &emptydir, sizeof emptydir); 710 dirty(bp); 711 DIP_SET(dp, di_nlink, 2); 712 inodirty(dp); 713 if (ino == ROOTINO) { 714 inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 715 cacheino(dp, ino); 716 return(ino); 717 } 718 if (!INO_IS_DVALID(parent)) { 719 freeino(ino); 720 return (0); 721 } 722 cacheino(dp, ino); 723 inp = getinoinfo(ino); 724 inp->i_parent = parent; 725 inp->i_dotdot = parent; 726 inoinfo(ino)->ino_state = inoinfo(parent)->ino_state; 727 if (inoinfo(ino)->ino_state == DSTATE) { 728 inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 729 inoinfo(parent)->ino_linkcnt++; 730 } 731 dp = ginode(parent); 732 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 733 inodirty(dp); 734 return (ino); 735} 736 737/* 738 * free a directory inode 739 */ 740static void 741freedir(ino_t ino, ino_t parent) 742{ 743 union dinode *dp; 744 745 if (ino != parent) { 746 dp = ginode(parent); 747 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - 1); 748 inodirty(dp); 749 } 750 freeino(ino); 751} 752 753/* 754 * generate a temporary name for the lost+found directory. 755 */ 756static int 757lftempname(char *bufp, ino_t ino) 758{ 759 ino_t in; 760 char *cp; 761 int namlen; 762 763 cp = bufp + 2; 764 for (in = maxino; in > 0; in /= 10) 765 cp++; 766 *--cp = 0; 767 namlen = cp - bufp; 768 in = ino; 769 while (cp > bufp) { 770 *--cp = (in % 10) + '0'; 771 in /= 10; 772 } 773 *cp = '#'; 774 return (namlen); 775} 776 777/* 778 * Get a directory block. 779 * Insure that it is held until another is requested. 780 */ 781static struct bufarea * 782getdirblk(ufs2_daddr_t blkno, long size) 783{ 784 785 if (pdirbp != NULL) 786 pdirbp->b_flags &= ~B_INUSE; 787 pdirbp = getdatablk(blkno, size, BT_DIRDATA); 788 return (pdirbp); 789} 790