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