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