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