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