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