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