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