1 /* $OpenBSD: dir.c,v 1.12 2003/07/29 18:38:35 deraadt 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 return (SKIP); 130 } 131 idesc->id_loc = 0; 132 for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 133 dsize = fs2h16(dp->e2d_reclen); 134 memcpy(dbuf, dp, (size_t)dsize); 135 idesc->id_dirp = (struct ext2fs_direct *)dbuf; 136 if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 137 bp = getdirblk(idesc->id_blkno, blksiz); 138 memcpy(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf, 139 (size_t)dsize); 140 dirty(bp); 141 sbdirty(); 142 } 143 if (n & STOP) { 144 free(dbuf); 145 return (n); 146 } 147 } 148 free(dbuf); 149 return (idesc->id_filesize > 0 ? KEEPON : STOP); 150 } 151 152 /* 153 * get next entry in a directory. 154 */ 155 static struct ext2fs_direct * 156 fsck_readdir(struct inodesc *idesc) 157 { 158 struct ext2fs_direct *dp, *ndp; 159 struct bufarea *bp; 160 long size, blksiz, fix, dploc; 161 162 blksiz = idesc->id_numfrags * sblock.e2fs_bsize; 163 bp = getdirblk(idesc->id_blkno, blksiz); 164 if (idesc->id_loc % sblock.e2fs_bsize == 0 && idesc->id_filesize > 0 && 165 idesc->id_loc < blksiz) { 166 dp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc); 167 if (dircheck(idesc, dp)) 168 goto dpok; 169 if (idesc->id_fix == IGNORE) 170 return (0); 171 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 172 bp = getdirblk(idesc->id_blkno, blksiz); 173 dp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc); 174 dp->e2d_reclen = h2fs16(sblock.e2fs_bsize); 175 dp->e2d_ino = 0; 176 dp->e2d_namlen = 0; 177 dp->e2d_type = 0; 178 dp->e2d_name[0] = '\0'; 179 if (fix) 180 dirty(bp); 181 idesc->id_loc += sblock.e2fs_bsize; 182 idesc->id_filesize -= sblock.e2fs_bsize; 183 return (dp); 184 } 185 dpok: 186 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 187 return NULL; 188 dploc = idesc->id_loc; 189 dp = (struct ext2fs_direct *)(bp->b_un.b_buf + dploc); 190 idesc->id_loc += fs2h16(dp->e2d_reclen); 191 idesc->id_filesize -= fs2h16(dp->e2d_reclen); 192 if ((idesc->id_loc % sblock.e2fs_bsize) == 0) 193 return (dp); 194 ndp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc); 195 if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && 196 dircheck(idesc, ndp) == 0) { 197 size = sblock.e2fs_bsize - (idesc->id_loc % sblock.e2fs_bsize); 198 idesc->id_loc += size; 199 idesc->id_filesize -= size; 200 if (idesc->id_fix == IGNORE) 201 return (0); 202 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 203 bp = getdirblk(idesc->id_blkno, blksiz); 204 dp = (struct ext2fs_direct *)(bp->b_un.b_buf + dploc); 205 dp->e2d_reclen = h2fs16(fs2h16(dp->e2d_reclen) + size); 206 if (fix) 207 dirty(bp); 208 } 209 return (dp); 210 } 211 212 /* 213 * Verify that a directory entry is valid. 214 * This is a superset of the checks made in the kernel. 215 */ 216 int 217 dircheck(struct inodesc *idesc, struct ext2fs_direct *dp) 218 { 219 int size; 220 char *cp; 221 int spaceleft; 222 u_int16_t reclen = fs2h16(dp->e2d_reclen); 223 224 spaceleft = sblock.e2fs_bsize - (idesc->id_loc % sblock.e2fs_bsize); 225 if (fs2h32(dp->e2d_ino) > maxino || 226 reclen == 0 || 227 reclen > spaceleft || 228 (reclen & 0x3) != 0) 229 return (0); 230 if (dp->e2d_ino == 0) 231 return (1); 232 if (sblock.e2fs.e2fs_rev < E2FS_REV1 || 233 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) == 0) 234 if (dp->e2d_type != 0) 235 return (1); 236 size = EXT2FS_DIRSIZ(dp->e2d_namlen); 237 if (reclen < size || 238 idesc->id_filesize < size || 239 dp->e2d_namlen > EXT2FS_MAXNAMLEN) 240 return (0); 241 for (cp = dp->e2d_name, size = 0; size < dp->e2d_namlen; size++) 242 if (*cp == '\0' || (*cp++ == '/')) 243 return (0); 244 return (1); 245 } 246 247 void 248 direrror(ino_t ino, char *errmesg) 249 { 250 251 fileerror(ino, ino, errmesg); 252 } 253 254 void 255 fileerror(ino_t cwd, ino_t ino, char *errmesg) 256 { 257 struct ext2fs_dinode *dp; 258 char pathbuf[MAXPATHLEN + 1]; 259 260 pwarn("%s ", errmesg); 261 pinode(ino); 262 printf("\n"); 263 getpathname(pathbuf, sizeof pathbuf, cwd, ino); 264 if ((ino < EXT2_FIRSTINO && ino != EXT2_ROOTINO) || ino > maxino) { 265 pfatal("NAME=%s\n", pathbuf); 266 return; 267 } 268 dp = ginode(ino); 269 if (ftypeok(dp)) 270 pfatal("%s=%s\n", 271 (fs2h16(dp->e2di_mode) & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf); 272 else 273 pfatal("NAME=%s\n", pathbuf); 274 } 275 276 void 277 adjust(struct inodesc *idesc, short lcnt) 278 { 279 struct ext2fs_dinode *dp; 280 281 dp = ginode(idesc->id_number); 282 if (fs2h16(dp->e2di_nlink) == lcnt) { 283 if (linkup(idesc->id_number, (ino_t)0) == 0) 284 clri(idesc, "UNREF", 0); 285 } else { 286 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 287 ((fs2h16(dp->e2di_mode) & IFMT) == IFDIR ? "DIR" : "FILE")); 288 pinode(idesc->id_number); 289 printf(" COUNT %d SHOULD BE %d", 290 fs2h16(dp->e2di_nlink), fs2h16(dp->e2di_nlink) - lcnt); 291 if (preen) { 292 if (lcnt < 0) { 293 printf("\n"); 294 pfatal("LINK COUNT INCREASING"); 295 } 296 printf(" (ADJUSTED)\n"); 297 } 298 if (preen || reply("ADJUST") == 1) { 299 dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) - lcnt); 300 inodirty(); 301 } 302 } 303 } 304 305 static int 306 mkentry(struct inodesc *idesc) 307 { 308 struct ext2fs_direct *dirp = idesc->id_dirp; 309 struct ext2fs_direct newent; 310 int newlen, oldlen; 311 312 newent.e2d_namlen = strlen(idesc->id_name); 313 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 314 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 315 newent.e2d_type = inot2ext2dt(typemap[idesc->id_parent]); 316 newlen = EXT2FS_DIRSIZ(newent.e2d_namlen); 317 if (dirp->e2d_ino != 0) 318 oldlen = EXT2FS_DIRSIZ(dirp->e2d_namlen); 319 else 320 oldlen = 0; 321 if (fs2h16(dirp->e2d_reclen) - oldlen < newlen) 322 return (KEEPON); 323 newent.e2d_reclen = h2fs16(fs2h16(dirp->e2d_reclen) - oldlen); 324 dirp->e2d_reclen = h2fs16(oldlen); 325 dirp = (struct ext2fs_direct *)(((char *)dirp) + oldlen); 326 dirp->e2d_ino = h2fs32(idesc->id_parent); /* ino to be entered is in id_parent */ 327 dirp->e2d_reclen = newent.e2d_reclen; 328 dirp->e2d_namlen = newent.e2d_namlen; 329 dirp->e2d_type = newent.e2d_type; 330 memcpy(dirp->e2d_name, idesc->id_name, (size_t)(dirp->e2d_namlen)); 331 return (ALTERED|STOP); 332 } 333 334 static int 335 chgino(struct inodesc *idesc) 336 { 337 struct ext2fs_direct *dirp = idesc->id_dirp; 338 u_int16_t namlen = dirp->e2d_namlen; 339 340 if (strlen(idesc->id_name) != namlen || 341 strncmp(dirp->e2d_name, idesc->id_name, (int)namlen)) 342 return (KEEPON); 343 dirp->e2d_ino = h2fs32(idesc->id_parent); 344 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 345 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 346 dirp->e2d_type = inot2ext2dt(typemap[idesc->id_parent]); 347 else 348 dirp->e2d_type = 0; 349 return (ALTERED|STOP); 350 } 351 352 int 353 linkup(ino_t orphan, ino_t parentdir) 354 { 355 struct ext2fs_dinode *dp; 356 int lostdir; 357 ino_t oldlfdir; 358 struct inodesc idesc; 359 char tempname[BUFSIZ]; 360 361 memset(&idesc, 0, sizeof(struct inodesc)); 362 dp = ginode(orphan); 363 lostdir = (fs2h16(dp->e2di_mode) & IFMT) == IFDIR; 364 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 365 pinode(orphan); 366 if (preen && fs2h32(dp->e2di_size) == 0) 367 return (0); 368 if (preen) 369 printf(" (RECONNECTED)\n"); 370 else 371 if (reply("RECONNECT") == 0) 372 return (0); 373 if (lfdir == 0) { 374 dp = ginode(EXT2_ROOTINO); 375 idesc.id_name = lfname; 376 idesc.id_type = DATA; 377 idesc.id_func = findino; 378 idesc.id_number = EXT2_ROOTINO; 379 if ((ckinode(dp, &idesc) & FOUND) != 0) { 380 lfdir = idesc.id_parent; 381 } else { 382 pwarn("NO lost+found DIRECTORY"); 383 if (preen || reply("CREATE")) { 384 lfdir = allocdir(EXT2_ROOTINO, (ino_t)0, lfmode); 385 if (lfdir != 0) { 386 if (makeentry(EXT2_ROOTINO, lfdir, lfname) != 0) { 387 if (preen) 388 printf(" (CREATED)\n"); 389 } else { 390 freedir(lfdir, EXT2_ROOTINO); 391 lfdir = 0; 392 if (preen) 393 printf("\n"); 394 } 395 } 396 } 397 } 398 if (lfdir == 0) { 399 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 400 printf("\n\n"); 401 return (0); 402 } 403 } 404 dp = ginode(lfdir); 405 if ((fs2h16(dp->e2di_mode) & IFMT) != IFDIR) { 406 pfatal("lost+found IS NOT A DIRECTORY"); 407 if (reply("REALLOCATE") == 0) 408 return (0); 409 oldlfdir = lfdir; 410 if ((lfdir = allocdir(EXT2_ROOTINO, (ino_t)0, lfmode)) == 0) { 411 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 412 return (0); 413 } 414 if ((changeino(EXT2_ROOTINO, lfname, lfdir) & ALTERED) == 0) { 415 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 416 return (0); 417 } 418 inodirty(); 419 idesc.id_type = ADDR; 420 idesc.id_func = pass4check; 421 idesc.id_number = oldlfdir; 422 adjust(&idesc, lncntp[oldlfdir] + 1); 423 lncntp[oldlfdir] = 0; 424 dp = ginode(lfdir); 425 } 426 if (statemap[lfdir] != DFOUND) { 427 pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 428 return (0); 429 } 430 (void)lftempname(tempname, orphan); 431 if (makeentry(lfdir, orphan, tempname) == 0) { 432 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 433 printf("\n\n"); 434 return (0); 435 } 436 lncntp[orphan]--; 437 if (lostdir) { 438 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 439 parentdir != (ino_t)-1) 440 (void)makeentry(orphan, lfdir, ".."); 441 dp = ginode(lfdir); 442 dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) +1); 443 inodirty(); 444 lncntp[lfdir]++; 445 pwarn("DIR I=%u CONNECTED. ", orphan); 446 if (parentdir != (ino_t)-1) 447 printf("PARENT WAS I=%u\n", parentdir); 448 if (preen == 0) 449 printf("\n"); 450 } 451 return (1); 452 } 453 454 /* 455 * fix an entry in a directory. 456 */ 457 int 458 changeino(ino_t dir, char *name, ino_t newnum) 459 { 460 struct inodesc idesc; 461 462 memset(&idesc, 0, sizeof(struct inodesc)); 463 idesc.id_type = DATA; 464 idesc.id_func = chgino; 465 idesc.id_number = dir; 466 idesc.id_fix = DONTKNOW; 467 idesc.id_name = name; 468 idesc.id_parent = newnum; /* new value for name */ 469 return (ckinode(ginode(dir), &idesc)); 470 } 471 472 /* 473 * make an entry in a directory 474 */ 475 int 476 makeentry(ino_t parent, ino_t ino, char *name) 477 { 478 struct ext2fs_dinode *dp; 479 struct inodesc idesc; 480 char pathbuf[MAXPATHLEN + 1]; 481 482 if ((parent < EXT2_FIRSTINO && parent != EXT2_ROOTINO) 483 || parent >= maxino || 484 (ino < EXT2_FIRSTINO && ino < EXT2_ROOTINO) || ino >= maxino) 485 return (0); 486 memset(&idesc, 0, sizeof(struct inodesc)); 487 idesc.id_type = DATA; 488 idesc.id_func = mkentry; 489 idesc.id_number = parent; 490 idesc.id_parent = ino; /* this is the inode to enter */ 491 idesc.id_fix = DONTKNOW; 492 idesc.id_name = name; 493 dp = ginode(parent); 494 if (fs2h32(dp->e2di_size) % sblock.e2fs_bsize) { 495 dp->e2di_size = 496 h2fs32(roundup(fs2h32(dp->e2di_size), 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 if ((firstblk = malloc(sblock.e2fs_bsize)) == NULL) { 519 fprintf(stderr, "out of memory"); 520 exit(8); 521 } 522 523 lastbn = lblkno(&sblock, fs2h32(dp->e2di_size)); 524 if (lastbn >= NDADDR - 1 || fs2h32(dp->e2di_blocks[lastbn]) == 0 || 525 fs2h32(dp->e2di_size) == 0) 526 return (0); 527 if ((newblk = allocblk()) == 0) 528 return (0); 529 dp->e2di_blocks[lastbn + 1] = dp->e2di_blocks[lastbn]; 530 dp->e2di_blocks[lastbn] = h2fs32(newblk); 531 dp->e2di_size = h2fs32(fs2h32(dp->e2di_size) + sblock.e2fs_bsize); 532 dp->e2di_nblock = h2fs32(fs2h32(dp->e2di_nblock) + 1); 533 bp = getdirblk(fs2h32(dp->e2di_blocks[lastbn + 1]), 534 sblock.e2fs_bsize); 535 if (bp->b_errs) 536 goto bad; 537 memcpy(firstblk, bp->b_un.b_buf, sblock.e2fs_bsize); 538 bp = getdirblk(newblk, sblock.e2fs_bsize); 539 if (bp->b_errs) 540 goto bad; 541 memcpy(bp->b_un.b_buf, firstblk, sblock.e2fs_bsize); 542 dirty(bp); 543 bp = getdirblk(fs2h32(dp->e2di_blocks[lastbn + 1]), 544 sblock.e2fs_bsize); 545 if (bp->b_errs) 546 goto bad; 547 emptydir.dot_reclen = h2fs16(sblock.e2fs_bsize); 548 memcpy(bp->b_un.b_buf, &emptydir, sizeof emptydir); 549 pwarn("NO SPACE LEFT IN %s", name); 550 if (preen) 551 printf(" (EXPANDED)\n"); 552 else if (reply("EXPAND") == 0) 553 goto bad; 554 dirty(bp); 555 inodirty(); 556 return (1); 557 bad: 558 dp->e2di_blocks[lastbn] = dp->e2di_blocks[lastbn + 1]; 559 dp->e2di_blocks[lastbn + 1] = 0; 560 dp->e2di_size = h2fs32(fs2h32(dp->e2di_size) - sblock.e2fs_bsize); 561 dp->e2di_nblock = h2fs32(fs2h32(dp->e2di_nblock) - 1); 562 freeblk(newblk); 563 return (0); 564 } 565 566 /* 567 * allocate a new directory 568 */ 569 int 570 allocdir(ino_t parent, ino_t request, int mode) 571 { 572 ino_t ino; 573 struct ext2fs_dinode *dp; 574 struct bufarea *bp; 575 struct ext2fs_dirtemplate *dirp; 576 577 ino = allocino(request, IFDIR|mode); 578 dirhead.dot_reclen = h2fs16(12); /* XXX */ 579 dirhead.dotdot_reclen = h2fs16(sblock.e2fs_bsize - 12); /* XXX */ 580 dirhead.dot_namlen = 1; 581 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 582 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 583 dirhead.dot_type = EXT2_FT_DIR; 584 else 585 dirhead.dot_type = 0; 586 dirhead.dotdot_namlen = 2; 587 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 588 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 589 dirhead.dotdot_type = EXT2_FT_DIR; 590 else 591 dirhead.dotdot_type = 0; 592 dirp = &dirhead; 593 dirp->dot_ino = h2fs32(ino); 594 dirp->dotdot_ino = h2fs32(parent); 595 dp = ginode(ino); 596 bp = getdirblk(fs2h32(dp->e2di_blocks[0]), sblock.e2fs_bsize); 597 if (bp->b_errs) { 598 freeino(ino); 599 return (0); 600 } 601 memcpy(bp->b_un.b_buf, dirp, sizeof(struct ext2fs_dirtemplate)); 602 dirty(bp); 603 dp->e2di_nlink = h2fs16(2); 604 inodirty(); 605 if (ino == EXT2_ROOTINO) { 606 lncntp[ino] = fs2h16(dp->e2di_nlink); 607 cacheino(dp, ino); 608 return(ino); 609 } 610 if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { 611 freeino(ino); 612 return (0); 613 } 614 cacheino(dp, ino); 615 statemap[ino] = statemap[parent]; 616 if (statemap[ino] == DSTATE) { 617 lncntp[ino] = fs2h16(dp->e2di_nlink); 618 lncntp[parent]++; 619 } 620 dp = ginode(parent); 621 dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) + 1); 622 inodirty(); 623 return (ino); 624 } 625 626 /* 627 * free a directory inode 628 */ 629 static void 630 freedir(ino_t ino, ino_t parent) 631 { 632 struct ext2fs_dinode *dp; 633 634 if (ino != parent) { 635 dp = ginode(parent); 636 dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) - 1); 637 inodirty(); 638 } 639 freeino(ino); 640 } 641 642 /* 643 * generate a temporary name for the lost+found directory. 644 */ 645 static int 646 lftempname(char *bufp, ino_t ino) 647 { 648 ino_t in; 649 char *cp; 650 int namlen; 651 652 cp = bufp + 2; 653 for (in = maxino; in > 0; in /= 10) 654 cp++; 655 *--cp = 0; 656 namlen = cp - bufp; 657 in = ino; 658 while (cp > bufp) { 659 *--cp = (in % 10) + '0'; 660 in /= 10; 661 } 662 *cp = '#'; 663 return (namlen); 664 } 665 666 /* 667 * Get a directory block. 668 * Insure that it is held until another is requested. 669 */ 670 static struct bufarea * 671 getdirblk(daddr_t blkno, long size) 672 { 673 674 if (pdirbp != 0) 675 pdirbp->b_flags &= ~B_INUSE; 676 pdirbp = getdatablk(blkno, size); 677 return (pdirbp); 678 } 679