1 /* 2 * Copyright (c) 1980, 1986, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #if 0 31 #ifndef lint 32 static const char sccsid[] = "@(#)dir.c 8.8 (Berkeley) 4/28/95"; 33 #endif /* not lint */ 34 #endif 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include <sys/param.h> 39 #include <sys/time.h> 40 #include <sys/types.h> 41 #include <sys/sysctl.h> 42 43 #include <ufs/ufs/dinode.h> 44 #include <ufs/ufs/dir.h> 45 #include <ufs/ffs/fs.h> 46 47 #include <err.h> 48 #include <string.h> 49 50 #include "fsck.h" 51 52 static struct dirtemplate emptydir = { 53 0, DIRBLKSIZ, DT_UNKNOWN, 0, "", 54 0, 0, DT_UNKNOWN, 0, "" 55 }; 56 static struct dirtemplate dirhead = { 57 0, 12, DT_DIR, 1, ".", 58 0, DIRBLKSIZ - 12, DT_DIR, 2, ".." 59 }; 60 61 static int chgino(struct inodesc *); 62 static int dircheck(struct inodesc *, struct direct *); 63 static int expanddir(union dinode *dp, char *name); 64 static void freedir(ino_t ino, ino_t parent); 65 static struct direct *fsck_readdir(struct inodesc *); 66 static struct bufarea *getdirblk(ufs2_daddr_t blkno, long size); 67 static int lftempname(char *bufp, ino_t ino); 68 static int mkentry(struct inodesc *); 69 70 /* 71 * Propagate connected state through the tree. 72 */ 73 void 74 propagate(void) 75 { 76 struct inoinfo **inpp, *inp; 77 struct inoinfo **inpend; 78 long change; 79 80 inpend = &inpsort[inplast]; 81 do { 82 change = 0; 83 for (inpp = inpsort; inpp < inpend; inpp++) { 84 inp = *inpp; 85 if (inp->i_parent == 0) 86 continue; 87 if (inoinfo(inp->i_parent)->ino_state == DFOUND && 88 INO_IS_DUNFOUND(inp->i_number)) { 89 inoinfo(inp->i_number)->ino_state = DFOUND; 90 change++; 91 } 92 } 93 } while (change > 0); 94 } 95 96 /* 97 * Scan each entry in a directory block. 98 */ 99 int 100 dirscan(struct inodesc *idesc) 101 { 102 struct direct *dp; 103 struct bufarea *bp; 104 u_int dsize, n; 105 long blksiz; 106 char dbuf[DIRBLKSIZ]; 107 108 if (idesc->id_type != DATA) 109 errx(EEXIT, "wrong type to dirscan %d", idesc->id_type); 110 if (idesc->id_entryno == 0 && 111 (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) 112 idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); 113 blksiz = idesc->id_numfrags * sblock.fs_fsize; 114 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 115 idesc->id_filesize -= blksiz; 116 return (SKIP); 117 } 118 idesc->id_loc = 0; 119 for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 120 dsize = dp->d_reclen; 121 if (dsize > sizeof(dbuf)) 122 dsize = sizeof(dbuf); 123 memmove(dbuf, dp, (size_t)dsize); 124 idesc->id_dirp = (struct direct *)dbuf; 125 if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 126 bp = getdirblk(idesc->id_blkno, blksiz); 127 memmove(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf, 128 (size_t)dsize); 129 dirty(bp); 130 sbdirty(); 131 rerun = 1; 132 } 133 if (n & STOP) 134 return (n); 135 } 136 return (idesc->id_filesize > 0 ? KEEPON : STOP); 137 } 138 139 /* 140 * get next entry in a directory. 141 */ 142 static struct direct * 143 fsck_readdir(struct inodesc *idesc) 144 { 145 struct direct *dp, *ndp; 146 struct bufarea *bp; 147 long size, blksiz, fix, dploc; 148 149 blksiz = idesc->id_numfrags * sblock.fs_fsize; 150 bp = getdirblk(idesc->id_blkno, blksiz); 151 if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 && 152 idesc->id_loc < blksiz) { 153 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 154 if (dircheck(idesc, dp)) 155 goto dpok; 156 if (idesc->id_fix == IGNORE) 157 return (0); 158 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 159 bp = getdirblk(idesc->id_blkno, blksiz); 160 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 161 dp->d_reclen = DIRBLKSIZ; 162 dp->d_ino = 0; 163 dp->d_type = 0; 164 dp->d_namlen = 0; 165 dp->d_name[0] = '\0'; 166 if (fix) 167 dirty(bp); 168 idesc->id_loc += DIRBLKSIZ; 169 idesc->id_filesize -= DIRBLKSIZ; 170 return (dp); 171 } 172 dpok: 173 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 174 return NULL; 175 dploc = idesc->id_loc; 176 dp = (struct direct *)(bp->b_un.b_buf + dploc); 177 idesc->id_loc += dp->d_reclen; 178 idesc->id_filesize -= dp->d_reclen; 179 if ((idesc->id_loc % DIRBLKSIZ) == 0) 180 return (dp); 181 ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 182 if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && 183 dircheck(idesc, ndp) == 0) { 184 size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 185 idesc->id_loc += size; 186 idesc->id_filesize -= size; 187 if (idesc->id_fix == IGNORE) 188 return (0); 189 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 190 bp = getdirblk(idesc->id_blkno, blksiz); 191 dp = (struct direct *)(bp->b_un.b_buf + dploc); 192 dp->d_reclen += size; 193 if (fix) 194 dirty(bp); 195 } 196 return (dp); 197 } 198 199 /* 200 * Verify that a directory entry is valid. 201 * This is a superset of the checks made in the kernel. 202 */ 203 static int 204 dircheck(struct inodesc *idesc, struct direct *dp) 205 { 206 size_t size; 207 char *cp; 208 u_char type; 209 u_int8_t namlen; 210 int spaceleft; 211 212 spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 213 if (dp->d_reclen == 0 || 214 dp->d_reclen > spaceleft || 215 (dp->d_reclen & 0x3) != 0) 216 goto bad; 217 if (dp->d_ino == 0) 218 return (1); 219 size = DIRSIZ(0, dp); 220 namlen = dp->d_namlen; 221 type = dp->d_type; 222 if (dp->d_reclen < size || 223 idesc->id_filesize < size || 224 namlen == 0 || 225 type > 15) 226 goto bad; 227 for (cp = dp->d_name, size = 0; size < namlen; size++) 228 if (*cp == '\0' || (*cp++ == '/')) 229 goto bad; 230 if (*cp != '\0') 231 goto bad; 232 return (1); 233 bad: 234 if (debug) 235 printf("Bad dir: ino %d reclen %d namlen %d type %d name %s\n", 236 dp->d_ino, dp->d_reclen, dp->d_namlen, dp->d_type, 237 dp->d_name); 238 return (0); 239 } 240 241 void 242 direrror(ino_t ino, const char *errmesg) 243 { 244 245 fileerror(ino, ino, errmesg); 246 } 247 248 void 249 fileerror(ino_t cwd, ino_t ino, const char *errmesg) 250 { 251 union dinode *dp; 252 char pathbuf[MAXPATHLEN + 1]; 253 254 pwarn("%s ", errmesg); 255 pinode(ino); 256 printf("\n"); 257 getpathname(pathbuf, cwd, ino); 258 if (ino < ROOTINO || ino > maxino) { 259 pfatal("NAME=%s\n", pathbuf); 260 return; 261 } 262 dp = ginode(ino); 263 if (ftypeok(dp)) 264 pfatal("%s=%s\n", 265 (DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE", 266 pathbuf); 267 else 268 pfatal("NAME=%s\n", pathbuf); 269 } 270 271 void 272 adjust(struct inodesc *idesc, int lcnt) 273 { 274 union dinode *dp; 275 int saveresolved; 276 277 dp = ginode(idesc->id_number); 278 if (DIP(dp, di_nlink) == lcnt) { 279 /* 280 * If we have not hit any unresolved problems, are running 281 * in preen mode, and are on a file system using soft updates, 282 * then just toss any partially allocated files. 283 */ 284 if (resolved && (preen || bkgrdflag) && usedsoftdep) { 285 clri(idesc, "UNREF", 1); 286 return; 287 } else { 288 /* 289 * The file system can be marked clean even if 290 * a file is not linked up, but is cleared. 291 * Hence, resolved should not be cleared when 292 * linkup is answered no, but clri is answered yes. 293 */ 294 saveresolved = resolved; 295 if (linkup(idesc->id_number, (ino_t)0, NULL) == 0) { 296 resolved = saveresolved; 297 clri(idesc, "UNREF", 0); 298 return; 299 } 300 /* 301 * Account for the new reference created by linkup(). 302 */ 303 dp = ginode(idesc->id_number); 304 lcnt--; 305 } 306 } 307 if (lcnt != 0) { 308 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 309 ((DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE")); 310 pinode(idesc->id_number); 311 printf(" COUNT %d SHOULD BE %d", 312 DIP(dp, di_nlink), DIP(dp, di_nlink) - lcnt); 313 if (preen || usedsoftdep) { 314 if (lcnt < 0) { 315 printf("\n"); 316 pfatal("LINK COUNT INCREASING"); 317 } 318 if (preen) 319 printf(" (ADJUSTED)\n"); 320 } 321 if (preen || reply("ADJUST") == 1) { 322 if (bkgrdflag == 0) { 323 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - lcnt); 324 inodirty(); 325 } else { 326 cmd.value = idesc->id_number; 327 cmd.size = -lcnt; 328 if (debug) 329 printf("adjrefcnt ino %ld amt %lld\n", 330 (long)cmd.value, 331 (long long)cmd.size); 332 if (sysctl(adjrefcnt, MIBSIZE, 0, 0, 333 &cmd, sizeof cmd) == -1) 334 rwerror("ADJUST INODE", cmd.value); 335 } 336 } 337 } 338 } 339 340 static int 341 mkentry(struct inodesc *idesc) 342 { 343 struct direct *dirp = idesc->id_dirp; 344 struct direct newent; 345 int newlen, oldlen; 346 347 newent.d_namlen = strlen(idesc->id_name); 348 newlen = DIRSIZ(0, &newent); 349 if (dirp->d_ino != 0) 350 oldlen = DIRSIZ(0, dirp); 351 else 352 oldlen = 0; 353 if (dirp->d_reclen - oldlen < newlen) 354 return (KEEPON); 355 newent.d_reclen = dirp->d_reclen - oldlen; 356 dirp->d_reclen = oldlen; 357 dirp = (struct direct *)(((char *)dirp) + oldlen); 358 dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ 359 dirp->d_reclen = newent.d_reclen; 360 dirp->d_type = inoinfo(idesc->id_parent)->ino_type; 361 dirp->d_namlen = newent.d_namlen; 362 memmove(dirp->d_name, idesc->id_name, (size_t)newent.d_namlen + 1); 363 return (ALTERED|STOP); 364 } 365 366 static int 367 chgino(struct inodesc *idesc) 368 { 369 struct direct *dirp = idesc->id_dirp; 370 371 if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) 372 return (KEEPON); 373 dirp->d_ino = idesc->id_parent; 374 dirp->d_type = inoinfo(idesc->id_parent)->ino_type; 375 return (ALTERED|STOP); 376 } 377 378 int 379 linkup(ino_t orphan, ino_t parentdir, char *name) 380 { 381 union dinode *dp; 382 int lostdir; 383 ino_t oldlfdir; 384 struct inodesc idesc; 385 char tempname[BUFSIZ]; 386 387 memset(&idesc, 0, sizeof(struct inodesc)); 388 dp = ginode(orphan); 389 lostdir = (DIP(dp, di_mode) & IFMT) == IFDIR; 390 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 391 pinode(orphan); 392 if (preen && DIP(dp, di_size) == 0) 393 return (0); 394 if (cursnapshot != 0) { 395 pfatal("FILE LINKUP IN SNAPSHOT"); 396 return (0); 397 } 398 if (preen) 399 printf(" (RECONNECTED)\n"); 400 else 401 if (reply("RECONNECT") == 0) 402 return (0); 403 if (lfdir == 0) { 404 dp = ginode(ROOTINO); 405 idesc.id_name = strdup(lfname); 406 idesc.id_type = DATA; 407 idesc.id_func = findino; 408 idesc.id_number = ROOTINO; 409 if ((ckinode(dp, &idesc) & FOUND) != 0) { 410 lfdir = idesc.id_parent; 411 } else { 412 pwarn("NO lost+found DIRECTORY"); 413 if (preen || reply("CREATE")) { 414 lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); 415 if (lfdir != 0) { 416 if (makeentry(ROOTINO, lfdir, lfname) != 0) { 417 numdirs++; 418 if (preen) 419 printf(" (CREATED)\n"); 420 } else { 421 freedir(lfdir, ROOTINO); 422 lfdir = 0; 423 if (preen) 424 printf("\n"); 425 } 426 } 427 } 428 } 429 if (lfdir == 0) { 430 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 431 printf("\n\n"); 432 return (0); 433 } 434 } 435 dp = ginode(lfdir); 436 if ((DIP(dp, di_mode) & IFMT) != IFDIR) { 437 pfatal("lost+found IS NOT A DIRECTORY"); 438 if (reply("REALLOCATE") == 0) 439 return (0); 440 oldlfdir = lfdir; 441 if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) { 442 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 443 return (0); 444 } 445 if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) { 446 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 447 return (0); 448 } 449 inodirty(); 450 idesc.id_type = ADDR; 451 idesc.id_func = pass4check; 452 idesc.id_number = oldlfdir; 453 adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1); 454 inoinfo(oldlfdir)->ino_linkcnt = 0; 455 dp = ginode(lfdir); 456 } 457 if (inoinfo(lfdir)->ino_state != DFOUND) { 458 pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 459 return (0); 460 } 461 (void)lftempname(tempname, orphan); 462 if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) { 463 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 464 printf("\n\n"); 465 return (0); 466 } 467 inoinfo(orphan)->ino_linkcnt--; 468 if (lostdir) { 469 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 470 parentdir != (ino_t)-1) 471 (void)makeentry(orphan, lfdir, ".."); 472 dp = ginode(lfdir); 473 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 474 inodirty(); 475 inoinfo(lfdir)->ino_linkcnt++; 476 pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan); 477 if (parentdir != (ino_t)-1) { 478 printf("PARENT WAS I=%lu\n", (u_long)parentdir); 479 /* 480 * The parent directory, because of the ordering 481 * guarantees, has had the link count incremented 482 * for the child, but no entry was made. This 483 * fixes the parent link count so that fsck does 484 * not need to be rerun. 485 */ 486 inoinfo(parentdir)->ino_linkcnt++; 487 } 488 if (preen == 0) 489 printf("\n"); 490 } 491 return (1); 492 } 493 494 /* 495 * fix an entry in a directory. 496 */ 497 int 498 changeino(ino_t dir, const char *name, ino_t newnum) 499 { 500 struct inodesc idesc; 501 502 memset(&idesc, 0, sizeof(struct inodesc)); 503 idesc.id_type = DATA; 504 idesc.id_func = chgino; 505 idesc.id_number = dir; 506 idesc.id_fix = DONTKNOW; 507 idesc.id_name = strdup(name); 508 idesc.id_parent = newnum; /* new value for name */ 509 return (ckinode(ginode(dir), &idesc)); 510 } 511 512 /* 513 * make an entry in a directory 514 */ 515 int 516 makeentry(ino_t parent, ino_t ino, const char *name) 517 { 518 union dinode *dp; 519 struct inodesc idesc; 520 char pathbuf[MAXPATHLEN + 1]; 521 522 if (parent < ROOTINO || parent >= maxino || 523 ino < ROOTINO || ino >= maxino) 524 return (0); 525 memset(&idesc, 0, sizeof(struct inodesc)); 526 idesc.id_type = DATA; 527 idesc.id_func = mkentry; 528 idesc.id_number = parent; 529 idesc.id_parent = ino; /* this is the inode to enter */ 530 idesc.id_fix = DONTKNOW; 531 idesc.id_name = strdup(name); 532 dp = ginode(parent); 533 if (DIP(dp, di_size) % DIRBLKSIZ) { 534 DIP_SET(dp, di_size, roundup(DIP(dp, di_size), DIRBLKSIZ)); 535 inodirty(); 536 } 537 if ((ckinode(dp, &idesc) & ALTERED) != 0) 538 return (1); 539 getpathname(pathbuf, parent, parent); 540 dp = ginode(parent); 541 if (expanddir(dp, pathbuf) == 0) 542 return (0); 543 return (ckinode(dp, &idesc) & ALTERED); 544 } 545 546 /* 547 * Attempt to expand the size of a directory 548 */ 549 static int 550 expanddir(union dinode *dp, char *name) 551 { 552 ufs2_daddr_t lastbn, newblk; 553 struct bufarea *bp; 554 char *cp, firstblk[DIRBLKSIZ]; 555 556 lastbn = lblkno(&sblock, DIP(dp, di_size)); 557 if (lastbn >= NDADDR - 1 || DIP(dp, di_db[lastbn]) == 0 || 558 DIP(dp, di_size) == 0) 559 return (0); 560 if ((newblk = allocblk(sblock.fs_frag)) == 0) 561 return (0); 562 DIP_SET(dp, di_db[lastbn + 1], DIP(dp, di_db[lastbn])); 563 DIP_SET(dp, di_db[lastbn], newblk); 564 DIP_SET(dp, di_size, DIP(dp, di_size) + sblock.fs_bsize); 565 DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize)); 566 bp = getdirblk(DIP(dp, di_db[lastbn + 1]), 567 sblksize(&sblock, DIP(dp, di_size), lastbn + 1)); 568 if (bp->b_errs) 569 goto bad; 570 memmove(firstblk, bp->b_un.b_buf, DIRBLKSIZ); 571 bp = getdirblk(newblk, sblock.fs_bsize); 572 if (bp->b_errs) 573 goto bad; 574 memmove(bp->b_un.b_buf, firstblk, DIRBLKSIZ); 575 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 576 cp < &bp->b_un.b_buf[sblock.fs_bsize]; 577 cp += DIRBLKSIZ) 578 memmove(cp, &emptydir, sizeof emptydir); 579 dirty(bp); 580 bp = getdirblk(DIP(dp, di_db[lastbn + 1]), 581 sblksize(&sblock, DIP(dp, di_size), lastbn + 1)); 582 if (bp->b_errs) 583 goto bad; 584 memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir); 585 pwarn("NO SPACE LEFT IN %s", name); 586 if (preen) 587 printf(" (EXPANDED)\n"); 588 else if (reply("EXPAND") == 0) 589 goto bad; 590 dirty(bp); 591 inodirty(); 592 return (1); 593 bad: 594 DIP_SET(dp, di_db[lastbn], DIP(dp, di_db[lastbn + 1])); 595 DIP_SET(dp, di_db[lastbn + 1], 0); 596 DIP_SET(dp, di_size, DIP(dp, di_size) - sblock.fs_bsize); 597 DIP_SET(dp, di_blocks, DIP(dp, di_blocks) - btodb(sblock.fs_bsize)); 598 freeblk(newblk, sblock.fs_frag); 599 return (0); 600 } 601 602 /* 603 * allocate a new directory 604 */ 605 ino_t 606 allocdir(ino_t parent, ino_t request, int mode) 607 { 608 ino_t ino; 609 char *cp; 610 union dinode *dp; 611 struct bufarea *bp; 612 struct inoinfo *inp; 613 struct dirtemplate *dirp; 614 615 ino = allocino(request, IFDIR|mode); 616 dirp = &dirhead; 617 dirp->dot_ino = ino; 618 dirp->dotdot_ino = parent; 619 dp = ginode(ino); 620 bp = getdirblk(DIP(dp, di_db[0]), sblock.fs_fsize); 621 if (bp->b_errs) { 622 freeino(ino); 623 return (0); 624 } 625 memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate)); 626 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 627 cp < &bp->b_un.b_buf[sblock.fs_fsize]; 628 cp += DIRBLKSIZ) 629 memmove(cp, &emptydir, sizeof emptydir); 630 dirty(bp); 631 DIP_SET(dp, di_nlink, 2); 632 inodirty(); 633 if (ino == ROOTINO) { 634 inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 635 cacheino(dp, ino); 636 return(ino); 637 } 638 if (!INO_IS_DVALID(parent)) { 639 freeino(ino); 640 return (0); 641 } 642 cacheino(dp, ino); 643 inp = getinoinfo(ino); 644 inp->i_parent = parent; 645 inp->i_dotdot = parent; 646 inoinfo(ino)->ino_state = inoinfo(parent)->ino_state; 647 if (inoinfo(ino)->ino_state == DSTATE) { 648 inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 649 inoinfo(parent)->ino_linkcnt++; 650 } 651 dp = ginode(parent); 652 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 653 inodirty(); 654 return (ino); 655 } 656 657 /* 658 * free a directory inode 659 */ 660 static void 661 freedir(ino_t ino, ino_t parent) 662 { 663 union dinode *dp; 664 665 if (ino != parent) { 666 dp = ginode(parent); 667 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - 1); 668 inodirty(); 669 } 670 freeino(ino); 671 } 672 673 /* 674 * generate a temporary name for the lost+found directory. 675 */ 676 static int 677 lftempname(char *bufp, ino_t ino) 678 { 679 ino_t in; 680 char *cp; 681 int namlen; 682 683 cp = bufp + 2; 684 for (in = maxino; in > 0; in /= 10) 685 cp++; 686 *--cp = 0; 687 namlen = cp - bufp; 688 in = ino; 689 while (cp > bufp) { 690 *--cp = (in % 10) + '0'; 691 in /= 10; 692 } 693 *cp = '#'; 694 return (namlen); 695 } 696 697 /* 698 * Get a directory block. 699 * Insure that it is held until another is requested. 700 */ 701 static struct bufarea * 702 getdirblk(ufs2_daddr_t blkno, long size) 703 { 704 705 if (pdirbp != 0) 706 pdirbp->b_flags &= ~B_INUSE; 707 pdirbp = getdatablk(blkno, size, BT_DIRDATA); 708 return (pdirbp); 709 } 710