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