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