1 /* 2 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 /* 10 * Copyright (c) 1980, 1986, 1990 The Regents of the University of California. 11 * All rights reserved. 12 * 13 * Redistribution and use in source and binary forms are permitted 14 * provided that: (1) source distributions retain this entire copyright 15 * notice and comment, and (2) distributions including binaries display 16 * the following acknowledgement: ``This product includes software 17 * developed by the University of California, Berkeley and its contributors'' 18 * in the documentation or other materials provided with the distribution 19 * and in all advertising materials mentioning features or use of this 20 * software. Neither the name of the University nor the names of its 21 * contributors may be used to endorse or promote products derived 22 * from this software without specific prior written permission. 23 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 #include <sys/param.h> 31 #include <sys/types.h> 32 #include <sys/sysmacros.h> 33 #include <sys/mntent.h> 34 35 #define bcopy(f, t, n) memcpy(t, f, n) 36 #define bzero(s, n) memset(s, 0, n) 37 #define bcmp(s, d, n) memcmp(s, d, n) 38 39 #define index(s, r) strchr(s, r) 40 #define rindex(s, r) strrchr(s, r) 41 42 #include <sys/fs/ufs_fs.h> 43 #include <sys/vnode.h> 44 #include <sys/fs/ufs_inode.h> 45 #define _KERNEL 46 #include <sys/fs/ufs_fsdir.h> 47 #undef _KERNEL 48 #include "fsck.h" 49 50 char *lfname = "lost+found"; 51 int lfmode = 01700; 52 struct dirtemplate emptydir = { 0, DIRBLKSIZ }; 53 struct dirtemplate dirhead = { 0, 12, 1, ".", 0, DIRBLKSIZ - 12, 2, ".." }; 54 55 struct direct *fsck_readdir(); 56 struct bufarea *getdirblk(daddr32_t, int); 57 58 /* 59 * Propagate connected state through the tree. 60 */ 61 propagate() 62 { 63 struct inoinfo **inpp, *inp; 64 struct inoinfo **inpend; 65 int change; 66 67 inpend = &inpsort[inplast]; 68 do { 69 change = 0; 70 for (inpp = inpsort; inpp < inpend; inpp++) { 71 inp = *inpp; 72 if (inp->i_parent == 0) 73 continue; 74 if (statemap[inp->i_parent] == DFOUND && 75 statemap[inp->i_number] == DSTATE) { 76 statemap[inp->i_number] = DFOUND; 77 change++; 78 } 79 } 80 } while (change > 0); 81 } 82 83 /* 84 * Scan each entry in a directory block. 85 */ 86 dirscan(idesc) 87 struct inodesc *idesc; 88 { 89 struct direct *dp; 90 struct bufarea *bp; 91 int dsize, n; 92 int blksiz; 93 char dbuf[DIRBLKSIZ]; 94 95 if (idesc->id_type != DATA) 96 errexit("wrong type to dirscan %d\n", idesc->id_type); 97 if (idesc->id_entryno == 0 && 98 (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) 99 idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); 100 blksiz = idesc->id_numfrags * sblock.fs_fsize; 101 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 102 idesc->id_filesize -= (offset_t)blksiz; 103 return (SKIP); 104 } 105 idesc->id_loc = 0; 106 for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 107 /* 108 * If we were just passed a corrupt directory entry with 109 * d_reclen > DIRBLKSIZ so we don't bcopy() all over our stack. 110 * This directory gets cleaned up later. 111 */ 112 dsize = MIN(dp->d_reclen, DIRBLKSIZ); 113 bcopy((char *)dp, dbuf, dsize); 114 idesc->id_dirp = (struct direct *)dbuf; 115 if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 116 bp = getdirblk(idesc->id_blkno, blksiz); 117 bcopy(dbuf, bp->b_un.b_buf + idesc->id_loc - dsize, 118 dsize); 119 dirty(bp); 120 sbdirty(); 121 } 122 if (n & STOP) 123 return (n); 124 } 125 return (idesc->id_filesize > 0 ? KEEPON : STOP); 126 } 127 128 /* 129 * Get current entry in a directory (and peek at the next entry). 130 */ 131 struct direct * 132 fsck_readdir(idesc) 133 struct inodesc *idesc; 134 { 135 struct direct *dp, *ndp = 0; 136 struct bufarea *bp; 137 int size, blksiz; 138 int dofixret; 139 int origloc = idesc->id_loc; 140 141 blksiz = idesc->id_numfrags * sblock.fs_fsize; 142 /* 143 * Sanity check id_filesize and id_loc fields. 144 */ 145 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 146 return (NULL); 147 148 bp = getdirblk(idesc->id_blkno, blksiz); 149 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 150 151 /* 152 * Check the current entry in the directory. 153 */ 154 if (dircheck(idesc, dp) == 0) { 155 /* 156 * If we are in here, then either the current directory 157 * entry is bad or the next directory entry is bad. 158 */ 159 next_is_bad: 160 /* 161 * Find the amount of space left to the end of the 162 * directory block for either directory entry. 163 */ 164 size = DIRBLKSIZ - (idesc->id_loc & (DIRBLKSIZ - 1)); 165 166 /* 167 * Advance to the end of the directory block. 168 */ 169 idesc->id_loc += size; 170 idesc->id_filesize -= (offset_t)size; 171 172 /* 173 * Ask the question before we fix the in-core directory 174 * block because dofix() may reuse the buffer. 175 */ 176 dofixret = dofix(idesc, "DIRECTORY CORRUPTED"); 177 bp = getdirblk(idesc->id_blkno, blksiz); 178 dp = (struct direct *)(bp->b_un.b_buf + origloc); 179 180 /* 181 * This is the current directory entry and since it is 182 * corrupt we cannot trust the rest of the directory 183 * block so change the current directory entry to 184 * contain nothing and encompass the rest of the block. 185 */ 186 if (ndp == NULL) { 187 dp->d_reclen = size; 188 dp->d_ino = 0; 189 dp->d_namlen = 0; 190 dp->d_name[0] = '\0'; 191 } 192 /* 193 * This is the next directory entry, i.e., we got here 194 * via a "goto next_is_bad". This directory entry is 195 * corrupt. The current directory entry is okay so, if we 196 * are in fix mode, extend just its record size to encompass 197 * the rest of the block. 198 */ 199 else if (dofixret) { 200 dp->d_reclen += size; 201 } 202 /* 203 * If the user said to fix the directory corruption, then 204 * mark the block as dirty. Otherwise, our "repairs" only 205 * apply to the in-core copy so we don't hand back trash 206 * to the caller. 207 * 208 * Note: It is possible that saying "no" to a change in 209 * one part of the I/O buffer and "yes" to a later change 210 * in the same I/O buffer may still flush the change to 211 * which we said "no". This is the pathalogical case and 212 * no fix is planned at this time. 213 */ 214 if (dofixret) 215 dirty(bp); 216 return (dp); 217 } 218 /* 219 * The current directory entry checked out so advance past it. 220 */ 221 idesc->id_loc += dp->d_reclen; 222 idesc->id_filesize -= (offset_t)dp->d_reclen; 223 224 /* 225 * If we are not at the directory block boundary, then peek 226 * at the next directory entry and if it is bad we can add 227 * its space to the current directory entry (compression). 228 * Again, we sanity check the id_loc and id_filesize fields 229 * since we modified them above. 230 */ 231 if ((idesc->id_loc & (DIRBLKSIZ - 1)) && 232 idesc->id_loc < blksiz && idesc->id_filesize > 0) { 233 ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 234 if (dircheck(idesc, ndp) == 0) 235 goto next_is_bad; 236 } 237 238 return (dp); 239 } 240 241 /* 242 * Verify that a directory entry is valid. 243 * This is a superset of the checks made in the kernel. 244 */ 245 dircheck(idesc, dp) 246 struct inodesc *idesc; 247 struct direct *dp; 248 { 249 int size; 250 char *cp; 251 int spaceleft; 252 253 size = DIRSIZ(dp); 254 spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 255 if (dp->d_ino < maxino && 256 dp->d_reclen != 0 && 257 (int)dp->d_reclen <= spaceleft && 258 (dp->d_reclen & 0x3) == 0 && 259 (int)dp->d_reclen >= size && 260 idesc->id_filesize >= (offset_t)size && 261 dp->d_namlen <= MAXNAMLEN) { 262 if (dp->d_ino == 0) 263 return (1); 264 for (cp = dp->d_name, size = 0; size < (int)dp->d_namlen; 265 size++, cp++) 266 if ((*cp == 0) || (*cp == '/')) 267 return (0); 268 if (*cp == 0) 269 return (1); 270 } 271 return (0); 272 } 273 274 direrror(ino, errmesg) 275 ino_t ino; 276 char *errmesg; 277 { 278 279 fileerror(ino, ino, errmesg); 280 } 281 282 fileerror(cwd, ino, errmesg) 283 ino_t cwd, ino; 284 char *errmesg; 285 { 286 struct dinode *dp; 287 char pathbuf[MAXPATHLEN + 1]; 288 289 pwarn("%s ", errmesg); 290 pinode(ino); 291 printf("\n"); 292 getpathname(pathbuf, cwd, ino); 293 if (ino < UFSROOTINO || ino > maxino) { 294 pfatal("NAME=%s\n", pathbuf); 295 return; 296 } 297 dp = ginode(ino); 298 if (ftypeok(dp)) 299 pfatal("%s=%s\n", 300 (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf); 301 else 302 pfatal("NAME=%s\n", pathbuf); 303 } 304 305 adjust(idesc, lcnt) 306 struct inodesc *idesc; 307 short lcnt; 308 { 309 struct dinode *dp; 310 311 dp = ginode(idesc->id_number); 312 if (dp->di_nlink == lcnt) { 313 if (linkup(idesc->id_number, (ino_t)0) == 0) { 314 clri(idesc, "UNREF", 0); 315 return; 316 } 317 lcnt = (short)lncntp[idesc->id_number]; 318 } 319 if (lcnt && dp->di_nlink != lcnt) { 320 pwarn("LINK COUNT %s", 321 (lfdir == idesc->id_number) ? lfname : 322 (dp->di_mode & IFMT) == IFDIR ? "DIR" : 323 (dp->di_mode & IFMT) == IFATTRDIR ? "ATTR DIR" : 324 (dp->di_mode & IFMT) == IFSHAD ? "ACL" : "FILE"); 325 pinode(idesc->id_number); 326 printf(" COUNT %d SHOULD BE %d", 327 dp->di_nlink, dp->di_nlink - lcnt); 328 if (preen) { 329 if (lcnt < 0) { 330 printf("\n"); 331 if ((dp->di_mode & IFMT) == IFSHAD) 332 pwarn("LINK COUNT INCREASING"); 333 else 334 pfatal("LINK COUNT INCREASING"); 335 } 336 printf(" (ADJUSTED)\n"); 337 } 338 if (preen || reply("ADJUST") == 1) { 339 dp->di_nlink -= lcnt; 340 inodirty(); 341 } 342 } 343 } 344 345 mkentry(idesc) 346 struct inodesc *idesc; 347 { 348 struct direct *dirp = idesc->id_dirp; 349 struct direct newent; 350 int newlen, oldlen; 351 352 newent.d_namlen = strlen(idesc->id_name); 353 newlen = DIRSIZ(&newent); 354 if (dirp->d_ino != 0) 355 oldlen = DIRSIZ(dirp); 356 else 357 oldlen = 0; 358 if ((int)dirp->d_reclen - oldlen < newlen) 359 return (KEEPON); 360 newent.d_reclen = dirp->d_reclen - (ushort_t)oldlen; 361 dirp->d_reclen = (ushort_t)oldlen; 362 dirp = (struct direct *)(((char *)dirp) + oldlen); 363 dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ 364 dirp->d_reclen = newent.d_reclen; 365 dirp->d_namlen = newent.d_namlen; 366 bcopy(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1); 367 return (ALTERED|STOP); 368 } 369 370 chgino(idesc) 371 struct inodesc *idesc; 372 { 373 struct direct *dirp = idesc->id_dirp; 374 375 if (bcmp(dirp->d_name, idesc->id_name, (size_t)dirp->d_namlen + 1)) 376 return (KEEPON); 377 dirp->d_ino = idesc->id_parent; 378 return (ALTERED|STOP); 379 } 380 381 linkup(orphan, parentdir) 382 ino_t orphan; 383 ino_t parentdir; 384 { 385 struct dinode *dp; 386 int lostdir; 387 int lostshadow; 388 int lostattrdir; 389 ino_t oldlfdir; 390 struct inodesc idesc; 391 char tempname[BUFSIZ]; 392 extern int pass4check(); 393 394 bzero((char *)&idesc, sizeof (struct inodesc)); 395 dp = ginode(orphan); 396 lostdir = (((dp->di_mode & IFMT) == IFDIR) || 397 ((dp->di_mode & IFMT) == IFATTRDIR)); 398 /* 399 * reclaim thread will remove this dir at mount 400 */ 401 if (lostdir && 402 (willreclaim && dp->di_nlink <= 0 && lncntp[orphan] == -1)) { 403 if (parentdir && lncntp[parentdir] < 0) 404 ++lncntp[parentdir]; 405 return (0); 406 } 407 lostshadow = (dp->di_mode & IFMT) == IFSHAD; 408 lostattrdir = (dp->di_mode & IFMT) == IFATTRDIR; 409 pwarn("UNREF %s ", lostattrdir ? "ATTRDIR" : lostdir ? "DIR" : 410 lostshadow ? "ACL" : "FILE"); 411 pinode(orphan); 412 if (lostshadow || (dp->di_size == 0 && dp->di_oeftflag == 0)) 413 return (0); 414 if (preen) 415 printf(" (RECONNECTED)\n"); 416 else 417 if (reply("RECONNECT") == 0) 418 return (0); 419 if (lfdir == 0) { 420 dp = ginode(UFSROOTINO); 421 idesc.id_name = lfname; 422 idesc.id_type = DATA; 423 idesc.id_func = findino; 424 idesc.id_number = UFSROOTINO; 425 idesc.id_fix = DONTKNOW; 426 if ((ckinode(dp, &idesc) & FOUND) != 0) { 427 lfdir = idesc.id_parent; 428 } else { 429 pwarn("NO lost+found DIRECTORY"); 430 if (preen || reply("CREATE")) { 431 lfdir = allocdir(UFSROOTINO, (ino_t)0, lfmode); 432 if (lfdir != 0) { 433 if (makeentry(UFSROOTINO, lfdir, 434 lfname) != 0) { 435 if (preen) 436 printf(" (CREATED)\n"); 437 } else { 438 freedir(lfdir, UFSROOTINO); 439 lfdir = 0; 440 if (preen) 441 printf("\n"); 442 } 443 } 444 } 445 } 446 if (lfdir == 0) { 447 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 448 printf("\n\n"); 449 return (0); 450 } 451 } else { /* lost+found already created */ 452 idesc.id_name = lfname; 453 } 454 dp = ginode(lfdir); 455 if ((dp->di_mode & IFMT) != IFDIR) { 456 pfatal("lost+found IS NOT A DIRECTORY"); 457 if (reply("REALLOCATE") == 0) 458 return (0); 459 oldlfdir = lfdir; 460 if ((lfdir = allocdir(UFSROOTINO, (ino_t)0, lfmode)) == 0) { 461 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 462 return (0); 463 } 464 if ((changeino(UFSROOTINO, lfname, lfdir) & ALTERED) == 0) { 465 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 466 return (0); 467 } 468 inodirty(); 469 idesc.id_type = ADDR; 470 idesc.id_func = pass4check; 471 idesc.id_number = oldlfdir; 472 idesc.id_fix = DONTKNOW; 473 lncntp[oldlfdir]++; 474 adjust(&idesc, lncntp[oldlfdir]); 475 lncntp[oldlfdir] = 0; 476 dp = ginode(lfdir); 477 } 478 if (statemap[lfdir] != DFOUND) { 479 pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 480 return (0); 481 } 482 (void) lftempname(tempname, orphan); 483 if (makeentry(lfdir, orphan, tempname) == 0) { 484 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 485 printf("\n\n"); 486 return (0); 487 } 488 lncntp[orphan]--; 489 if (lostdir) { 490 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 491 parentdir != (ino_t)-1) 492 (void) makeentry(orphan, lfdir, ".."); 493 /* 494 * If we were half-detached, don't try to get 495 * inode 0 later on. 496 */ 497 if (parentdir == 0) 498 parentdir = -1; 499 dp = ginode(lfdir); 500 dp->di_nlink++; 501 inodirty(); 502 lncntp[lfdir]++; 503 if (parentdir != lfdir && parentdir != -1) { 504 dp = ginode(parentdir); 505 if ((dp->di_mode & IFMT) == IFDIR) 506 lncntp[parentdir]++; 507 } 508 pwarn("DIR I=%u CONNECTED. ", orphan); 509 if (parentdir != (ino_t)-1) 510 printf("PARENT WAS I=%u\n", parentdir); 511 if (preen == 0) 512 printf("\n"); 513 } 514 return (1); 515 } 516 517 /* 518 * fix an entry in a directory. 519 */ 520 changeino(dir, name, newnum) 521 ino_t dir; 522 char *name; 523 ino_t newnum; 524 { 525 struct inodesc idesc; 526 527 bzero((char *)&idesc, sizeof (struct inodesc)); 528 idesc.id_type = DATA; 529 idesc.id_func = chgino; 530 idesc.id_number = dir; 531 idesc.id_fix = DONTKNOW; 532 idesc.id_name = name; 533 idesc.id_parent = newnum; /* new value for name */ 534 return (ckinode(ginode(dir), &idesc)); 535 } 536 537 /* 538 * make an entry in a directory 539 */ 540 makeentry(parent, ino, name) 541 ino_t parent, ino; 542 char *name; 543 { 544 struct dinode *dp; 545 struct inodesc idesc; 546 char pathbuf[MAXPATHLEN + 1]; 547 548 if (parent < UFSROOTINO || parent >= maxino || 549 ino < UFSROOTINO || ino >= maxino) 550 return (0); 551 bzero((char *)&idesc, sizeof (struct inodesc)); 552 idesc.id_type = DATA; 553 idesc.id_func = mkentry; 554 idesc.id_number = parent; 555 idesc.id_parent = ino; /* this is the inode to enter */ 556 idesc.id_fix = DONTKNOW; 557 idesc.id_name = name; 558 dp = ginode(parent); 559 if (dp->di_size % DIRBLKSIZ) { 560 dp->di_size = roundup(dp->di_size, DIRBLKSIZ); 561 inodirty(); 562 } 563 if ((ckinode(dp, &idesc) & ALTERED) != 0) 564 return (1); 565 getpathname(pathbuf, parent, parent); 566 dp = ginode(parent); 567 if (expanddir(dp, pathbuf) == 0) 568 return (0); 569 idesc.id_fix = DONTKNOW; 570 return (ckinode(dp, &idesc) & ALTERED); 571 } 572 573 /* 574 * Attempt to expand the size of a directory 575 */ 576 expanddir(dp, name) 577 struct dinode *dp; 578 char *name; 579 { 580 struct bufarea *bpback, *bp[2]; 581 daddr32_t nxtibn, nxtbn; 582 daddr32_t newblk[2]; 583 char *cp; 584 int bc, n, f; 585 int allocIndir; 586 int frag2blks = 0; 587 int lffragsz = 0; 588 int c = 0; 589 590 if (dp->di_size == 0) 591 return (0); 592 593 nxtbn = lblkno(&sblock, dp->di_size - 1) + 1; 594 595 /* 596 * Check that none of the direct block addresses for 597 * the directory are bogus. 598 */ 599 for (bc = 0; ((nxtbn > 0) && (bc < nxtbn) && (bc < NDADDR)); bc++) { 600 if (dp->di_db[bc] == 0) 601 return (0); 602 } 603 604 /* 605 * Determine our data block allocation needs. We always need to 606 * allocate at least one data block. We may need a second, the 607 * indirect block itself. 608 */ 609 allocIndir = 0; 610 nxtibn = -1; 611 612 if (nxtbn <= NDADDR) { 613 /* 614 * Still in direct blocks. Check for the unlikely 615 * case where the last block is a frag rather than 616 * a full block. This would only happen if someone had 617 * created a file in lost+found, and then that caused 618 * the dynamic directory shrinking capabilities of ufs 619 * to kick in. 620 */ 621 lffragsz = dp->di_size % sblock.fs_bsize; 622 } 623 624 if (nxtbn >= NDADDR && !lffragsz) { 625 n = sblock.fs_bsize/sizeof (daddr32_t); 626 nxtibn = nxtbn - NDADDR; 627 /* 628 * Only go one level of indirection 629 */ 630 if (nxtibn >= n) 631 return (0); 632 633 if (nxtibn == 0) 634 allocIndir++; 635 } 636 637 /* 638 * Allocate all the new blocks we need. 639 */ 640 if ((newblk[0] = allocblk(sblock.fs_frag)) == 0) 641 goto bail; 642 c++; 643 if (allocIndir) { 644 if ((newblk[1] = allocblk(sblock.fs_frag)) == 0) 645 goto bail; 646 c++; 647 } 648 649 /* 650 * Take care of the block that will hold new directory entries. 651 * This one is always allocated. 652 */ 653 bp[0] = getdirblk(newblk[0], sblock.fs_bsize); 654 if (bp[0]->b_errs) 655 goto bail; 656 if (lffragsz) { 657 bpback = getdirblk(dp->di_db[nxtbn - 1], 658 dblksize(&sblock, dp, nxtbn - 1)); 659 if (bpback->b_errs) 660 goto bail; 661 bcopy(bpback->b_un.b_buf, bp[0]->b_un.b_buf, (size_t)lffragsz); 662 for (cp = &(bp[0]->b_un.b_buf[lffragsz]); 663 cp < &(bp[0]->b_un.b_buf[sblock.fs_bsize]); 664 cp += DIRBLKSIZ) 665 bcopy((char *)&emptydir, cp, sizeof (emptydir)); 666 } else { 667 for (cp = bp[0]->b_un.b_buf; 668 cp < &(bp[0]->b_un.b_buf[sblock.fs_bsize]); 669 cp += DIRBLKSIZ) 670 bcopy((char *)&emptydir, cp, sizeof (emptydir)); 671 } 672 dirty(bp[0]); 673 674 /* 675 * If we allocated the indirect block, zero it out. Otherwise 676 * read it in if we're using one. 677 */ 678 if (allocIndir) { 679 bp[1] = getdatablk(newblk[1], sblock.fs_bsize); 680 if (bp[1]->b_errs) 681 goto bail; 682 bzero(bp[1]->b_un.b_buf, sblock.fs_bsize); 683 dirty(bp[1]); 684 } else if (nxtibn >= 0) { 685 /* Check that the indirect block pointer looks okay */ 686 if (dp->di_ib[0] == 0) 687 goto bail; 688 689 bp[1] = getdatablk(dp->di_ib[0], sblock.fs_bsize); 690 if (bp[1]->b_errs) 691 goto bail; 692 693 for (bc = 0; ((bc < nxtibn) && (bc < n)); bc++) { 694 if (((daddr32_t *)bp[1]->b_un.b_buf)[bc] == 0) 695 goto bail; 696 } 697 } 698 699 pwarn("NO SPACE LEFT IN %s", name); 700 if (preen) 701 printf(" (EXPANDED)\n"); 702 else if (reply("EXPAND") == 0) 703 goto bail; 704 705 /* 706 * Now that everything we need is gathered up, we 707 * do the final assignments 708 */ 709 710 if (lffragsz) { 711 frag2blks = roundup(lffragsz, sblock.fs_fsize); 712 freeblk(dp->di_db[nxtbn - 1], 713 frag2blks / sblock.fs_fsize); 714 frag2blks = btodb(frag2blks); 715 dp->di_size -= (u_offset_t)lffragsz; 716 dp->di_blocks = dp->di_blocks - frag2blks; 717 dp->di_db[nxtbn - 1] = newblk[0]; 718 dp->di_size += (u_offset_t)sblock.fs_bsize; 719 dp->di_blocks += btodb(sblock.fs_bsize); 720 inodirty(); 721 return (1); 722 } 723 724 dp->di_size += (u_offset_t)sblock.fs_bsize; 725 dp->di_blocks += btodb(sblock.fs_bsize); 726 if (allocIndir) { 727 dp->di_blocks += btodb(sblock.fs_bsize); 728 } 729 730 inodirty(); 731 if (nxtibn < 0) { 732 /* 733 * Still in direct blocks 734 */ 735 dp->di_db[nxtbn] = newblk[0]; 736 } else { 737 /* 738 * Last indirect is always going to point at the 739 * new directory buffer 740 */ 741 if (allocIndir) 742 dp->di_ib[0] = newblk[1]; 743 ((daddr32_t *)bp[1]->b_un.b_buf)[nxtibn] = newblk[0]; 744 dirty(bp[1]); 745 } 746 return (1); 747 748 bail: 749 for (f = 0; f < c; f++) 750 freeblk(newblk[f], sblock.fs_frag); 751 return (0); 752 } 753 754 /* 755 * allocate a new directory 756 */ 757 allocdir(parent, request, mode) 758 ino_t parent, request; 759 int mode; 760 { 761 ino_t ino; 762 char *cp; 763 struct dinode *dp; 764 struct inoinfo *inp; 765 struct bufarea *bp; 766 767 ino = allocino(request, IFDIR|mode); 768 if (ino == 0) 769 return (0); 770 dirhead.dot_ino = ino; 771 dirhead.dotdot_ino = parent; 772 dp = ginode(ino); 773 bp = getdirblk(dp->di_db[0], sblock.fs_fsize); 774 if (bp->b_errs) { 775 freeino(ino); 776 return (0); 777 } 778 bcopy((char *)&dirhead, bp->b_un.b_buf, sizeof (dirhead)); 779 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 780 cp < &bp->b_un.b_buf[sblock.fs_fsize]; 781 cp += DIRBLKSIZ) 782 bcopy((char *)&emptydir, cp, sizeof (emptydir)); 783 dirty(bp); 784 dp->di_nlink = 2; 785 inodirty(); 786 if (!inocached(ino)) { 787 if (debug) 788 printf("inode %d added to directory cache\n", 789 ino); 790 cacheino(dp, ino); 791 } else { 792 /* 793 * re-using an old directory inode 794 */ 795 inp = getinoinfo(ino); 796 inp->i_isize = (offset_t)dp->di_size; 797 inp->i_numblks = dp->di_blocks * sizeof (daddr32_t); 798 inp->i_parent = parent; 799 bcopy((char *)&dp->di_db[0], (char *)&inp->i_blks[0], 800 (size_t)inp->i_numblks); 801 } 802 if (ino == UFSROOTINO) { 803 lncntp[ino] = dp->di_nlink; 804 return (ino); 805 } 806 if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { 807 freeino(ino); 808 return (0); 809 } 810 statemap[ino] = statemap[parent]; 811 if (statemap[ino] == DSTATE) { 812 lncntp[ino] = dp->di_nlink; 813 lncntp[parent]++; 814 } 815 dp = ginode(parent); 816 dp->di_nlink++; 817 inodirty(); 818 return (ino); 819 } 820 821 /* 822 * free a directory inode 823 */ 824 freedir(ino, parent) 825 ino_t ino, parent; 826 { 827 struct dinode *dp; 828 829 if (ino != parent) { 830 dp = ginode(parent); 831 dp->di_nlink--; 832 inodirty(); 833 } 834 freeino(ino); 835 } 836 837 /* 838 * generate a temporary name for the lost+found directory. 839 */ 840 lftempname(bufp, ino) 841 char *bufp; 842 ino_t ino; 843 { 844 ino_t in; 845 char *cp; 846 int namlen; 847 848 cp = bufp + 2; 849 for (in = maxino; in > 0; in /= 10) 850 cp++; 851 *--cp = 0; 852 namlen = cp - bufp; 853 in = ino; 854 while (cp > bufp) { 855 *--cp = (in % 10) + '0'; 856 in /= 10; 857 } 858 *cp = '#'; 859 return (namlen); 860 } 861 862 /* 863 * Get a directory block. 864 * Insure that it is held until another is requested. 865 */ 866 struct bufarea * 867 getdirblk(blkno, size) 868 daddr32_t blkno; 869 int size; 870 { 871 if (pdirbp != 0) { 872 brelse(pdirbp); 873 } 874 pdirbp = getdatablk(blkno, size); 875 return (pdirbp); 876 } 877