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 pinode(ino); 258 printf("\n"); 259 getpathname(pathbuf, cwd, ino); 260 if (ino < UFS_ROOTINO || ino > maxino) { 261 pfatal("NAME=%s\n", pathbuf); 262 return; 263 } 264 dp = ginode(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 pinode(idesc->id_number); 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(); 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 pinode(orphan); 394 if (preen && DIP(dp, di_size) == 0) 395 return (0); 396 if (cursnapshot != 0) { 397 pfatal("FILE LINKUP IN SNAPSHOT"); 398 return (0); 399 } 400 if (preen) 401 printf(" (RECONNECTED)\n"); 402 else 403 if (reply("RECONNECT") == 0) 404 return (0); 405 if (lfdir == 0) { 406 dp = ginode(UFS_ROOTINO); 407 idesc.id_name = strdup(lfname); 408 idesc.id_type = DATA; 409 idesc.id_func = findino; 410 idesc.id_number = UFS_ROOTINO; 411 if ((ckinode(dp, &idesc) & FOUND) != 0) { 412 lfdir = idesc.id_parent; 413 } else { 414 pwarn("NO lost+found DIRECTORY"); 415 if (preen || reply("CREATE")) { 416 lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode); 417 if (lfdir != 0) { 418 if (makeentry(UFS_ROOTINO, lfdir, 419 lfname) != 0) { 420 numdirs++; 421 if (preen) 422 printf(" (CREATED)\n"); 423 } else { 424 freedir(lfdir, UFS_ROOTINO); 425 lfdir = 0; 426 if (preen) 427 printf("\n"); 428 } 429 } 430 } 431 } 432 if (lfdir == 0) { 433 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 434 printf("\n\n"); 435 return (0); 436 } 437 } 438 dp = ginode(lfdir); 439 if ((DIP(dp, di_mode) & IFMT) != IFDIR) { 440 pfatal("lost+found IS NOT A DIRECTORY"); 441 if (reply("REALLOCATE") == 0) 442 return (0); 443 oldlfdir = lfdir; 444 if ((lfdir = allocdir(UFS_ROOTINO, (ino_t)0, lfmode)) == 0) { 445 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 446 return (0); 447 } 448 if ((changeino(UFS_ROOTINO, lfname, lfdir) & ALTERED) == 0) { 449 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 450 return (0); 451 } 452 inodirty(); 453 idesc.id_type = ADDR; 454 idesc.id_func = pass4check; 455 idesc.id_number = oldlfdir; 456 adjust(&idesc, inoinfo(oldlfdir)->ino_linkcnt + 1); 457 inoinfo(oldlfdir)->ino_linkcnt = 0; 458 dp = ginode(lfdir); 459 } 460 if (inoinfo(lfdir)->ino_state != DFOUND) { 461 pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 462 return (0); 463 } 464 (void)lftempname(tempname, orphan); 465 if (makeentry(lfdir, orphan, (name ? name : tempname)) == 0) { 466 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 467 printf("\n\n"); 468 return (0); 469 } 470 inoinfo(orphan)->ino_linkcnt--; 471 if (lostdir) { 472 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 473 parentdir != (ino_t)-1) 474 (void)makeentry(orphan, lfdir, ".."); 475 dp = ginode(lfdir); 476 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 477 inodirty(); 478 inoinfo(lfdir)->ino_linkcnt++; 479 pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan); 480 if (parentdir != (ino_t)-1) { 481 printf("PARENT WAS I=%lu\n", (u_long)parentdir); 482 /* 483 * The parent directory, because of the ordering 484 * guarantees, has had the link count incremented 485 * for the child, but no entry was made. This 486 * fixes the parent link count so that fsck does 487 * not need to be rerun. 488 */ 489 inoinfo(parentdir)->ino_linkcnt++; 490 } 491 if (preen == 0) 492 printf("\n"); 493 } 494 return (1); 495 } 496 497 /* 498 * fix an entry in a directory. 499 */ 500 int 501 changeino(ino_t dir, const char *name, ino_t newnum) 502 { 503 struct inodesc idesc; 504 505 memset(&idesc, 0, sizeof(struct inodesc)); 506 idesc.id_type = DATA; 507 idesc.id_func = chgino; 508 idesc.id_number = dir; 509 idesc.id_fix = DONTKNOW; 510 idesc.id_name = strdup(name); 511 idesc.id_parent = newnum; /* new value for name */ 512 return (ckinode(ginode(dir), &idesc)); 513 } 514 515 /* 516 * make an entry in a directory 517 */ 518 int 519 makeentry(ino_t parent, ino_t ino, const char *name) 520 { 521 union dinode *dp; 522 struct inodesc idesc; 523 char pathbuf[MAXPATHLEN + 1]; 524 525 if (parent < UFS_ROOTINO || parent >= maxino || 526 ino < UFS_ROOTINO || ino >= maxino) 527 return (0); 528 memset(&idesc, 0, sizeof(struct inodesc)); 529 idesc.id_type = DATA; 530 idesc.id_func = mkentry; 531 idesc.id_number = parent; 532 idesc.id_parent = ino; /* this is the inode to enter */ 533 idesc.id_fix = DONTKNOW; 534 idesc.id_name = strdup(name); 535 dp = ginode(parent); 536 if (DIP(dp, di_size) % DIRBLKSIZ) { 537 DIP_SET(dp, di_size, roundup(DIP(dp, di_size), DIRBLKSIZ)); 538 inodirty(); 539 } 540 if ((ckinode(dp, &idesc) & ALTERED) != 0) 541 return (1); 542 getpathname(pathbuf, parent, parent); 543 dp = ginode(parent); 544 if (expanddir(dp, pathbuf) == 0) 545 return (0); 546 return (ckinode(dp, &idesc) & ALTERED); 547 } 548 549 /* 550 * Attempt to expand the size of a directory 551 */ 552 static int 553 expanddir(union dinode *dp, char *name) 554 { 555 ufs2_daddr_t lastbn, newblk; 556 struct bufarea *bp; 557 char *cp, firstblk[DIRBLKSIZ]; 558 559 lastbn = lblkno(&sblock, DIP(dp, di_size)); 560 if (lastbn >= UFS_NDADDR - 1 || DIP(dp, di_db[lastbn]) == 0 || 561 DIP(dp, di_size) == 0) 562 return (0); 563 if ((newblk = allocblk(sblock.fs_frag)) == 0) 564 return (0); 565 DIP_SET(dp, di_db[lastbn + 1], DIP(dp, di_db[lastbn])); 566 DIP_SET(dp, di_db[lastbn], newblk); 567 DIP_SET(dp, di_size, DIP(dp, di_size) + sblock.fs_bsize); 568 DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize)); 569 bp = getdirblk(DIP(dp, di_db[lastbn + 1]), 570 sblksize(&sblock, DIP(dp, di_size), lastbn + 1)); 571 if (bp->b_errs) 572 goto bad; 573 memmove(firstblk, bp->b_un.b_buf, DIRBLKSIZ); 574 bp = getdirblk(newblk, sblock.fs_bsize); 575 if (bp->b_errs) 576 goto bad; 577 memmove(bp->b_un.b_buf, firstblk, DIRBLKSIZ); 578 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 579 cp < &bp->b_un.b_buf[sblock.fs_bsize]; 580 cp += DIRBLKSIZ) 581 memmove(cp, &emptydir, sizeof emptydir); 582 dirty(bp); 583 bp = getdirblk(DIP(dp, di_db[lastbn + 1]), 584 sblksize(&sblock, DIP(dp, di_size), lastbn + 1)); 585 if (bp->b_errs) 586 goto bad; 587 memmove(bp->b_un.b_buf, &emptydir, sizeof emptydir); 588 pwarn("NO SPACE LEFT IN %s", name); 589 if (preen) 590 printf(" (EXPANDED)\n"); 591 else if (reply("EXPAND") == 0) 592 goto bad; 593 dirty(bp); 594 inodirty(); 595 return (1); 596 bad: 597 DIP_SET(dp, di_db[lastbn], DIP(dp, di_db[lastbn + 1])); 598 DIP_SET(dp, di_db[lastbn + 1], 0); 599 DIP_SET(dp, di_size, DIP(dp, di_size) - sblock.fs_bsize); 600 DIP_SET(dp, di_blocks, DIP(dp, di_blocks) - btodb(sblock.fs_bsize)); 601 freeblk(newblk, sblock.fs_frag); 602 return (0); 603 } 604 605 /* 606 * allocate a new directory 607 */ 608 ino_t 609 allocdir(ino_t parent, ino_t request, int mode) 610 { 611 ino_t ino; 612 char *cp; 613 union dinode *dp; 614 struct bufarea *bp; 615 struct inoinfo *inp; 616 struct dirtemplate *dirp; 617 618 ino = allocino(request, IFDIR|mode); 619 dirp = &dirhead; 620 dirp->dot_ino = ino; 621 dirp->dotdot_ino = parent; 622 dp = ginode(ino); 623 bp = getdirblk(DIP(dp, di_db[0]), sblock.fs_fsize); 624 if (bp->b_errs) { 625 freeino(ino); 626 return (0); 627 } 628 memmove(bp->b_un.b_buf, dirp, sizeof(struct dirtemplate)); 629 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 630 cp < &bp->b_un.b_buf[sblock.fs_fsize]; 631 cp += DIRBLKSIZ) 632 memmove(cp, &emptydir, sizeof emptydir); 633 dirty(bp); 634 DIP_SET(dp, di_nlink, 2); 635 inodirty(); 636 if (ino == UFS_ROOTINO) { 637 inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 638 cacheino(dp, ino); 639 return(ino); 640 } 641 if (!INO_IS_DVALID(parent)) { 642 freeino(ino); 643 return (0); 644 } 645 cacheino(dp, ino); 646 inp = getinoinfo(ino); 647 inp->i_parent = parent; 648 inp->i_dotdot = parent; 649 inoinfo(ino)->ino_state = inoinfo(parent)->ino_state; 650 if (inoinfo(ino)->ino_state == DSTATE) { 651 inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); 652 inoinfo(parent)->ino_linkcnt++; 653 } 654 dp = ginode(parent); 655 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); 656 inodirty(); 657 return (ino); 658 } 659 660 /* 661 * free a directory inode 662 */ 663 static void 664 freedir(ino_t ino, ino_t parent) 665 { 666 union dinode *dp; 667 668 if (ino != parent) { 669 dp = ginode(parent); 670 DIP_SET(dp, di_nlink, DIP(dp, di_nlink) - 1); 671 inodirty(); 672 } 673 freeino(ino); 674 } 675 676 /* 677 * generate a temporary name for the lost+found directory. 678 */ 679 static int 680 lftempname(char *bufp, ino_t ino) 681 { 682 ino_t in; 683 char *cp; 684 int namlen; 685 686 cp = bufp + 2; 687 for (in = maxino; in > 0; in /= 10) 688 cp++; 689 *--cp = 0; 690 namlen = cp - bufp; 691 in = ino; 692 while (cp > bufp) { 693 *--cp = (in % 10) + '0'; 694 in /= 10; 695 } 696 *cp = '#'; 697 return (namlen); 698 } 699 700 /* 701 * Get a directory block. 702 * Insure that it is held until another is requested. 703 */ 704 static struct bufarea * 705 getdirblk(ufs2_daddr_t blkno, long size) 706 { 707 708 if (pdirbp != NULL) 709 pdirbp->b_flags &= ~B_INUSE; 710 pdirbp = getdatablk(blkno, size, BT_DIRDATA); 711 return (pdirbp); 712 } 713