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