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