1/* $NetBSD$ */ 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$"); 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 = 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 = 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 = DIRBLKSIZ - 12, 83 .dotdot_namlen = 2, 84 .dotdot_name = "..", 85}; 86 87static int chgino(struct inodesc *); 88static int dircheck(struct inodesc *, struct direct *); 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/* 148 * Scan each entry in a directory block. 149 */ 150int 151dirscan(struct inodesc *idesc) 152{ 153 struct direct *dp; 154 struct bufarea *bp; 155 int dsize, n; 156 long blksiz; 157#if DIRBLKSIZ > APPLEUFS_DIRBLKSIZ 158 char dbuf[DIRBLKSIZ]; 159#else 160 char dbuf[APPLEUFS_DIRBLKSIZ]; 161#endif 162 163 if (idesc->id_type != DATA) 164 errexit("wrong type to dirscan %d", idesc->id_type); 165 if (idesc->id_entryno == 0 && 166 (idesc->id_filesize & (dirblksiz - 1)) != 0) 167 idesc->id_filesize = roundup(idesc->id_filesize, dirblksiz); 168 blksiz = idesc->id_numfrags * sblock->fs_fsize; 169 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 170 idesc->id_filesize -= blksiz; 171 return (SKIP); 172 } 173 174 /* 175 * If we are are swapping byte order in directory entries, just swap 176 * this block and return. 177 */ 178 if (do_dirswap) { 179 int off; 180 bp = getdirblk(idesc->id_blkno, blksiz); 181 for (off = 0; off < blksiz; off += iswap16(dp->d_reclen)) { 182 dp = (struct direct *)(bp->b_un.b_buf + off); 183 dp->d_ino = bswap32(dp->d_ino); 184 dp->d_reclen = bswap16(dp->d_reclen); 185 if (!newinofmt) { 186 u_int8_t tmp = dp->d_namlen; 187 dp->d_namlen = dp->d_type; 188 dp->d_type = tmp; 189 } 190 if (dp->d_reclen == 0) 191 break; 192 } 193 dirty(bp); 194 idesc->id_filesize -= blksiz; 195 return (idesc->id_filesize > 0 ? KEEPON : STOP); 196 } 197 198 idesc->id_loc = 0; 199 for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 200 dsize = iswap16(dp->d_reclen); 201 if (dsize > (int)sizeof dbuf) 202 dsize = sizeof dbuf; 203 memmove(dbuf, dp, (size_t)dsize); 204# if (BYTE_ORDER == LITTLE_ENDIAN) 205 if (!newinofmt && !needswap) { 206# else 207 if (!newinofmt && needswap) { 208# endif 209 struct direct *tdp = (struct direct *)dbuf; 210 u_char tmp; 211 212 tmp = tdp->d_namlen; 213 tdp->d_namlen = tdp->d_type; 214 tdp->d_type = tmp; 215 } 216 idesc->id_dirp = (struct direct *)dbuf; 217 if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 218# if (BYTE_ORDER == LITTLE_ENDIAN) 219 if (!newinofmt && !doinglevel2 && !needswap) { 220# else 221 if (!newinofmt && !doinglevel2 && needswap) { 222# endif 223 struct direct *tdp; 224 u_char tmp; 225 226 tdp = (struct direct *)dbuf; 227 tmp = tdp->d_namlen; 228 tdp->d_namlen = tdp->d_type; 229 tdp->d_type = tmp; 230 } 231 bp = getdirblk(idesc->id_blkno, blksiz); 232 memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf, 233 (size_t)dsize); 234 dirty(bp); 235 sbdirty(); 236 } 237 if (n & STOP) 238 return (n); 239 } 240 return (idesc->id_filesize > 0 ? KEEPON : STOP); 241} 242 243/* 244 * get next entry in a directory. 245 */ 246static struct direct * 247fsck_readdir(struct inodesc *idesc) 248{ 249 struct direct *dp, *ndp; 250 struct bufarea *bp; 251 long size, blksiz, fix, dploc; 252 253 blksiz = idesc->id_numfrags * sblock->fs_fsize; 254 bp = getdirblk(idesc->id_blkno, blksiz); 255 if (idesc->id_loc % dirblksiz == 0 && idesc->id_filesize > 0 && 256 idesc->id_loc < blksiz) { 257 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 258 if (dircheck(idesc, dp)) 259 goto dpok; 260 if (idesc->id_fix == IGNORE) 261 return (0); 262 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 263 bp = getdirblk(idesc->id_blkno, blksiz); 264 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 265 dp->d_reclen = iswap16(dirblksiz); 266 dp->d_ino = 0; 267 dp->d_type = 0; 268 dp->d_namlen = 0; 269 dp->d_name[0] = '\0'; 270 if (fix) 271 dirty(bp); 272 else 273 markclean = 0; 274 idesc->id_loc += dirblksiz; 275 idesc->id_filesize -= dirblksiz; 276 return (dp); 277 } 278dpok: 279 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 280 return NULL; 281 dploc = idesc->id_loc; 282 dp = (struct direct *)(bp->b_un.b_buf + dploc); 283 idesc->id_loc += iswap16(dp->d_reclen); 284 idesc->id_filesize -= iswap16(dp->d_reclen); 285 if ((idesc->id_loc % dirblksiz) == 0) 286 return (dp); 287 ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 288 if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && 289 dircheck(idesc, ndp) == 0) { 290 size = dirblksiz - (idesc->id_loc % dirblksiz); 291 idesc->id_loc += size; 292 idesc->id_filesize -= size; 293 if (idesc->id_fix == IGNORE) 294 return (0); 295 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 296 bp = getdirblk(idesc->id_blkno, blksiz); 297 dp = (struct direct *)(bp->b_un.b_buf + dploc); 298 dp->d_reclen = iswap16(iswap16(dp->d_reclen) + size); 299 if (fix) 300 dirty(bp); 301 else 302 markclean = 0; 303 } 304 return (dp); 305} 306 307/* 308 * Verify that a directory entry is valid. 309 * This is a superset of the checks made in the kernel. 310 */ 311static int 312dircheck(struct inodesc *idesc, struct direct *dp) 313{ 314 int size; 315 char *cp; 316 u_char namlen, type; 317 int spaceleft; 318 319 spaceleft = dirblksiz - (idesc->id_loc % dirblksiz); 320 if (iswap32(dp->d_ino) >= maxino || 321 dp->d_reclen == 0 || 322 iswap16(dp->d_reclen) > spaceleft || 323 (iswap16(dp->d_reclen) & 0x3) != 0) 324 return (0); 325 if (dp->d_ino == 0) 326 return (1); 327 size = DIRSIZ(!newinofmt, dp, needswap); 328# if (BYTE_ORDER == LITTLE_ENDIAN) 329 if (!newinofmt && !needswap) { 330# else 331 if (!newinofmt && needswap) { 332# endif 333 type = dp->d_namlen; 334 namlen = dp->d_type; 335 } else { 336 namlen = dp->d_namlen; 337 type = dp->d_type; 338 } 339 if (iswap16(dp->d_reclen) < size || 340 idesc->id_filesize < size || 341 /* namlen > MAXNAMLEN || */ 342 type > 15) 343 return (0); 344 for (cp = dp->d_name, size = 0; size < namlen; size++) 345 if (*cp == '\0' || (*cp++ == '/')) 346 return (0); 347 if (*cp != '\0') 348 return (0); 349 return (1); 350} 351 352void 353direrror(ino_t ino, const char *errmesg) 354{ 355 356 fileerror(ino, ino, errmesg); 357} 358 359void 360fileerror(ino_t cwd, ino_t ino, const char *errmesg) 361{ 362 union dinode *dp; 363 char pathbuf[MAXPATHLEN + 1]; 364 uint16_t mode; 365 366 pwarn("%s ", errmesg); 367 pinode(ino); 368 printf("\n"); 369 getpathname(pathbuf, sizeof(pathbuf), cwd, ino); 370 if (ino < ROOTINO || ino > maxino) { 371 pfatal("NAME=%s\n", pathbuf); 372 return; 373 } 374 dp = ginode(ino); 375 if (ftypeok(dp)) { 376 mode = DIP(dp, mode); 377 pfatal("%s=%s\n", 378 (iswap16(mode) & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf); 379 } 380 else 381 pfatal("NAME=%s\n", pathbuf); 382} 383 384void 385adjust(struct inodesc *idesc, int 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_SET(dp, nlink, iswap16(nlink - lcnt)); 439 inodirty(); 440 } else 441 markclean = 0; 442 } 443} 444 445static int 446mkentry(struct inodesc *idesc) 447{ 448 struct direct *dirp = idesc->id_dirp; 449 struct direct newent; 450 int newlen, oldlen; 451 452 newent.d_namlen = strlen(idesc->id_name); 453 newlen = DIRSIZ(0, &newent, 0); 454 if (dirp->d_ino != 0) 455 oldlen = DIRSIZ(0, dirp, 0); 456 else 457 oldlen = 0; 458 if (iswap16(dirp->d_reclen) - oldlen < newlen) 459 return (KEEPON); 460 newent.d_reclen = iswap16(iswap16(dirp->d_reclen) - oldlen); 461 dirp->d_reclen = iswap16(oldlen); 462 dirp = (struct direct *)(((char *)dirp) + oldlen); 463 /* ino to be entered is in id_parent */ 464 dirp->d_ino = iswap32(idesc->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 492static int 493chgino(struct inodesc *idesc) 494{ 495 struct direct *dirp = idesc->id_dirp; 496 497 if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) 498 return (KEEPON); 499 dirp->d_ino = iswap32(idesc->id_parent); 500 if (newinofmt) 501 dirp->d_type = inoinfo(idesc->id_parent)->ino_type; 502 else 503 dirp->d_type = 0; 504 return (ALTERED|STOP); 505} 506 507int 508linkup(ino_t orphan, ino_t parentdir, 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 idesc.id_uid = iswap32(DIP(dp, uid)); 543 idesc.id_gid = iswap32(DIP(dp, gid)); 544 if ((ckinode(dp, &idesc) & FOUND) != 0) { 545 lfdir = idesc.id_parent; 546 } else { 547 pwarn("NO lost+found DIRECTORY"); 548 if (preen || reply("CREATE")) { 549 lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); 550 if (lfdir != 0) { 551 if (makeentry(ROOTINO, lfdir, lfname) != 0) { 552 numdirs++; 553 if (preen) 554 printf(" (CREATED)\n"); 555 } else { 556 freedir(lfdir, ROOTINO); 557 lfdir = 0; 558 if (preen) 559 printf("\n"); 560 } 561 } 562 if (lfdir != 0) { 563 reparent(lfdir, ROOTINO); 564 } 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_SET(dp, nlink, iswap16(iswap16(nlink) + 1)); 623 inodirty(); 624 inoinfo(lfdir)->ino_linkcnt++; 625 reparent(orphan, lfdir); 626 pwarn("DIR I=%llu CONNECTED. ", (unsigned long long)orphan); 627 if (parentdir != (ino_t)-1) 628 printf("PARENT WAS I=%llu\n", 629 (unsigned long long)parentdir); 630 if (preen == 0) 631 printf("\n"); 632 } 633 return (1); 634} 635 636/* 637 * fix an entry in a directory. 638 */ 639int 640changeino(ino_t dir, const char *name, ino_t newnum) 641{ 642 struct inodesc idesc; 643 union dinode *dp; 644 645 dp = ginode(dir); 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 idesc.id_uid = iswap32(DIP(dp, uid)); 654 idesc.id_gid = iswap32(DIP(dp, gid)); 655 return (ckinode(dp, &idesc)); 656} 657 658/* 659 * make an entry in a directory 660 */ 661int 662makeentry(ino_t parent, ino_t ino, const 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 dp = ginode(parent); 672 memset(&idesc, 0, sizeof(struct inodesc)); 673 idesc.id_type = DATA; 674 idesc.id_func = mkentry; 675 idesc.id_number = parent; 676 idesc.id_parent = ino; /* this is the inode to enter */ 677 idesc.id_fix = DONTKNOW; 678 idesc.id_name = name; 679 idesc.id_uid = iswap32(DIP(dp, uid)); 680 idesc.id_gid = iswap32(DIP(dp, gid)); 681 if (iswap64(DIP(dp, size)) % dirblksiz) { 682 DIP_SET(dp, size, 683 iswap64(roundup(iswap64(DIP(dp, size)), dirblksiz))); 684 inodirty(); 685 } 686 if ((ckinode(dp, &idesc) & ALTERED) != 0) 687 return (1); 688 getpathname(pathbuf, sizeof(pathbuf), parent, parent); 689 dp = ginode(parent); 690 if (expanddir(dp, pathbuf) == 0) 691 return (0); 692 update_uquot(idesc.id_number, idesc.id_uid, idesc.id_gid, 693 btodb(sblock->fs_bsize), 0); 694 return (ckinode(dp, &idesc) & ALTERED); 695} 696 697/* 698 * Attempt to expand the size of a directory 699 */ 700static int 701expanddir(union dinode *dp, char *name) 702{ 703 daddr_t lastbn, newblk, dirblk; 704 struct bufarea *bp; 705 char *cp; 706#if DIRBLKSIZ > APPLEUFS_DIRBLKSIZ 707 char firstblk[DIRBLKSIZ]; 708#else 709 char firstblk[APPLEUFS_DIRBLKSIZ]; 710#endif 711 struct ufs1_dinode *dp1 = NULL; 712 struct ufs2_dinode *dp2 = NULL; 713 714 if (is_ufs2) 715 dp2 = &dp->dp2; 716 else 717 dp1 = &dp->dp1; 718 719 lastbn = lblkno(sblock, iswap64(DIP(dp, size))); 720 if (lastbn >= NDADDR - 1 || DIP(dp, db[lastbn]) == 0 || 721 DIP(dp, size) == 0) 722 return (0); 723 if ((newblk = allocblk(sblock->fs_frag)) == 0) 724 return (0); 725 if (is_ufs2) { 726 dp2->di_db[lastbn + 1] = dp2->di_db[lastbn]; 727 dp2->di_db[lastbn] = iswap64(newblk); 728 dp2->di_size = iswap64(iswap64(dp2->di_size)+sblock->fs_bsize); 729 dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) + 730 btodb(sblock->fs_bsize)); 731 dirblk = iswap64(dp2->di_db[lastbn + 1]); 732 } else { 733 dp1->di_db[lastbn + 1] = dp1->di_db[lastbn]; 734 dp1->di_db[lastbn] = iswap32((int32_t)newblk); 735 dp1->di_size = iswap64(iswap64(dp1->di_size)+sblock->fs_bsize); 736 dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) + 737 btodb(sblock->fs_bsize)); 738 dirblk = iswap32(dp1->di_db[lastbn + 1]); 739 } 740 bp = getdirblk(dirblk, sblksize(sblock, (daddr_t)DIP(dp, size), lastbn + 1)); 741 if (bp->b_errs) 742 goto bad; 743 memmove(firstblk, bp->b_un.b_buf, dirblksiz); 744 bp = getdirblk(newblk, sblock->fs_bsize); 745 if (bp->b_errs) 746 goto bad; 747 memmove(bp->b_un.b_buf, firstblk, dirblksiz); 748 emptydir.dot_reclen = iswap16(dirblksiz); 749 for (cp = &bp->b_un.b_buf[dirblksiz]; 750 cp < &bp->b_un.b_buf[sblock->fs_bsize]; 751 cp += dirblksiz) 752 memmove(cp, &emptydir, sizeof emptydir); 753 dirty(bp); 754 bp = getdirblk(dirblk, sblksize(sblock, (daddr_t)DIP(dp, size), lastbn + 1)); 755 if (bp->b_errs) 756 goto bad; 757 memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir); 758 pwarn("NO SPACE LEFT IN %s", name); 759 if (preen) 760 printf(" (EXPANDED)\n"); 761 else if (reply("EXPAND") == 0) 762 goto bad; 763 dirty(bp); 764 inodirty(); 765 return (1); 766bad: 767 if (is_ufs2) { 768 dp2->di_db[lastbn] = dp2->di_db[lastbn + 1]; 769 dp2->di_db[lastbn + 1] = 0; 770 dp2->di_size = iswap64(iswap64(dp2->di_size)-sblock->fs_bsize); 771 dp2->di_blocks = iswap64(iswap64(dp2->di_blocks) - 772 btodb(sblock->fs_bsize)); 773 } else { 774 dp1->di_db[lastbn] = dp1->di_db[lastbn + 1]; 775 dp1->di_db[lastbn + 1] = 0; 776 dp1->di_size = iswap64(iswap64(dp1->di_size)-sblock->fs_bsize); 777 dp1->di_blocks = iswap32(iswap32(dp1->di_blocks) - 778 btodb(sblock->fs_bsize)); 779 } 780 freeblk(newblk, sblock->fs_frag); 781 markclean = 0; 782 return (0); 783} 784 785/* 786 * allocate a new directory 787 */ 788ino_t 789allocdir(ino_t parent, ino_t request, 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 if (ino < ROOTINO) 801 return 0; 802 update_uquot(ino, 0, 0, btodb(sblock->fs_fsize), 1); 803 dirhead.dot_reclen = iswap16(12); 804 dirhead.dotdot_reclen = iswap16(dirblksiz - 12); 805 odirhead.dot_reclen = iswap16(12); 806 odirhead.dotdot_reclen = iswap16(dirblksiz - 12); 807 odirhead.dot_namlen = iswap16(1); 808 odirhead.dotdot_namlen = iswap16(2); 809 if (newinofmt) 810 dirp = &dirhead; 811 else 812 dirp = (struct dirtemplate *)&odirhead; 813 dirp->dot_ino = iswap32(ino); 814 dirp->dotdot_ino = iswap32(parent); 815 dp = ginode(ino); 816 dirblk = is_ufs2 ? iswap64(dp->dp2.di_db[0]) 817 : iswap32(dp->dp1.di_db[0]); 818 bp = getdirblk(dirblk, sblock->fs_fsize); 819 if (bp->b_errs) { 820 freeino(ino); 821 return (0); 822 } 823 memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate)); 824 emptydir.dot_reclen = iswap16(dirblksiz); 825 for (cp = &bp->b_un.b_buf[dirblksiz]; 826 cp < &bp->b_un.b_buf[sblock->fs_fsize]; 827 cp += dirblksiz) 828 memmove(cp, &emptydir, sizeof emptydir); 829 dirty(bp); 830 DIP_SET(dp, nlink, iswap16(2)); 831 inodirty(); 832 if (ino == ROOTINO) { 833 inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink)); 834 cacheino(dp, ino); 835 return(ino); 836 } 837 if (inoinfo(parent)->ino_state != DSTATE && 838 inoinfo(parent)->ino_state != DFOUND) { 839 freeino(ino); 840 return (0); 841 } 842 cacheino(dp, ino); 843 inp = getinoinfo(ino); 844 inp->i_parent = parent; 845 inp->i_dotdot = parent; 846 inoinfo(ino)->ino_state = inoinfo(parent)->ino_state; 847 if (inoinfo(ino)->ino_state == DSTATE) { 848 inoinfo(ino)->ino_linkcnt = iswap16(DIP(dp, nlink)); 849 inoinfo(parent)->ino_linkcnt++; 850 } 851 dp = ginode(parent); 852 DIP_SET(dp, nlink, iswap16(iswap16(DIP(dp, nlink)) + 1)); 853 inodirty(); 854 return (ino); 855} 856 857/* 858 * free a directory inode 859 */ 860static void 861freedir(ino_t ino, ino_t parent) 862{ 863 union dinode *dp; 864 865 if (ino != parent) { 866 dp = ginode(parent); 867 DIP_SET(dp, nlink, iswap16(iswap16(DIP(dp, nlink)) - 1)); 868 inodirty(); 869 } 870 freeino(ino); 871} 872 873/* 874 * generate a temporary name for the lost+found directory. 875 */ 876static int 877lftempname(char *bufp, 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 */ 901static struct bufarea * 902getdirblk(daddr_t blkno, long size) 903{ 904 905 if (pdirbp != 0) 906 pdirbp->b_flags &= ~B_INUSE; 907 pdirbp = getdatablk(blkno, size); 908 return (pdirbp); 909} 910