1 /* $NetBSD: dir.c,v 1.19 2006/05/10 21:53:15 mrg 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.19 2006/05/10 21:53:15 mrg 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 #include <err.h> 83 84 #include "fsck.h" 85 #include "fsutil.h" 86 #include "extern.h" 87 88 const char *lfname = "lost+found"; 89 int lfmode = 01777; 90 struct ext2fs_dirtemplate emptydir = { 0, DIRBLKSIZ }; 91 struct ext2fs_dirtemplate dirhead = { 92 0, 12, 1, EXT2_FT_DIR, ".", 93 0, DIRBLKSIZ - 12, 2, EXT2_FT_DIR, ".." 94 }; 95 #undef DIRBLKSIZ 96 97 static int expanddir(struct ext2fs_dinode *, char *); 98 static void freedir(ino_t, ino_t); 99 static struct ext2fs_direct *fsck_readdir(struct inodesc *); 100 static struct bufarea *getdirblk(daddr_t, long); 101 static int lftempname(char *, ino_t); 102 static int mkentry(struct inodesc *); 103 static int chgino(struct inodesc *); 104 105 /* 106 * Propagate connected state through the tree. 107 */ 108 void 109 propagate(void) 110 { 111 struct inoinfo **inpp, *inp, *pinp; 112 struct inoinfo **inpend; 113 114 /* 115 * Create a list of children for each directory. 116 */ 117 inpend = &inpsort[inplast]; 118 for (inpp = inpsort; inpp < inpend; inpp++) { 119 inp = *inpp; 120 if (inp->i_parent == 0 || 121 inp->i_number == EXT2_ROOTINO) 122 continue; 123 pinp = getinoinfo(inp->i_parent); 124 inp->i_parentp = pinp; 125 inp->i_sibling = pinp->i_child; 126 pinp->i_child = inp; 127 } 128 inp = getinoinfo(EXT2_ROOTINO); 129 while (inp) { 130 statemap[inp->i_number] = DFOUND; 131 if (inp->i_child && 132 statemap[inp->i_child->i_number] == DSTATE) 133 inp = inp->i_child; 134 else if (inp->i_sibling) 135 inp = inp->i_sibling; 136 else 137 inp = inp->i_parentp; 138 } 139 } 140 141 /* 142 * Scan each entry in a directory block. 143 */ 144 int 145 dirscan(struct inodesc *idesc) 146 { 147 struct ext2fs_direct *dp; 148 struct bufarea *bp; 149 int dsize, n; 150 long blksiz; 151 char *dbuf = NULL; 152 153 if ((dbuf = malloc(sblock.e2fs_bsize)) == NULL) 154 err(8, "Can't allocate directory block"); 155 156 if (idesc->id_type != DATA) 157 errexit("wrong type to dirscan %d\n", idesc->id_type); 158 if (idesc->id_entryno == 0 && 159 (idesc->id_filesize & (sblock.e2fs_bsize - 1)) != 0) 160 idesc->id_filesize = roundup(idesc->id_filesize, sblock.e2fs_bsize); 161 blksiz = idesc->id_numfrags * sblock.e2fs_bsize; 162 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 163 idesc->id_filesize -= blksiz; 164 free(dbuf); 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_type = 0; /* XXX gcc */ 349 newent.e2d_namlen = strlen(idesc->id_name); 350 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 351 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 352 newent.e2d_type = inot2ext2dt(typemap[idesc->id_parent]); 353 newlen = EXT2FS_DIRSIZ(newent.e2d_namlen); 354 if (dirp->e2d_ino != 0) 355 oldlen = EXT2FS_DIRSIZ(dirp->e2d_namlen); 356 else 357 oldlen = 0; 358 if (fs2h16(dirp->e2d_reclen) - oldlen < newlen) 359 return (KEEPON); 360 newent.e2d_reclen = h2fs16(fs2h16(dirp->e2d_reclen) - oldlen); 361 dirp->e2d_reclen = h2fs16(oldlen); 362 dirp = (struct ext2fs_direct *)(((char *)dirp) + oldlen); 363 dirp->e2d_ino = h2fs32(idesc->id_parent); /* ino to be entered is in id_parent */ 364 dirp->e2d_reclen = newent.e2d_reclen; 365 dirp->e2d_namlen = newent.e2d_namlen; 366 dirp->e2d_type = newent.e2d_type; 367 memcpy(dirp->e2d_name, idesc->id_name, (size_t)(dirp->e2d_namlen)); 368 return (ALTERED|STOP); 369 } 370 371 static int 372 chgino(struct inodesc *idesc) 373 { 374 struct ext2fs_direct *dirp = idesc->id_dirp; 375 u_int16_t namlen = dirp->e2d_namlen; 376 377 if (strlen(idesc->id_name) != namlen || 378 strncmp(dirp->e2d_name, idesc->id_name, (int)namlen)) 379 return (KEEPON); 380 dirp->e2d_ino = h2fs32(idesc->id_parent); 381 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 382 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 383 dirp->e2d_type = inot2ext2dt(typemap[idesc->id_parent]); 384 else 385 dirp->e2d_type = 0; 386 return (ALTERED|STOP); 387 } 388 389 int 390 linkup(ino_t orphan, ino_t parentdir) 391 { 392 struct ext2fs_dinode *dp; 393 int lostdir; 394 ino_t oldlfdir; 395 struct inodesc idesc; 396 char tempname[BUFSIZ]; 397 398 memset(&idesc, 0, sizeof(struct inodesc)); 399 dp = ginode(orphan); 400 lostdir = (fs2h16(dp->e2di_mode) & IFMT) == IFDIR; 401 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 402 pinode(orphan); 403 if (preen && inosize(dp) == 0) 404 return (0); 405 if (preen) 406 printf(" (RECONNECTED)\n"); 407 else 408 if (reply("RECONNECT") == 0) 409 return (0); 410 if (lfdir == 0) { 411 dp = ginode(EXT2_ROOTINO); 412 idesc.id_name = lfname; 413 idesc.id_type = DATA; 414 idesc.id_func = findino; 415 idesc.id_number = EXT2_ROOTINO; 416 if ((ckinode(dp, &idesc) & FOUND) != 0) { 417 lfdir = idesc.id_parent; 418 } else { 419 pwarn("NO lost+found DIRECTORY"); 420 if (preen || reply("CREATE")) { 421 lfdir = allocdir(EXT2_ROOTINO, (ino_t)0, lfmode); 422 if (lfdir != 0) { 423 if (makeentry(EXT2_ROOTINO, lfdir, lfname) != 0) { 424 if (preen) 425 printf(" (CREATED)\n"); 426 } else { 427 freedir(lfdir, EXT2_ROOTINO); 428 lfdir = 0; 429 if (preen) 430 printf("\n"); 431 } 432 } 433 } 434 } 435 if (lfdir == 0) { 436 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 437 printf("\n\n"); 438 return (0); 439 } 440 } 441 dp = ginode(lfdir); 442 if ((fs2h16(dp->e2di_mode) & IFMT) != IFDIR) { 443 pfatal("lost+found IS NOT A DIRECTORY"); 444 if (reply("REALLOCATE") == 0) 445 return (0); 446 oldlfdir = lfdir; 447 if ((lfdir = allocdir(EXT2_ROOTINO, (ino_t)0, lfmode)) == 0) { 448 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 449 return (0); 450 } 451 if ((changeino(EXT2_ROOTINO, lfname, lfdir) & ALTERED) == 0) { 452 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 453 return (0); 454 } 455 inodirty(); 456 idesc.id_type = ADDR; 457 idesc.id_func = pass4check; 458 idesc.id_number = oldlfdir; 459 adjust(&idesc, lncntp[oldlfdir] + 1); 460 lncntp[oldlfdir] = 0; 461 dp = ginode(lfdir); 462 } 463 if (statemap[lfdir] != DFOUND) { 464 pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 465 return (0); 466 } 467 (void)lftempname(tempname, orphan); 468 if (makeentry(lfdir, orphan, tempname) == 0) { 469 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 470 printf("\n\n"); 471 return (0); 472 } 473 lncntp[orphan]--; 474 if (lostdir) { 475 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 476 parentdir != (ino_t)-1) 477 (void)makeentry(orphan, lfdir, ".."); 478 dp = ginode(lfdir); 479 dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) +1); 480 inodirty(); 481 lncntp[lfdir]++; 482 pwarn("DIR I=%llu CONNECTED. ", (unsigned long long)orphan); 483 if (parentdir != (ino_t)-1) 484 printf("PARENT WAS I=%llu\n", 485 (unsigned long long)parentdir); 486 if (preen == 0) 487 printf("\n"); 488 } 489 return (1); 490 } 491 492 /* 493 * fix an entry in a directory. 494 */ 495 int 496 changeino(ino_t dir, const char *name, ino_t newnum) 497 { 498 struct inodesc idesc; 499 500 memset(&idesc, 0, sizeof(struct inodesc)); 501 idesc.id_type = DATA; 502 idesc.id_func = chgino; 503 idesc.id_number = dir; 504 idesc.id_fix = DONTKNOW; 505 idesc.id_name = name; 506 idesc.id_parent = newnum; /* new value for name */ 507 return (ckinode(ginode(dir), &idesc)); 508 } 509 510 /* 511 * make an entry in a directory 512 */ 513 int 514 makeentry(ino_t parent, ino_t ino, const char *name) 515 { 516 struct ext2fs_dinode *dp; 517 struct inodesc idesc; 518 char pathbuf[MAXPATHLEN + 1]; 519 520 if ((parent < EXT2_FIRSTINO && parent != EXT2_ROOTINO) 521 || parent >= maxino || 522 (ino < EXT2_FIRSTINO && ino < EXT2_ROOTINO) || ino >= maxino) 523 return (0); 524 memset(&idesc, 0, sizeof(struct inodesc)); 525 idesc.id_type = DATA; 526 idesc.id_func = mkentry; 527 idesc.id_number = parent; 528 idesc.id_parent = ino; /* this is the inode to enter */ 529 idesc.id_fix = DONTKNOW; 530 idesc.id_name = name; 531 dp = ginode(parent); 532 if (inosize(dp) % sblock.e2fs_bsize) { 533 inossize(dp, roundup(inosize(dp), sblock.e2fs_bsize)); 534 inodirty(); 535 } 536 if ((ckinode(dp, &idesc) & ALTERED) != 0) 537 return (1); 538 getpathname(pathbuf, sizeof(pathbuf), parent, parent); 539 dp = ginode(parent); 540 if (expanddir(dp, pathbuf) == 0) 541 return (0); 542 return (ckinode(dp, &idesc) & ALTERED); 543 } 544 545 /* 546 * Attempt to expand the size of a directory 547 */ 548 static int 549 expanddir(struct ext2fs_dinode *dp, char *name) 550 { 551 daddr_t lastbn, newblk; 552 struct bufarea *bp; 553 char *firstblk; 554 555 lastbn = lblkno(&sblock, inosize(dp)); 556 if (lastbn >= NDADDR - 1 || fs2h32(dp->e2di_blocks[lastbn]) == 0 || 557 inosize(dp) == 0) { 558 return (0); 559 } 560 if ((newblk = allocblk()) == 0) { 561 return (0); 562 } 563 dp->e2di_blocks[lastbn + 1] = dp->e2di_blocks[lastbn]; 564 dp->e2di_blocks[lastbn] = h2fs32(newblk); 565 inossize(dp, inosize(dp) + sblock.e2fs_bsize); 566 dp->e2di_nblock = h2fs32(fs2h32(dp->e2di_nblock) + 1); 567 bp = getdirblk(fs2h32(dp->e2di_blocks[lastbn + 1]), 568 sblock.e2fs_bsize); 569 if (bp->b_errs) 570 goto bad; 571 if ((firstblk = malloc(sblock.e2fs_bsize)) == NULL) 572 err(8, "cannot allocate first block"); 573 memcpy(firstblk, bp->b_un.b_buf, sblock.e2fs_bsize); 574 bp = getdirblk(newblk, sblock.e2fs_bsize); 575 if (bp->b_errs) { 576 free(firstblk); 577 goto bad; 578 } 579 memcpy(bp->b_un.b_buf, firstblk, sblock.e2fs_bsize); 580 free(firstblk); 581 dirty(bp); 582 bp = getdirblk(fs2h32(dp->e2di_blocks[lastbn + 1]), 583 sblock.e2fs_bsize); 584 if (bp->b_errs) 585 goto bad; 586 emptydir.dot_reclen = h2fs16(sblock.e2fs_bsize); 587 memcpy(bp->b_un.b_buf, &emptydir, sizeof emptydir); 588 pwarn("NO SPACE LEFT IN %s", name); 589 if (preen) 590 printf(" (EXPANDED)\n"); 591 else if (reply("EXPAND") == 0) 592 goto bad; 593 dirty(bp); 594 inodirty(); 595 return (1); 596 bad: 597 dp->e2di_blocks[lastbn] = dp->e2di_blocks[lastbn + 1]; 598 dp->e2di_blocks[lastbn + 1] = 0; 599 inossize(dp, inosize(dp) - sblock.e2fs_bsize); 600 dp->e2di_nblock = h2fs32(fs2h32(dp->e2di_nblock) - 1); 601 freeblk(newblk); 602 return (0); 603 } 604 605 /* 606 * allocate a new directory 607 */ 608 int 609 allocdir(ino_t parent, ino_t request, int mode) 610 { 611 ino_t ino; 612 struct ext2fs_dinode *dp; 613 struct bufarea *bp; 614 struct ext2fs_dirtemplate *dirp; 615 616 ino = allocino(request, IFDIR|mode); 617 dirhead.dot_reclen = h2fs16(12); /* XXX */ 618 dirhead.dotdot_reclen = h2fs16(sblock.e2fs_bsize - 12); /* XXX */ 619 dirhead.dot_namlen = 1; 620 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 621 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 622 dirhead.dot_type = EXT2_FT_DIR; 623 else 624 dirhead.dot_type = 0; 625 dirhead.dotdot_namlen = 2; 626 if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 627 (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 628 dirhead.dotdot_type = EXT2_FT_DIR; 629 else 630 dirhead.dotdot_type = 0; 631 dirp = &dirhead; 632 dirp->dot_ino = h2fs32(ino); 633 dirp->dotdot_ino = h2fs32(parent); 634 dp = ginode(ino); 635 bp = getdirblk(fs2h32(dp->e2di_blocks[0]), sblock.e2fs_bsize); 636 if (bp->b_errs) { 637 freeino(ino); 638 return (0); 639 } 640 memcpy(bp->b_un.b_buf, dirp, sizeof(struct ext2fs_dirtemplate)); 641 dirty(bp); 642 dp->e2di_nlink = h2fs16(2); 643 inodirty(); 644 if (ino == EXT2_ROOTINO) { 645 lncntp[ino] = fs2h16(dp->e2di_nlink); 646 cacheino(dp, ino); 647 return(ino); 648 } 649 if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { 650 freeino(ino); 651 return (0); 652 } 653 cacheino(dp, ino); 654 statemap[ino] = statemap[parent]; 655 if (statemap[ino] == DSTATE) { 656 lncntp[ino] = fs2h16(dp->e2di_nlink); 657 lncntp[parent]++; 658 } 659 dp = ginode(parent); 660 dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) + 1); 661 inodirty(); 662 return (ino); 663 } 664 665 /* 666 * free a directory inode 667 */ 668 static void 669 freedir(ino_t ino, ino_t parent) 670 { 671 struct ext2fs_dinode *dp; 672 673 if (ino != parent) { 674 dp = ginode(parent); 675 dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) - 1); 676 inodirty(); 677 } 678 freeino(ino); 679 } 680 681 /* 682 * generate a temporary name for the lost+found directory. 683 */ 684 static int 685 lftempname(char *bufp, ino_t ino) 686 { 687 ino_t in; 688 char *cp; 689 int namlen; 690 691 cp = bufp + 2; 692 for (in = maxino; in > 0; in /= 10) 693 cp++; 694 *--cp = 0; 695 namlen = cp - bufp; 696 in = ino; 697 while (cp > bufp) { 698 *--cp = (in % 10) + '0'; 699 in /= 10; 700 } 701 *cp = '#'; 702 return (namlen); 703 } 704 705 /* 706 * Get a directory block. 707 * Insure that it is held until another is requested. 708 */ 709 static struct bufarea * 710 getdirblk(daddr_t blkno, long size) 711 { 712 713 if (pdirbp != 0) 714 pdirbp->b_flags &= ~B_INUSE; 715 pdirbp = getdatablk(blkno, size); 716 return (pdirbp); 717 } 718