1 /* $NetBSD: dir.c,v 1.33 2002/05/09 02:55:50 simonb Exp $ */ 2 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. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)dir.c 8.8 (Berkeley) 4/28/95"; 40 #else 41 __RCSID("$NetBSD: dir.c,v 1.33 2002/05/09 02:55:50 simonb Exp $"); 42 #endif 43 #endif /* not lint */ 44 45 #include <sys/param.h> 46 #include <sys/time.h> 47 48 #include <ufs/ufs/dinode.h> 49 #include <ufs/ufs/dir.h> 50 #include <ufs/ffs/fs.h> 51 #include <ufs/ffs/ffs_extern.h> 52 53 #include <err.h> 54 #include <stdio.h> 55 #include <string.h> 56 57 #include "fsck.h" 58 #include "fsutil.h" 59 #include "extern.h" 60 61 char *lfname = "lost+found"; 62 int lfmode = 01700; 63 ino_t lfdir; 64 struct dirtemplate emptydir = { 0, DIRBLKSIZ }; 65 struct dirtemplate dirhead = { 66 0, 12, DT_DIR, 1, ".", 67 0, DIRBLKSIZ - 12, DT_DIR, 2, ".." 68 }; 69 struct odirtemplate odirhead = { 70 0, 12, 1, ".", 71 0, DIRBLKSIZ - 12, 2, ".." 72 }; 73 74 static int chgino __P((struct inodesc *)); 75 static int dircheck __P((struct inodesc *, struct direct *)); 76 static int expanddir __P((struct dinode *, char *)); 77 static void freedir __P((ino_t, ino_t)); 78 static struct direct *fsck_readdir __P((struct inodesc *)); 79 static struct bufarea *getdirblk __P((daddr_t, long)); 80 static int lftempname __P((char *, ino_t)); 81 static int mkentry __P((struct inodesc *)); 82 void reparent __P((ino_t, ino_t)); 83 84 /* 85 * Propagate connected state through the tree. 86 */ 87 void 88 propagate(inumber) 89 ino_t inumber; 90 { 91 struct inoinfo *inp; 92 93 inp = getinoinfo(inumber); 94 95 for (;;) { 96 statemap[inp->i_number] = DMARK; 97 if (inp->i_child && 98 statemap[inp->i_child->i_number] != DMARK) 99 inp = inp->i_child; 100 else if (inp->i_number == inumber) 101 break; 102 else if (inp->i_sibling) 103 inp = inp->i_sibling; 104 else 105 inp = inp->i_parentp; 106 } 107 108 for (;;) { 109 statemap[inp->i_number] = DFOUND; 110 if (inp->i_child && 111 statemap[inp->i_child->i_number] != DFOUND) 112 inp = inp->i_child; 113 else if (inp->i_number == inumber) 114 break; 115 else if (inp->i_sibling) 116 inp = inp->i_sibling; 117 else 118 inp = inp->i_parentp; 119 } 120 } 121 122 void 123 reparent(inumber, parent) 124 ino_t inumber, parent; 125 { 126 struct inoinfo *inp, *pinp; 127 128 inp = getinoinfo(inumber); 129 inp->i_parent = inp->i_dotdot = parent; 130 pinp = getinoinfo(parent); 131 inp->i_parentp = pinp; 132 inp->i_sibling = pinp->i_child; 133 pinp->i_child = inp; 134 propagate(inumber); 135 } 136 137 /* 138 * Scan each entry in a directory block. 139 */ 140 int 141 dirscan(idesc) 142 struct inodesc *idesc; 143 { 144 struct direct *dp; 145 struct bufarea *bp; 146 int dsize, n; 147 long blksiz; 148 char dbuf[DIRBLKSIZ]; 149 150 if (idesc->id_type != DATA) 151 errx(EEXIT, "wrong type to dirscan %d", idesc->id_type); 152 if (idesc->id_entryno == 0 && 153 (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) 154 idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); 155 blksiz = idesc->id_numfrags * sblock->fs_fsize; 156 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 157 idesc->id_filesize -= blksiz; 158 return (SKIP); 159 } 160 161 /* 162 * If we are are swapping byte order in directory entries, just swap 163 * this block and return. 164 */ 165 if (do_dirswap) { 166 int off; 167 bp = getdirblk(idesc->id_blkno, blksiz); 168 for (off = 0; off < blksiz; off += iswap16(dp->d_reclen)) { 169 dp = (struct direct *)(bp->b_un.b_buf + off); 170 dp->d_ino = bswap32(dp->d_ino); 171 dp->d_reclen = bswap16(dp->d_reclen); 172 if (!newinofmt) { 173 u_int8_t tmp = dp->d_namlen; 174 dp->d_namlen = dp->d_type; 175 dp->d_type = tmp; 176 } 177 if (dp->d_reclen == 0) 178 break; 179 } 180 dirty(bp); 181 idesc->id_filesize -= blksiz; 182 return (idesc->id_filesize > 0 ? KEEPON : STOP); 183 } 184 185 idesc->id_loc = 0; 186 for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 187 dsize = iswap16(dp->d_reclen); 188 memmove(dbuf, dp, (size_t)dsize); 189 # if (BYTE_ORDER == LITTLE_ENDIAN) 190 if (!newinofmt && !needswap) { 191 # else 192 if (!newinofmt && needswap) { 193 # endif 194 struct direct *tdp = (struct direct *)dbuf; 195 u_char tmp; 196 197 tmp = tdp->d_namlen; 198 tdp->d_namlen = tdp->d_type; 199 tdp->d_type = tmp; 200 } 201 idesc->id_dirp = (struct direct *)dbuf; 202 if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 203 # if (BYTE_ORDER == LITTLE_ENDIAN) 204 if (!newinofmt && !doinglevel2 && !needswap) { 205 # else 206 if (!newinofmt && !doinglevel2 && needswap) { 207 # endif 208 struct direct *tdp; 209 u_char tmp; 210 211 tdp = (struct direct *)dbuf; 212 tmp = tdp->d_namlen; 213 tdp->d_namlen = tdp->d_type; 214 tdp->d_type = tmp; 215 } 216 bp = getdirblk(idesc->id_blkno, blksiz); 217 memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf, 218 (size_t)dsize); 219 dirty(bp); 220 sbdirty(); 221 } 222 if (n & STOP) 223 return (n); 224 } 225 return (idesc->id_filesize > 0 ? KEEPON : STOP); 226 } 227 228 /* 229 * get next entry in a directory. 230 */ 231 static struct direct * 232 fsck_readdir(idesc) 233 struct inodesc *idesc; 234 { 235 struct direct *dp, *ndp; 236 struct bufarea *bp; 237 long size, blksiz, fix, dploc; 238 239 blksiz = idesc->id_numfrags * sblock->fs_fsize; 240 bp = getdirblk(idesc->id_blkno, blksiz); 241 if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 && 242 idesc->id_loc < blksiz) { 243 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 244 if (dircheck(idesc, dp)) 245 goto dpok; 246 if (idesc->id_fix == IGNORE) 247 return (0); 248 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 249 bp = getdirblk(idesc->id_blkno, blksiz); 250 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 251 dp->d_reclen = iswap16(DIRBLKSIZ); 252 dp->d_ino = 0; 253 dp->d_type = 0; 254 dp->d_namlen = 0; 255 dp->d_name[0] = '\0'; 256 if (fix) 257 dirty(bp); 258 else 259 markclean= 0; 260 idesc->id_loc += DIRBLKSIZ; 261 idesc->id_filesize -= DIRBLKSIZ; 262 return (dp); 263 } 264 dpok: 265 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 266 return NULL; 267 dploc = idesc->id_loc; 268 dp = (struct direct *)(bp->b_un.b_buf + dploc); 269 idesc->id_loc += iswap16(dp->d_reclen); 270 idesc->id_filesize -= iswap16(dp->d_reclen); 271 if ((idesc->id_loc % DIRBLKSIZ) == 0) 272 return (dp); 273 ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 274 if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && 275 dircheck(idesc, ndp) == 0) { 276 size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 277 idesc->id_loc += size; 278 idesc->id_filesize -= size; 279 if (idesc->id_fix == IGNORE) 280 return (0); 281 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 282 bp = getdirblk(idesc->id_blkno, blksiz); 283 dp = (struct direct *)(bp->b_un.b_buf + dploc); 284 dp->d_reclen = iswap16(iswap16(dp->d_reclen) + size); 285 if (fix) 286 dirty(bp); 287 else 288 markclean= 0; 289 } 290 return (dp); 291 } 292 293 /* 294 * Verify that a directory entry is valid. 295 * This is a superset of the checks made in the kernel. 296 */ 297 static int 298 dircheck(idesc, dp) 299 struct inodesc *idesc; 300 struct direct *dp; 301 { 302 int size; 303 char *cp; 304 u_char namlen, type; 305 int spaceleft; 306 307 spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 308 if (iswap32(dp->d_ino) >= maxino || 309 dp->d_reclen == 0 || 310 iswap16(dp->d_reclen) > spaceleft || 311 (iswap16(dp->d_reclen) & 0x3) != 0) 312 return (0); 313 if (dp->d_ino == 0) 314 return (1); 315 size = DIRSIZ(!newinofmt, dp, needswap); 316 # if (BYTE_ORDER == LITTLE_ENDIAN) 317 if (!newinofmt && !needswap) { 318 # else 319 if (!newinofmt && needswap) { 320 # endif 321 type = dp->d_namlen; 322 namlen = dp->d_type; 323 } else { 324 namlen = dp->d_namlen; 325 type = dp->d_type; 326 } 327 if (iswap16(dp->d_reclen) < size || 328 idesc->id_filesize < size || 329 /* namlen > MAXNAMLEN || */ 330 type > 15) 331 return (0); 332 for (cp = dp->d_name, size = 0; size < namlen; size++) 333 if (*cp == '\0' || (*cp++ == '/')) 334 return (0); 335 if (*cp != '\0') 336 return (0); 337 return (1); 338 } 339 340 void 341 direrror(ino, errmesg) 342 ino_t ino; 343 char *errmesg; 344 { 345 346 fileerror(ino, ino, errmesg); 347 } 348 349 void 350 fileerror(cwd, ino, errmesg) 351 ino_t cwd, ino; 352 char *errmesg; 353 { 354 struct dinode *dp; 355 char pathbuf[MAXPATHLEN + 1]; 356 357 pwarn("%s ", errmesg); 358 pinode(ino); 359 printf("\n"); 360 getpathname(pathbuf, cwd, ino); 361 if (ino < ROOTINO || ino > maxino) { 362 pfatal("NAME=%s\n", pathbuf); 363 return; 364 } 365 dp = ginode(ino); 366 if (ftypeok(dp)) 367 pfatal("%s=%s\n", 368 (iswap16(dp->di_mode) & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf); 369 else 370 pfatal("NAME=%s\n", pathbuf); 371 } 372 373 void 374 adjust(idesc, lcnt) 375 struct inodesc *idesc; 376 short lcnt; 377 { 378 struct dinode *dp; 379 380 dp = ginode(idesc->id_number); 381 if (iswap16(dp->di_nlink) == lcnt) { 382 if (linkup(idesc->id_number, (ino_t)0) == 0) 383 clri(idesc, "UNREF", 0); 384 } else { 385 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 386 ((iswap16(dp->di_mode) & IFMT) == IFDIR ? "DIR" : 387 "FILE")); 388 pinode(idesc->id_number); 389 printf(" COUNT %d SHOULD BE %d", 390 dp->di_nlink, dp->di_nlink - lcnt); 391 if (preen || usedsoftdep) { 392 if (lcnt < 0) { 393 printf("\n"); 394 pfatal("LINK COUNT INCREASING"); 395 } 396 if (preen) 397 printf(" (ADJUSTED)\n"); 398 } 399 if (preen || reply("ADJUST") == 1) { 400 dp->di_nlink = iswap16(iswap16(dp->di_nlink) - lcnt); 401 inodirty(); 402 } else 403 markclean= 0; 404 } 405 } 406 407 static int 408 mkentry(idesc) 409 struct inodesc *idesc; 410 { 411 struct direct *dirp = idesc->id_dirp; 412 struct direct newent; 413 int newlen, oldlen; 414 415 newent.d_namlen = strlen(idesc->id_name); 416 newlen = DIRSIZ(0, &newent, 0); 417 if (dirp->d_ino != 0) 418 oldlen = DIRSIZ(0, dirp, 0); 419 else 420 oldlen = 0; 421 if (iswap16(dirp->d_reclen) - oldlen < newlen) 422 return (KEEPON); 423 newent.d_reclen = iswap16(iswap16(dirp->d_reclen) - oldlen); 424 dirp->d_reclen = iswap16(oldlen); 425 dirp = (struct direct *)(((char *)dirp) + oldlen); 426 dirp->d_ino = iswap32(idesc->id_parent); /* ino to be entered is in id_parent */ 427 dirp->d_reclen = newent.d_reclen; 428 if (newinofmt) 429 dirp->d_type = typemap[idesc->id_parent]; 430 else 431 dirp->d_type = 0; 432 dirp->d_namlen = newent.d_namlen; 433 memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1); 434 # if (BYTE_ORDER == LITTLE_ENDIAN) 435 /* 436 * If the entry was split, dirscan() will only reverse the byte 437 * order of the original entry, and not the new one, before 438 * writing it back out. So, we reverse the byte order here if 439 * necessary. 440 */ 441 if (oldlen != 0 && !newinofmt && !doinglevel2 && !needswap) { 442 # else 443 if (oldlen != 0 && !newinofmt && !doinglevel2 && needswap) { 444 # endif 445 u_char tmp; 446 447 tmp = dirp->d_namlen; 448 dirp->d_namlen = dirp->d_type; 449 dirp->d_type = tmp; 450 } 451 return (ALTERED|STOP); 452 } 453 454 static int 455 chgino(idesc) 456 struct inodesc *idesc; 457 { 458 struct direct *dirp = idesc->id_dirp; 459 460 if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) 461 return (KEEPON); 462 dirp->d_ino = iswap32(idesc->id_parent); 463 if (newinofmt) 464 dirp->d_type = typemap[idesc->id_parent]; 465 else 466 dirp->d_type = 0; 467 return (ALTERED|STOP); 468 } 469 470 int 471 linkup(orphan, parentdir) 472 ino_t orphan; 473 ino_t parentdir; 474 { 475 struct 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 dp = ginode(orphan); 483 lostdir = (iswap16(dp->di_mode) & IFMT) == IFDIR; 484 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 485 pinode(orphan); 486 if ((preen || usedsoftdep) && dp->di_size == 0) 487 return (0); 488 if (preen) 489 printf(" (RECONNECTED)\n"); 490 else 491 if (reply("RECONNECT") == 0) { 492 markclean = 0; 493 return (0); 494 } 495 if (parentdir != 0) 496 lncntp[parentdir]++; 497 if (lfdir == 0) { 498 dp = ginode(ROOTINO); 499 idesc.id_name = lfname; 500 idesc.id_type = DATA; 501 idesc.id_func = findino; 502 idesc.id_number = ROOTINO; 503 if ((ckinode(dp, &idesc) & FOUND) != 0) { 504 lfdir = idesc.id_parent; 505 } else { 506 pwarn("NO lost+found DIRECTORY"); 507 if (preen || reply("CREATE")) { 508 lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); 509 if (lfdir != 0) { 510 if (makeentry(ROOTINO, lfdir, lfname) != 0) { 511 if (preen) 512 printf(" (CREATED)\n"); 513 } else { 514 freedir(lfdir, ROOTINO); 515 lfdir = 0; 516 if (preen) 517 printf("\n"); 518 } 519 } 520 reparent(lfdir, ROOTINO); 521 } 522 } 523 if (lfdir == 0) { 524 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 525 markclean = 0; 526 return (0); 527 } 528 } 529 dp = ginode(lfdir); 530 if ((iswap16(dp->di_mode) & IFMT) != IFDIR) { 531 pfatal("lost+found IS NOT A DIRECTORY"); 532 if (reply("REALLOCATE") == 0) { 533 markclean = 0; 534 return (0); 535 } 536 oldlfdir = lfdir; 537 lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); 538 if (lfdir == 0) { 539 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 540 markclean = 0; 541 return (0); 542 } 543 if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) { 544 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 545 markclean = 0; 546 return (0); 547 } 548 inodirty(); 549 reparent(lfdir, ROOTINO); 550 idesc.id_type = ADDR; 551 idesc.id_func = pass4check; 552 idesc.id_number = oldlfdir; 553 adjust(&idesc, lncntp[oldlfdir] + 1); 554 lncntp[oldlfdir] = 0; 555 dp = ginode(lfdir); 556 } 557 if (statemap[lfdir] != DFOUND) { 558 pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 559 markclean = 0; 560 return (0); 561 } 562 (void)lftempname(tempname, orphan); 563 if (makeentry(lfdir, orphan, tempname) == 0) { 564 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 565 printf("\n\n"); 566 markclean = 0; 567 return (0); 568 } 569 lncntp[orphan]--; 570 if (lostdir) { 571 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 572 parentdir != (ino_t)-1) 573 (void)makeentry(orphan, lfdir, ".."); 574 dp = ginode(lfdir); 575 dp->di_nlink = iswap16(iswap16(dp->di_nlink) + 1); 576 inodirty(); 577 lncntp[lfdir]++; 578 reparent(orphan, lfdir); 579 pwarn("DIR I=%u CONNECTED. ", orphan); 580 if (parentdir != (ino_t)-1) 581 printf("PARENT WAS I=%u\n", parentdir); 582 if (preen == 0) 583 printf("\n"); 584 } 585 return (1); 586 } 587 588 /* 589 * fix an entry in a directory. 590 */ 591 int 592 changeino(dir, name, newnum) 593 ino_t dir; 594 char *name; 595 ino_t newnum; 596 { 597 struct inodesc idesc; 598 599 memset(&idesc, 0, sizeof(struct inodesc)); 600 idesc.id_type = DATA; 601 idesc.id_func = chgino; 602 idesc.id_number = dir; 603 idesc.id_fix = DONTKNOW; 604 idesc.id_name = name; 605 idesc.id_parent = newnum; /* new value for name */ 606 return (ckinode(ginode(dir), &idesc)); 607 } 608 609 /* 610 * make an entry in a directory 611 */ 612 int 613 makeentry(parent, ino, name) 614 ino_t parent, ino; 615 char *name; 616 { 617 struct dinode *dp; 618 struct inodesc idesc; 619 char pathbuf[MAXPATHLEN + 1]; 620 621 if (parent < ROOTINO || parent >= maxino || 622 ino < ROOTINO || ino >= maxino) 623 return (0); 624 memset(&idesc, 0, sizeof(struct inodesc)); 625 idesc.id_type = DATA; 626 idesc.id_func = mkentry; 627 idesc.id_number = parent; 628 idesc.id_parent = ino; /* this is the inode to enter */ 629 idesc.id_fix = DONTKNOW; 630 idesc.id_name = name; 631 dp = ginode(parent); 632 if (iswap64(dp->di_size) % DIRBLKSIZ) { 633 dp->di_size = iswap64(roundup(iswap64(dp->di_size), DIRBLKSIZ)); 634 inodirty(); 635 } 636 if ((ckinode(dp, &idesc) & ALTERED) != 0) 637 return (1); 638 getpathname(pathbuf, parent, parent); 639 dp = ginode(parent); 640 if (expanddir(dp, pathbuf) == 0) 641 return (0); 642 return (ckinode(dp, &idesc) & ALTERED); 643 } 644 645 /* 646 * Attempt to expand the size of a directory 647 */ 648 static int 649 expanddir(dp, name) 650 struct dinode *dp; 651 char *name; 652 { 653 ufs_daddr_t lastbn, newblk; 654 struct bufarea *bp; 655 char *cp, firstblk[DIRBLKSIZ]; 656 657 lastbn = lblkno(sblock, iswap64(dp->di_size)); 658 if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0) 659 return (0); 660 if ((newblk = allocblk(sblock->fs_frag)) == 0) 661 return (0); 662 dp->di_db[lastbn + 1] = dp->di_db[lastbn]; 663 dp->di_db[lastbn] = iswap32(newblk); 664 dp->di_size = iswap64(iswap64(dp->di_size) + sblock->fs_bsize); 665 dp->di_blocks = iswap32(iswap32(dp->di_blocks) + btodb(sblock->fs_bsize)); 666 bp = getdirblk(iswap32(dp->di_db[lastbn + 1]), 667 (long)dblksize(sblock, dp, lastbn + 1)); 668 if (bp->b_errs) 669 goto bad; 670 memmove(firstblk, bp->b_un.b_buf, DIRBLKSIZ); 671 bp = getdirblk(newblk, sblock->fs_bsize); 672 if (bp->b_errs) 673 goto bad; 674 memmove(bp->b_un.b_buf, firstblk, DIRBLKSIZ); 675 emptydir.dot_reclen = iswap16(DIRBLKSIZ); 676 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 677 cp < &bp->b_un.b_buf[sblock->fs_bsize]; 678 cp += DIRBLKSIZ) 679 memmove(cp, &emptydir, sizeof emptydir); 680 dirty(bp); 681 bp = getdirblk(iswap32(dp->di_db[lastbn + 1]), 682 (long)dblksize(sblock, dp, lastbn + 1)); 683 if (bp->b_errs) 684 goto bad; 685 memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir); 686 pwarn("NO SPACE LEFT IN %s", name); 687 if (preen) 688 printf(" (EXPANDED)\n"); 689 else if (reply("EXPAND") == 0) 690 goto bad; 691 dirty(bp); 692 inodirty(); 693 return (1); 694 bad: 695 dp->di_db[lastbn] = dp->di_db[lastbn + 1]; 696 dp->di_db[lastbn + 1] = 0; 697 dp->di_size = iswap64(iswap64(dp->di_size) - sblock->fs_bsize); 698 dp->di_blocks = iswap32(iswap32(dp->di_blocks) - btodb(sblock->fs_bsize)); 699 freeblk(newblk, sblock->fs_frag); 700 markclean = 0; 701 return (0); 702 } 703 704 /* 705 * allocate a new directory 706 */ 707 ino_t 708 allocdir(parent, request, mode) 709 ino_t parent, request; 710 int mode; 711 { 712 ino_t ino; 713 char *cp; 714 struct dinode *dp; 715 struct bufarea *bp; 716 struct dirtemplate *dirp; 717 718 ino = allocino(request, IFDIR|mode); 719 dirhead.dot_reclen = iswap16(12); 720 dirhead.dotdot_reclen = iswap16(DIRBLKSIZ - 12); 721 odirhead.dot_reclen = iswap16(12); 722 odirhead.dotdot_reclen = iswap16(DIRBLKSIZ - 12); 723 odirhead.dot_namlen = iswap16(1); 724 odirhead.dotdot_namlen = iswap16(2); 725 if (newinofmt) 726 dirp = &dirhead; 727 else 728 dirp = (struct dirtemplate *)&odirhead; 729 dirp->dot_ino = iswap32(ino); 730 dirp->dotdot_ino = iswap32(parent); 731 dp = ginode(ino); 732 bp = getdirblk(iswap32(dp->di_db[0]), sblock->fs_fsize); 733 if (bp->b_errs) { 734 freeino(ino); 735 return (0); 736 } 737 memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate)); 738 emptydir.dot_reclen = iswap16(DIRBLKSIZ); 739 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 740 cp < &bp->b_un.b_buf[sblock->fs_fsize]; 741 cp += DIRBLKSIZ) 742 memmove(cp, &emptydir, sizeof emptydir); 743 dirty(bp); 744 dp->di_nlink = iswap16(2); 745 inodirty(); 746 if (ino == ROOTINO) { 747 lncntp[ino] = iswap16(dp->di_nlink); 748 cacheino(dp, ino); 749 return(ino); 750 } 751 if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { 752 freeino(ino); 753 return (0); 754 } 755 cacheino(dp, ino); 756 statemap[ino] = statemap[parent]; 757 if (statemap[ino] == DSTATE) { 758 lncntp[ino] = iswap16(dp->di_nlink); 759 lncntp[parent]++; 760 } 761 dp = ginode(parent); 762 dp->di_nlink = iswap16(iswap16(dp->di_nlink) + 1); 763 inodirty(); 764 return (ino); 765 } 766 767 /* 768 * free a directory inode 769 */ 770 static void 771 freedir(ino, parent) 772 ino_t ino, parent; 773 { 774 struct dinode *dp; 775 776 if (ino != parent) { 777 dp = ginode(parent); 778 dp->di_nlink = iswap16(iswap16(dp->di_nlink) -1); 779 inodirty(); 780 } 781 freeino(ino); 782 } 783 784 /* 785 * generate a temporary name for the lost+found directory. 786 */ 787 static int 788 lftempname(bufp, ino) 789 char *bufp; 790 ino_t ino; 791 { 792 ino_t in; 793 char *cp; 794 int namlen; 795 796 cp = bufp + 2; 797 for (in = maxino; in > 0; in /= 10) 798 cp++; 799 *--cp = 0; 800 namlen = cp - bufp; 801 in = ino; 802 while (cp > bufp) { 803 *--cp = (in % 10) + '0'; 804 in /= 10; 805 } 806 *cp = '#'; 807 return (namlen); 808 } 809 810 /* 811 * Get a directory block. 812 * Insure that it is held until another is requested. 813 */ 814 static struct bufarea * 815 getdirblk(blkno, size) 816 ufs_daddr_t blkno; 817 long size; 818 { 819 820 if (pdirbp != 0) 821 pdirbp->b_flags &= ~B_INUSE; 822 pdirbp = getdatablk(blkno, size); 823 return (pdirbp); 824 } 825