1 /* $OpenBSD: dir.c,v 1.14 2006/03/20 21:35:37 dhill Exp $ */ 2 /* $NetBSD: dir.c,v 1.5 2000/01/28 16:01:46 bouyer Exp $ */ 3 4 /* 5 * Copyright (c) 1997 Manuel Bouyer. 6 * Copyright (c) 1980, 1986, 1993 7 * The Regents of the University of California. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include <sys/param.h> 35 #include <sys/time.h> 36 #include <ufs/ufs/dir.h> 37 #include <ufs/ext2fs/ext2fs_dinode.h> 38 #include <ufs/ext2fs/ext2fs_dir.h> 39 #include <ufs/ext2fs/ext2fs.h> 40 41 #include <ufs/ufs/dinode.h> /* for IFMT & friends */ 42 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 47 #include "fsck.h" 48 #include "fsutil.h" 49 #include "extern.h" 50 51 char *lfname = "lost+found"; 52 int lfmode = 01777; 53 struct ext2fs_dirtemplate emptydir = { 0, DIRBLKSIZ }; 54 struct ext2fs_dirtemplate dirhead = { 55 0, 12, 1, EXT2_FT_DIR, ".", 56 0, DIRBLKSIZ - 12, 2, EXT2_FT_DIR, ".." 57 }; 58 #undef DIRBLKSIZ 59 60 static int expanddir(struct ext2fs_dinode *, char *); 61 static void freedir(ino_t, ino_t); 62 static struct ext2fs_direct *fsck_readdir(struct inodesc *); 63 static struct bufarea *getdirblk(daddr_t, long); 64 static int lftempname(char *, ino_t); 65 static int mkentry(struct inodesc *); 66 static int chgino(struct inodesc *); 67 68 /* 69 * Propagate connected state through the tree. 70 */ 71 void 72 propagate(void) 73 { 74 struct inoinfo **inpp, *inp, *pinp; 75 struct inoinfo **inpend; 76 77 /* 78 * Create a list of children for each directory. 79 */ 80 inpend = &inpsort[inplast]; 81 for (inpp = inpsort; inpp < inpend; inpp++) { 82 inp = *inpp; 83 if (inp->i_parent == 0 || 84 inp->i_number == EXT2_ROOTINO) 85 continue; 86 pinp = getinoinfo(inp->i_parent); 87 inp->i_parentp = pinp; 88 inp->i_sibling = pinp->i_child; 89 pinp->i_child = inp; 90 } 91 inp = getinoinfo(EXT2_ROOTINO); 92 while (inp) { 93 statemap[inp->i_number] = DFOUND; 94 if (inp->i_child && 95 statemap[inp->i_child->i_number] == DSTATE) 96 inp = inp->i_child; 97 else if (inp->i_sibling) 98 inp = inp->i_sibling; 99 else 100 inp = inp->i_parentp; 101 } 102 } 103 104 /* 105 * Scan each entry in a directory block. 106 */ 107 int 108 dirscan(struct inodesc *idesc) 109 { 110 struct ext2fs_direct *dp; 111 struct bufarea *bp; 112 int dsize, n; 113 long blksiz; 114 char *dbuf = NULL; 115 116 if ((dbuf = malloc(sblock.e2fs_bsize)) == NULL) { 117 fprintf(stderr, "out of memory"); 118 exit(8); 119 } 120 121 if (idesc->id_type != DATA) 122 errexit("wrong type to dirscan %d\n", idesc->id_type); 123 if (idesc->id_entryno == 0 && 124 (idesc->id_filesize & (sblock.e2fs_bsize - 1)) != 0) 125 idesc->id_filesize = roundup(idesc->id_filesize, sblock.e2fs_bsize); 126 blksiz = idesc->id_numfrags * sblock.e2fs_bsize; 127 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 128 idesc->id_filesize -= blksiz; 129 free(dbuf); 130 return (SKIP); 131 } 132 idesc->id_loc = 0; 133 for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 134 dsize = fs2h16(dp->e2d_reclen); 135 memcpy(dbuf, dp, (size_t)dsize); 136 idesc->id_dirp = (struct ext2fs_direct *)dbuf; 137 if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 138 bp = getdirblk(idesc->id_blkno, blksiz); 139 memcpy(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf, 140 (size_t)dsize); 141 dirty(bp); 142 sbdirty(); 143 } 144 if (n & STOP) { 145 free(dbuf); 146 return (n); 147 } 148 } 149 free(dbuf); 150 return (idesc->id_filesize > 0 ? KEEPON : STOP); 151 } 152 153 /* 154 * get next entry in a directory. 155 */ 156 static struct ext2fs_direct * 157 fsck_readdir(struct inodesc *idesc) 158 { 159 struct ext2fs_direct *dp, *ndp; 160 struct bufarea *bp; 161 long size, blksiz, fix, dploc; 162 163 blksiz = idesc->id_numfrags * sblock.e2fs_bsize; 164 bp = getdirblk(idesc->id_blkno, blksiz); 165 if (idesc->id_loc % sblock.e2fs_bsize == 0 && idesc->id_filesize > 0 && 166 idesc->id_loc < blksiz) { 167 dp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc); 168 if (dircheck(idesc, dp)) 169 goto dpok; 170 if (idesc->id_fix == IGNORE) 171 return (0); 172 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 173 bp = getdirblk(idesc->id_blkno, blksiz); 174 dp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc); 175 dp->e2d_reclen = h2fs16(sblock.e2fs_bsize); 176 dp->e2d_ino = 0; 177 dp->e2d_namlen = 0; 178 dp->e2d_type = 0; 179 dp->e2d_name[0] = '\0'; 180 if (fix) 181 dirty(bp); 182 idesc->id_loc += sblock.e2fs_bsize; 183 idesc->id_filesize -= sblock.e2fs_bsize; 184 return (dp); 185 } 186 dpok: 187 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 188 return NULL; 189 dploc = idesc->id_loc; 190 dp = (struct ext2fs_direct *)(bp->b_un.b_buf + dploc); 191 idesc->id_loc += fs2h16(dp->e2d_reclen); 192 idesc->id_filesize -= fs2h16(dp->e2d_reclen); 193 if ((idesc->id_loc % sblock.e2fs_bsize) == 0) 194 return (dp); 195 ndp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc); 196 if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && 197 dircheck(idesc, ndp) == 0) { 198 size = sblock.e2fs_bsize - (idesc->id_loc % sblock.e2fs_bsize); 199 idesc->id_loc += size; 200 idesc->id_filesize -= size; 201 if (idesc->id_fix == IGNORE) 202 return (0); 203 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 204 bp = getdirblk(idesc->id_blkno, blksiz); 205 dp = (struct ext2fs_direct *)(bp->b_un.b_buf + dploc); 206 dp->e2d_reclen = h2fs16(fs2h16(dp->e2d_reclen) + size); 207 if (fix) 208 dirty(bp); 209 } 210 return (dp); 211 } 212 213 /* 214 * Verify that a directory entry is valid. 215 * This is a superset of the checks made in the kernel. 216 */ 217 int 218 dircheck(struct inodesc *idesc, struct ext2fs_direct *dp) 219 { 220 int size; 221 char *cp; 222 int spaceleft; 223 u_int16_t reclen = fs2h16(dp->e2d_reclen); 224 225 spaceleft = sblock.e2fs_bsize - (idesc->id_loc % sblock.e2fs_bsize); 226 if (fs2h32(dp->e2d_ino) > maxino || 227 reclen == 0 || 228 reclen > spaceleft || 229 (reclen & 0x3) != 0) 230 return (0); 231 if (dp->e2d_ino == 0) 232 return (1); 233 if (sblock.e2fs.e2fs_rev < E2FS_REV1 || 234 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) == 0) 235 if (dp->e2d_type != 0) 236 return (1); 237 size = EXT2FS_DIRSIZ(dp->e2d_namlen); 238 if (reclen < size || 239 idesc->id_filesize < size || 240 dp->e2d_namlen > EXT2FS_MAXNAMLEN) 241 return (0); 242 for (cp = dp->e2d_name, size = 0; size < dp->e2d_namlen; size++) 243 if (*cp == '\0' || (*cp++ == '/')) 244 return (0); 245 return (1); 246 } 247 248 void 249 direrror(ino_t ino, char *errmesg) 250 { 251 252 fileerror(ino, ino, errmesg); 253 } 254 255 void 256 fileerror(ino_t cwd, ino_t ino, char *errmesg) 257 { 258 struct ext2fs_dinode *dp; 259 char pathbuf[MAXPATHLEN + 1]; 260 261 pwarn("%s ", errmesg); 262 pinode(ino); 263 printf("\n"); 264 getpathname(pathbuf, sizeof pathbuf, cwd, ino); 265 if ((ino < EXT2_FIRSTINO && ino != EXT2_ROOTINO) || ino > maxino) { 266 pfatal("NAME=%s\n", pathbuf); 267 return; 268 } 269 dp = ginode(ino); 270 if (ftypeok(dp)) 271 pfatal("%s=%s\n", 272 (fs2h16(dp->e2di_mode) & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf); 273 else 274 pfatal("NAME=%s\n", pathbuf); 275 } 276 277 void 278 adjust(struct inodesc *idesc, short lcnt) 279 { 280 struct ext2fs_dinode *dp; 281 282 dp = ginode(idesc->id_number); 283 if (fs2h16(dp->e2di_nlink) == lcnt) { 284 if (linkup(idesc->id_number, (ino_t)0) == 0) 285 clri(idesc, "UNREF", 0); 286 } else { 287 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 288 ((fs2h16(dp->e2di_mode) & IFMT) == IFDIR ? "DIR" : "FILE")); 289 pinode(idesc->id_number); 290 printf(" COUNT %d SHOULD BE %d", 291 fs2h16(dp->e2di_nlink), fs2h16(dp->e2di_nlink) - lcnt); 292 if (preen) { 293 if (lcnt < 0) { 294 printf("\n"); 295 pfatal("LINK COUNT INCREASING"); 296 } 297 printf(" (ADJUSTED)\n"); 298 } 299 if (preen || reply("ADJUST") == 1) { 300 dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) - lcnt); 301 inodirty(); 302 } 303 } 304 } 305 306 static int 307 mkentry(struct inodesc *idesc) 308 { 309 struct ext2fs_direct *dirp = idesc->id_dirp; 310 struct ext2fs_direct newent; 311 int newlen, oldlen; 312 313 newent.e2d_namlen = strlen(idesc->id_name); 314 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 315 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 316 newent.e2d_type = inot2ext2dt(typemap[idesc->id_parent]); 317 newlen = EXT2FS_DIRSIZ(newent.e2d_namlen); 318 if (dirp->e2d_ino != 0) 319 oldlen = EXT2FS_DIRSIZ(dirp->e2d_namlen); 320 else 321 oldlen = 0; 322 if (fs2h16(dirp->e2d_reclen) - oldlen < newlen) 323 return (KEEPON); 324 newent.e2d_reclen = h2fs16(fs2h16(dirp->e2d_reclen) - oldlen); 325 dirp->e2d_reclen = h2fs16(oldlen); 326 dirp = (struct ext2fs_direct *)(((char *)dirp) + oldlen); 327 dirp->e2d_ino = h2fs32(idesc->id_parent); /* ino to be entered is in id_parent */ 328 dirp->e2d_reclen = newent.e2d_reclen; 329 dirp->e2d_namlen = newent.e2d_namlen; 330 dirp->e2d_type = newent.e2d_type; 331 memcpy(dirp->e2d_name, idesc->id_name, (size_t)(dirp->e2d_namlen)); 332 return (ALTERED|STOP); 333 } 334 335 static int 336 chgino(struct inodesc *idesc) 337 { 338 struct ext2fs_direct *dirp = idesc->id_dirp; 339 u_int16_t namlen = dirp->e2d_namlen; 340 341 if (strlen(idesc->id_name) != namlen || 342 strncmp(dirp->e2d_name, idesc->id_name, (int)namlen)) 343 return (KEEPON); 344 dirp->e2d_ino = h2fs32(idesc->id_parent); 345 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 346 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 347 dirp->e2d_type = inot2ext2dt(typemap[idesc->id_parent]); 348 else 349 dirp->e2d_type = 0; 350 return (ALTERED|STOP); 351 } 352 353 int 354 linkup(ino_t orphan, ino_t parentdir) 355 { 356 struct ext2fs_dinode *dp; 357 int lostdir; 358 ino_t oldlfdir; 359 struct inodesc idesc; 360 char tempname[BUFSIZ]; 361 362 memset(&idesc, 0, sizeof(struct inodesc)); 363 dp = ginode(orphan); 364 lostdir = (fs2h16(dp->e2di_mode) & IFMT) == IFDIR; 365 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 366 pinode(orphan); 367 if (preen && inosize(dp) == 0) 368 return (0); 369 if (preen) 370 printf(" (RECONNECTED)\n"); 371 else 372 if (reply("RECONNECT") == 0) 373 return (0); 374 if (lfdir == 0) { 375 dp = ginode(EXT2_ROOTINO); 376 idesc.id_name = lfname; 377 idesc.id_type = DATA; 378 idesc.id_func = findino; 379 idesc.id_number = EXT2_ROOTINO; 380 if ((ckinode(dp, &idesc) & FOUND) != 0) { 381 lfdir = idesc.id_parent; 382 } else { 383 pwarn("NO lost+found DIRECTORY"); 384 if (preen || reply("CREATE")) { 385 lfdir = allocdir(EXT2_ROOTINO, (ino_t)0, lfmode); 386 if (lfdir != 0) { 387 if (makeentry(EXT2_ROOTINO, lfdir, lfname) != 0) { 388 if (preen) 389 printf(" (CREATED)\n"); 390 } else { 391 freedir(lfdir, EXT2_ROOTINO); 392 lfdir = 0; 393 if (preen) 394 printf("\n"); 395 } 396 } 397 } 398 } 399 if (lfdir == 0) { 400 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 401 printf("\n\n"); 402 return (0); 403 } 404 } 405 dp = ginode(lfdir); 406 if ((fs2h16(dp->e2di_mode) & IFMT) != IFDIR) { 407 pfatal("lost+found IS NOT A DIRECTORY"); 408 if (reply("REALLOCATE") == 0) 409 return (0); 410 oldlfdir = lfdir; 411 if ((lfdir = allocdir(EXT2_ROOTINO, (ino_t)0, lfmode)) == 0) { 412 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 413 return (0); 414 } 415 if ((changeino(EXT2_ROOTINO, lfname, lfdir) & ALTERED) == 0) { 416 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 417 return (0); 418 } 419 inodirty(); 420 idesc.id_type = ADDR; 421 idesc.id_func = pass4check; 422 idesc.id_number = oldlfdir; 423 adjust(&idesc, lncntp[oldlfdir] + 1); 424 lncntp[oldlfdir] = 0; 425 dp = ginode(lfdir); 426 } 427 if (statemap[lfdir] != DFOUND) { 428 pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 429 return (0); 430 } 431 (void)lftempname(tempname, orphan); 432 if (makeentry(lfdir, orphan, tempname) == 0) { 433 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 434 printf("\n\n"); 435 return (0); 436 } 437 lncntp[orphan]--; 438 if (lostdir) { 439 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 440 parentdir != (ino_t)-1) 441 (void)makeentry(orphan, lfdir, ".."); 442 dp = ginode(lfdir); 443 dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) +1); 444 inodirty(); 445 lncntp[lfdir]++; 446 pwarn("DIR I=%u CONNECTED. ", orphan); 447 if (parentdir != (ino_t)-1) 448 printf("PARENT WAS I=%u\n", parentdir); 449 if (preen == 0) 450 printf("\n"); 451 } 452 return (1); 453 } 454 455 /* 456 * fix an entry in a directory. 457 */ 458 int 459 changeino(ino_t dir, char *name, ino_t newnum) 460 { 461 struct inodesc idesc; 462 463 memset(&idesc, 0, sizeof(struct inodesc)); 464 idesc.id_type = DATA; 465 idesc.id_func = chgino; 466 idesc.id_number = dir; 467 idesc.id_fix = DONTKNOW; 468 idesc.id_name = name; 469 idesc.id_parent = newnum; /* new value for name */ 470 return (ckinode(ginode(dir), &idesc)); 471 } 472 473 /* 474 * make an entry in a directory 475 */ 476 int 477 makeentry(ino_t parent, ino_t ino, char *name) 478 { 479 struct ext2fs_dinode *dp; 480 struct inodesc idesc; 481 char pathbuf[MAXPATHLEN + 1]; 482 483 if ((parent < EXT2_FIRSTINO && parent != EXT2_ROOTINO) 484 || parent >= maxino || 485 (ino < EXT2_FIRSTINO && ino < EXT2_ROOTINO) || ino >= maxino) 486 return (0); 487 memset(&idesc, 0, sizeof(struct inodesc)); 488 idesc.id_type = DATA; 489 idesc.id_func = mkentry; 490 idesc.id_number = parent; 491 idesc.id_parent = ino; /* this is the inode to enter */ 492 idesc.id_fix = DONTKNOW; 493 idesc.id_name = name; 494 dp = ginode(parent); 495 if (inosize(dp) % sblock.e2fs_bsize) { 496 inossize(dp, roundup(inosize(dp), sblock.e2fs_bsize)); 497 inodirty(); 498 } 499 if ((ckinode(dp, &idesc) & ALTERED) != 0) 500 return (1); 501 getpathname(pathbuf, sizeof pathbuf, parent, parent); 502 dp = ginode(parent); 503 if (expanddir(dp, pathbuf) == 0) 504 return (0); 505 return (ckinode(dp, &idesc) & ALTERED); 506 } 507 508 /* 509 * Attempt to expand the size of a directory 510 */ 511 static int 512 expanddir(struct ext2fs_dinode *dp, char *name) 513 { 514 daddr_t lastbn, newblk; 515 struct bufarea *bp; 516 char *firstblk; 517 518 lastbn = lblkno(&sblock, inosize(dp)); 519 if (lastbn >= NDADDR - 1 || fs2h32(dp->e2di_blocks[lastbn]) == 0 || 520 inosize(dp) == 0) 521 return (0); 522 if ((newblk = allocblk()) == 0) 523 return (0); 524 dp->e2di_blocks[lastbn + 1] = dp->e2di_blocks[lastbn]; 525 dp->e2di_blocks[lastbn] = h2fs32(newblk); 526 inossize(dp, inosize(dp) + sblock.e2fs_bsize); 527 dp->e2di_nblock = h2fs32(fs2h32(dp->e2di_nblock) + 1); 528 bp = getdirblk(fs2h32(dp->e2di_blocks[lastbn + 1]), 529 sblock.e2fs_bsize); 530 if (bp->b_errs) 531 goto bad; 532 if ((firstblk = malloc(sblock.e2fs_bsize)) == NULL) { 533 fprintf(stderr, "out of memory\n"); 534 exit(8); 535 } 536 memcpy(firstblk, bp->b_un.b_buf, sblock.e2fs_bsize); 537 bp = getdirblk(newblk, sblock.e2fs_bsize); 538 if (bp->b_errs) { 539 free(firstblk); 540 goto bad; 541 } 542 memcpy(bp->b_un.b_buf, firstblk, sblock.e2fs_bsize); 543 free(firstblk); 544 dirty(bp); 545 bp = getdirblk(fs2h32(dp->e2di_blocks[lastbn + 1]), 546 sblock.e2fs_bsize); 547 if (bp->b_errs) 548 goto bad; 549 emptydir.dot_reclen = h2fs16(sblock.e2fs_bsize); 550 memcpy(bp->b_un.b_buf, &emptydir, sizeof emptydir); 551 pwarn("NO SPACE LEFT IN %s", name); 552 if (preen) 553 printf(" (EXPANDED)\n"); 554 else if (reply("EXPAND") == 0) 555 goto bad; 556 dirty(bp); 557 inodirty(); 558 return (1); 559 bad: 560 dp->e2di_blocks[lastbn] = dp->e2di_blocks[lastbn + 1]; 561 dp->e2di_blocks[lastbn + 1] = 0; 562 dp->e2di_size = h2fs32(fs2h32(dp->e2di_size) - sblock.e2fs_bsize); 563 inossize(dp, inosize(dp) - sblock.e2fs_bsize); 564 dp->e2di_nblock = h2fs32(fs2h32(dp->e2di_nblock) - 1); 565 freeblk(newblk); 566 return (0); 567 } 568 569 /* 570 * allocate a new directory 571 */ 572 int 573 allocdir(ino_t parent, ino_t request, int mode) 574 { 575 ino_t ino; 576 struct ext2fs_dinode *dp; 577 struct bufarea *bp; 578 struct ext2fs_dirtemplate *dirp; 579 580 ino = allocino(request, IFDIR|mode); 581 dirhead.dot_reclen = h2fs16(12); /* XXX */ 582 dirhead.dotdot_reclen = h2fs16(sblock.e2fs_bsize - 12); /* XXX */ 583 dirhead.dot_namlen = 1; 584 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 585 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 586 dirhead.dot_type = EXT2_FT_DIR; 587 else 588 dirhead.dot_type = 0; 589 dirhead.dotdot_namlen = 2; 590 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 591 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 592 dirhead.dotdot_type = EXT2_FT_DIR; 593 else 594 dirhead.dotdot_type = 0; 595 dirp = &dirhead; 596 dirp->dot_ino = h2fs32(ino); 597 dirp->dotdot_ino = h2fs32(parent); 598 dp = ginode(ino); 599 bp = getdirblk(fs2h32(dp->e2di_blocks[0]), sblock.e2fs_bsize); 600 if (bp->b_errs) { 601 freeino(ino); 602 return (0); 603 } 604 memcpy(bp->b_un.b_buf, dirp, sizeof(struct ext2fs_dirtemplate)); 605 dirty(bp); 606 dp->e2di_nlink = h2fs16(2); 607 inodirty(); 608 if (ino == EXT2_ROOTINO) { 609 lncntp[ino] = fs2h16(dp->e2di_nlink); 610 cacheino(dp, ino); 611 return(ino); 612 } 613 if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { 614 freeino(ino); 615 return (0); 616 } 617 cacheino(dp, ino); 618 statemap[ino] = statemap[parent]; 619 if (statemap[ino] == DSTATE) { 620 lncntp[ino] = fs2h16(dp->e2di_nlink); 621 lncntp[parent]++; 622 } 623 dp = ginode(parent); 624 dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) + 1); 625 inodirty(); 626 return (ino); 627 } 628 629 /* 630 * free a directory inode 631 */ 632 static void 633 freedir(ino_t ino, ino_t parent) 634 { 635 struct ext2fs_dinode *dp; 636 637 if (ino != parent) { 638 dp = ginode(parent); 639 dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) - 1); 640 inodirty(); 641 } 642 freeino(ino); 643 } 644 645 /* 646 * generate a temporary name for the lost+found directory. 647 */ 648 static int 649 lftempname(char *bufp, ino_t ino) 650 { 651 ino_t in; 652 char *cp; 653 int namlen; 654 655 cp = bufp + 2; 656 for (in = maxino; in > 0; in /= 10) 657 cp++; 658 *--cp = 0; 659 namlen = cp - bufp; 660 in = ino; 661 while (cp > bufp) { 662 *--cp = (in % 10) + '0'; 663 in /= 10; 664 } 665 *cp = '#'; 666 return (namlen); 667 } 668 669 /* 670 * Get a directory block. 671 * Insure that it is held until another is requested. 672 */ 673 static struct bufarea * 674 getdirblk(daddr_t blkno, long size) 675 { 676 677 if (pdirbp != 0) 678 pdirbp->b_flags &= ~B_INUSE; 679 pdirbp = getdatablk(blkno, size); 680 return (pdirbp); 681 } 682