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