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