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