1 /* $NetBSD: dir.c,v 1.46 2015/09/21 01:24:23 dholland 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 #include <sys/types.h> 33 #include <sys/param.h> 34 #include <sys/time.h> 35 #include <sys/buf.h> 36 #include <sys/mount.h> 37 38 #include <ufs/lfs/lfs.h> 39 #include <ufs/lfs/lfs_accessors.h> 40 #include <ufs/lfs/lfs_inode.h> 41 42 #include <err.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 47 #include "bufcache.h" 48 #include "vnode.h" 49 #include "lfs_user.h" 50 51 #include "fsck.h" 52 #include "fsutil.h" 53 #include "extern.h" 54 55 const char *lfname = "lost+found"; 56 int lfmode = 01700; 57 #if 0 58 struct lfs_dirtemplate emptydir = { 59 .dot_ino = 0, 60 .dot_reclen = LFS_DIRBLKSIZ, 61 }; 62 struct lfs_dirtemplate dirhead = { 63 .dot_ino = 0, 64 .dot_reclen = 12, 65 .dot_type = LFS_DT_DIR, 66 .dot_namlen = 1, 67 .dot_name = ".", 68 .dotdot_ino = 0, 69 .dotdot_reclen = LFS_DIRBLKSIZ - 12, 70 .dotdot_type = LFS_DT_DIR, 71 .dotdot_namlen = 2, 72 .dotdot_name = ".." 73 }; 74 struct lfs_odirtemplate odirhead = { 75 .dot_ino = 0, 76 .dot_reclen = 12, 77 .dot_namlen = 1, 78 .dot_name = ".", 79 .dotdot_ino = 0, 80 .dotdot_reclen = LFS_DIRBLKSIZ - 12, 81 .dotdot_namlen = 2, 82 .dotdot_name = ".." 83 }; 84 #endif 85 86 static int expanddir(struct uvnode *, union lfs_dinode *, char *); 87 static void freedir(ino_t, ino_t); 88 static LFS_DIRHEADER *fsck_readdir(struct uvnode *, struct inodesc *); 89 static int lftempname(char *, ino_t); 90 static int mkentry(struct inodesc *); 91 static int chgino(struct inodesc *); 92 93 /* 94 * Propagate connected state through the tree. 95 */ 96 void 97 propagate(void) 98 { 99 struct inoinfo **inpp, *inp, *pinp; 100 struct inoinfo **inpend; 101 102 /* 103 * Create a list of children for each directory. 104 */ 105 inpend = &inpsort[inplast]; 106 for (inpp = inpsort; inpp < inpend; inpp++) { 107 inp = *inpp; 108 if (inp->i_parent == 0 || 109 inp->i_number == ULFS_ROOTINO) 110 continue; 111 pinp = getinoinfo(inp->i_parent); 112 inp->i_parentp = pinp; 113 inp->i_sibling = pinp->i_child; 114 pinp->i_child = inp; 115 } 116 inp = getinoinfo(ULFS_ROOTINO); 117 while (inp) { 118 statemap[inp->i_number] = DFOUND; 119 if (inp->i_child && 120 statemap[inp->i_child->i_number] == DSTATE) 121 inp = inp->i_child; 122 else if (inp->i_sibling) 123 inp = inp->i_sibling; 124 else 125 inp = inp->i_parentp; 126 } 127 } 128 129 /* 130 * Scan each entry in a directory block. 131 */ 132 int 133 dirscan(struct inodesc *idesc) 134 { 135 LFS_DIRHEADER *dp; 136 struct ubuf *bp; 137 int dsize, n; 138 long blksiz; 139 char dbuf[LFS_DIRBLKSIZ]; 140 struct uvnode *vp; 141 142 if (idesc->id_type != DATA) 143 errexit("wrong type to dirscan %d", idesc->id_type); 144 if (idesc->id_entryno == 0 && 145 (idesc->id_filesize & (LFS_DIRBLKSIZ - 1)) != 0) 146 idesc->id_filesize = roundup(idesc->id_filesize, LFS_DIRBLKSIZ); 147 blksiz = idesc->id_numfrags * lfs_sb_getfsize(fs); 148 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 149 idesc->id_filesize -= blksiz; 150 return (SKIP); 151 } 152 idesc->id_loc = 0; 153 154 vp = vget(fs, idesc->id_number); 155 for (dp = fsck_readdir(vp, idesc); dp != NULL; 156 dp = fsck_readdir(vp, idesc)) { 157 dsize = lfs_dir_getreclen(fs, dp); 158 memcpy(dbuf, dp, (size_t) dsize); 159 idesc->id_dirp = (LFS_DIRHEADER *) dbuf; 160 if ((n = (*idesc->id_func) (idesc)) & ALTERED) { 161 bread(vp, idesc->id_lblkno, blksiz, 0, &bp); 162 memcpy(bp->b_data + idesc->id_loc - dsize, dbuf, 163 (size_t) dsize); 164 VOP_BWRITE(bp); 165 sbdirty(); 166 } 167 if (n & STOP) 168 return (n); 169 } 170 return (idesc->id_filesize > 0 ? KEEPON : STOP); 171 } 172 173 /* 174 * get next entry in a directory. 175 */ 176 static LFS_DIRHEADER * 177 fsck_readdir(struct uvnode *vp, struct inodesc *idesc) 178 { 179 LFS_DIRHEADER *dp, *ndp; 180 struct ubuf *bp; 181 long size, blksiz, fix, dploc; 182 183 blksiz = idesc->id_numfrags * lfs_sb_getfsize(fs); 184 bread(vp, idesc->id_lblkno, blksiz, 0, &bp); 185 if (idesc->id_loc % LFS_DIRBLKSIZ == 0 && idesc->id_filesize > 0 && 186 idesc->id_loc < blksiz) { 187 dp = (LFS_DIRHEADER *) (bp->b_data + idesc->id_loc); 188 if (dircheck(idesc, dp)) 189 goto dpok; 190 brelse(bp, 0); 191 if (idesc->id_fix == IGNORE) 192 return (0); 193 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 194 bread(vp, idesc->id_lblkno, blksiz, 0, &bp); 195 dp = (LFS_DIRHEADER *) (bp->b_data + idesc->id_loc); 196 lfs_dir_setino(fs, dp, 0); 197 lfs_dir_settype(fs, dp, LFS_DT_UNKNOWN); 198 lfs_dir_setnamlen(fs, dp, 0); 199 lfs_dir_setreclen(fs, dp, LFS_DIRBLKSIZ); 200 /* for now at least, don't zero the old contents */ 201 /*lfs_copydirname(fs, lfs_dir_nameptr(fs, dp), "", 0, LFS_DIRBLKSIZ);*/ 202 lfs_dir_nameptr(fs, dp)[0] = '\0'; 203 if (fix) 204 VOP_BWRITE(bp); 205 else 206 brelse(bp, 0); 207 idesc->id_loc += LFS_DIRBLKSIZ; 208 idesc->id_filesize -= LFS_DIRBLKSIZ; 209 return (dp); 210 } 211 dpok: 212 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) { 213 brelse(bp, 0); 214 return NULL; 215 } 216 dploc = idesc->id_loc; 217 dp = (LFS_DIRHEADER *) (bp->b_data + dploc); 218 idesc->id_loc += lfs_dir_getreclen(fs, dp); 219 idesc->id_filesize -= lfs_dir_getreclen(fs, dp); 220 if ((idesc->id_loc % LFS_DIRBLKSIZ) == 0) { 221 brelse(bp, 0); 222 return dp; 223 } 224 ndp = (LFS_DIRHEADER *) (bp->b_data + idesc->id_loc); 225 if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && 226 dircheck(idesc, ndp) == 0) { 227 brelse(bp, 0); 228 size = LFS_DIRBLKSIZ - (idesc->id_loc % LFS_DIRBLKSIZ); 229 idesc->id_loc += size; 230 idesc->id_filesize -= size; 231 if (idesc->id_fix == IGNORE) 232 return 0; 233 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 234 bread(vp, idesc->id_lblkno, blksiz, 0, &bp); 235 dp = (LFS_DIRHEADER *) (bp->b_data + dploc); 236 lfs_dir_setreclen(fs, dp, lfs_dir_getreclen(fs, dp) + size); 237 if (fix) 238 VOP_BWRITE(bp); 239 else 240 brelse(bp, 0); 241 } else 242 brelse(bp, 0); 243 244 return (dp); 245 } 246 247 /* 248 * Verify that a directory entry is valid. 249 * This is a superset of the checks made in the kernel. 250 */ 251 int 252 dircheck(struct inodesc *idesc, LFS_DIRHEADER *dp) 253 { 254 int size; 255 const char *cp; 256 u_char namlen, type; 257 int spaceleft; 258 259 spaceleft = LFS_DIRBLKSIZ - (idesc->id_loc % LFS_DIRBLKSIZ); 260 if (lfs_dir_getino(fs, dp) >= maxino || 261 lfs_dir_getreclen(fs, dp) == 0 || 262 lfs_dir_getreclen(fs, dp) > spaceleft || 263 (lfs_dir_getreclen(fs, dp) & 0x3) != 0) { 264 pwarn("ino too large, reclen=0, reclen>space, or reclen&3!=0\n"); 265 pwarn("dp->d_ino = 0x%jx\tdp->d_reclen = 0x%x\n", 266 (uintmax_t)lfs_dir_getino(fs, dp), 267 lfs_dir_getreclen(fs, dp)); 268 pwarn("maxino = %ju\tspaceleft = 0x%x\n", 269 (uintmax_t)maxino, spaceleft); 270 return (0); 271 } 272 if (lfs_dir_getino(fs, dp) == 0) 273 return (1); 274 size = LFS_DIRSIZ(fs, dp); 275 namlen = lfs_dir_getnamlen(fs, dp); 276 type = lfs_dir_gettype(fs, dp); 277 if (lfs_dir_getreclen(fs, dp) < size || 278 idesc->id_filesize < size || 279 /* namlen > MAXNAMLEN || */ 280 type > 15) { 281 printf("reclen<size, filesize<size, namlen too large, or type>15\n"); 282 return (0); 283 } 284 cp = lfs_dir_nameptr(fs, dp); 285 for (size = 0; size < namlen; size++) 286 if (*cp == '\0' || (*cp++ == '/')) { 287 printf("name contains NUL or /\n"); 288 return (0); 289 } 290 if (*cp != '\0') { 291 printf("name size misstated\n"); 292 return (0); 293 } 294 return (1); 295 } 296 297 void 298 direrror(ino_t ino, const char *errmesg) 299 { 300 301 fileerror(ino, ino, errmesg); 302 } 303 304 void 305 fileerror(ino_t cwd, ino_t ino, const char *errmesg) 306 { 307 char pathbuf[MAXPATHLEN + 1]; 308 struct uvnode *vp; 309 310 pwarn("%s ", errmesg); 311 pinode(ino); 312 printf("\n"); 313 pwarn("PARENT=%lld\n", (long long)cwd); 314 getpathname(pathbuf, sizeof(pathbuf), cwd, ino); 315 if (ino < ULFS_ROOTINO || ino >= maxino) { 316 pfatal("NAME=%s\n", pathbuf); 317 return; 318 } 319 vp = vget(fs, ino); 320 if (vp == NULL) 321 pfatal("INO is NULL\n"); 322 else { 323 if (ftypeok(VTOD(vp))) 324 pfatal("%s=%s\n", 325 (lfs_dino_getmode(fs, VTOI(vp)->i_din) & LFS_IFMT) == LFS_IFDIR ? 326 "DIR" : "FILE", pathbuf); 327 else 328 pfatal("NAME=%s\n", pathbuf); 329 } 330 } 331 332 void 333 adjust(struct inodesc *idesc, short lcnt) 334 { 335 struct uvnode *vp; 336 union lfs_dinode *dp; 337 338 /* 339 * XXX: (1) since lcnt is apparently a delta, rename it; (2) 340 * why is it a value to *subtract*? that is unnecessarily 341 * confusing. 342 */ 343 344 vp = vget(fs, idesc->id_number); 345 dp = VTOD(vp); 346 if (lfs_dino_getnlink(fs, dp) == lcnt) { 347 if (linkup(idesc->id_number, (ino_t) 0) == 0) 348 clri(idesc, "UNREF", 0); 349 } else { 350 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 351 ((lfs_dino_getmode(fs, dp) & LFS_IFMT) == LFS_IFDIR ? "DIR" : "FILE")); 352 pinode(idesc->id_number); 353 printf(" COUNT %d SHOULD BE %d", 354 lfs_dino_getnlink(fs, dp), lfs_dino_getnlink(fs, dp) - lcnt); 355 if (preen) { 356 if (lcnt < 0) { 357 printf("\n"); 358 pfatal("LINK COUNT INCREASING"); 359 } 360 printf(" (ADJUSTED)\n"); 361 } 362 if (preen || reply("ADJUST") == 1) { 363 lfs_dino_setnlink(fs, dp, 364 lfs_dino_getnlink(fs, dp) - lcnt); 365 inodirty(VTOI(vp)); 366 } 367 } 368 } 369 370 static int 371 mkentry(struct inodesc *idesc) 372 { 373 LFS_DIRHEADER *dirp = idesc->id_dirp; 374 unsigned namlen; 375 unsigned newreclen, oldreclen; 376 377 /* figure the length needed for id_name */ 378 namlen = strlen(idesc->id_name); 379 newreclen = LFS_DIRECTSIZ(fs, namlen); 380 381 /* find the minimum record length for the existing name */ 382 if (lfs_dir_getino(fs, dirp) != 0) 383 oldreclen = LFS_DIRSIZ(fs, dirp); 384 else 385 oldreclen = 0; 386 387 /* Can we insert here? */ 388 if (lfs_dir_getreclen(fs, dirp) - oldreclen < newreclen) 389 return (KEEPON); 390 391 /* Divide the record; all but oldreclen goes to the new record */ 392 newreclen = lfs_dir_getreclen(fs, dirp) - oldreclen; 393 lfs_dir_setreclen(fs, dirp, oldreclen); 394 395 /* advance the pointer to the new record */ 396 dirp = LFS_NEXTDIR(fs, dirp); 397 398 /* write record; ino to be entered is in id_parent */ 399 lfs_dir_setino(fs, dirp, idesc->id_parent); 400 lfs_dir_setreclen(fs, dirp, newreclen); 401 lfs_dir_settype(fs, dirp, typemap[idesc->id_parent]); 402 lfs_dir_setnamlen(fs, dirp, namlen); 403 lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), idesc->id_name, 404 namlen, newreclen); 405 406 return (ALTERED | STOP); 407 } 408 409 static int 410 chgino(struct inodesc *idesc) 411 { 412 LFS_DIRHEADER *dirp = idesc->id_dirp; 413 int namlen; 414 415 namlen = lfs_dir_getnamlen(fs, dirp); 416 if (memcmp(lfs_dir_nameptr(fs, dirp), idesc->id_name, namlen + 1)) 417 return (KEEPON); 418 lfs_dir_setino(fs, dirp, idesc->id_parent); 419 lfs_dir_settype(fs, dirp, typemap[idesc->id_parent]); 420 return (ALTERED | STOP); 421 } 422 423 int 424 linkup(ino_t orphan, ino_t parentdir) 425 { 426 union lfs_dinode *dp; 427 int lostdir; 428 ino_t oldlfdir; 429 struct inodesc idesc; 430 char tempname[BUFSIZ]; 431 struct uvnode *vp; 432 433 memset(&idesc, 0, sizeof(struct inodesc)); 434 vp = vget(fs, orphan); 435 dp = VTOD(vp); 436 lostdir = (lfs_dino_getmode(fs, dp) & LFS_IFMT) == LFS_IFDIR; 437 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 438 pinode(orphan); 439 if (preen && lfs_dino_getsize(fs, dp) == 0) 440 return (0); 441 if (preen) 442 printf(" (RECONNECTED)\n"); 443 else if (reply("RECONNECT") == 0) 444 return (0); 445 if (lfdir == 0) { 446 dp = ginode(ULFS_ROOTINO); 447 idesc.id_name = lfname; 448 idesc.id_type = DATA; 449 idesc.id_func = findino; 450 idesc.id_number = ULFS_ROOTINO; 451 if ((ckinode(dp, &idesc) & FOUND) != 0) { 452 lfdir = idesc.id_parent; 453 } else { 454 pwarn("NO lost+found DIRECTORY"); 455 if (preen || reply("CREATE")) { 456 lfdir = allocdir(ULFS_ROOTINO, (ino_t) 0, lfmode); 457 if (lfdir != 0) { 458 if (makeentry(ULFS_ROOTINO, lfdir, lfname) != 0) { 459 if (preen) 460 printf(" (CREATED)\n"); 461 } else { 462 freedir(lfdir, ULFS_ROOTINO); 463 lfdir = 0; 464 if (preen) 465 printf("\n"); 466 } 467 } 468 } 469 } 470 if (lfdir == 0) { 471 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 472 printf("\n\n"); 473 return (0); 474 } 475 } 476 vp = vget(fs, lfdir); 477 dp = VTOD(vp); 478 if ((lfs_dino_getmode(fs, dp) & LFS_IFMT) != LFS_IFDIR) { 479 pfatal("lost+found IS NOT A DIRECTORY"); 480 if (reply("REALLOCATE") == 0) 481 return (0); 482 oldlfdir = lfdir; 483 if ((lfdir = allocdir(ULFS_ROOTINO, (ino_t) 0, lfmode)) == 0) { 484 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 485 return (0); 486 } 487 if ((changeino(ULFS_ROOTINO, lfname, lfdir) & ALTERED) == 0) { 488 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 489 return (0); 490 } 491 inodirty(VTOI(vp)); 492 idesc.id_type = ADDR; 493 idesc.id_func = pass4check; 494 idesc.id_number = oldlfdir; 495 adjust(&idesc, lncntp[oldlfdir] + 1); 496 lncntp[oldlfdir] = 0; 497 vp = vget(fs, lfdir); 498 dp = VTOD(vp); 499 } 500 if (statemap[lfdir] != DFOUND) { 501 pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 502 return (0); 503 } 504 (void) lftempname(tempname, orphan); 505 if (makeentry(lfdir, orphan, tempname) == 0) { 506 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 507 printf("\n\n"); 508 return (0); 509 } 510 lncntp[orphan]--; 511 if (lostdir) { 512 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 513 parentdir != (ino_t) - 1) 514 (void) makeentry(orphan, lfdir, ".."); 515 vp = vget(fs, lfdir); 516 lfs_dino_setnlink(fs, VTOI(vp)->i_din, 517 lfs_dino_getnlink(fs, VTOI(vp)->i_din) + 1); 518 inodirty(VTOI(vp)); 519 lncntp[lfdir]++; 520 pwarn("DIR I=%llu CONNECTED. ", (unsigned long long)orphan); 521 if (parentdir != (ino_t) - 1) 522 printf("PARENT WAS I=%llu\n", 523 (unsigned long long)parentdir); 524 if (preen == 0) 525 printf("\n"); 526 } 527 return (1); 528 } 529 530 /* 531 * fix an entry in a directory. 532 */ 533 int 534 changeino(ino_t dir, const char *name, ino_t newnum) 535 { 536 struct inodesc idesc; 537 538 memset(&idesc, 0, sizeof(struct inodesc)); 539 idesc.id_type = DATA; 540 idesc.id_func = chgino; 541 idesc.id_number = dir; 542 idesc.id_fix = DONTKNOW; 543 idesc.id_name = name; 544 idesc.id_parent = newnum; /* new value for name */ 545 546 return (ckinode(ginode(dir), &idesc)); 547 } 548 549 /* 550 * make an entry in a directory 551 */ 552 int 553 makeentry(ino_t parent, ino_t ino, const char *name) 554 { 555 union lfs_dinode *dp; 556 struct inodesc idesc; 557 char pathbuf[MAXPATHLEN + 1]; 558 struct uvnode *vp; 559 uint64_t size; 560 561 if (parent < ULFS_ROOTINO || parent >= maxino || 562 ino < ULFS_ROOTINO || ino >= maxino) 563 return (0); 564 memset(&idesc, 0, sizeof(struct inodesc)); 565 idesc.id_type = DATA; 566 idesc.id_func = mkentry; 567 idesc.id_number = parent; 568 idesc.id_parent = ino; /* this is the inode to enter */ 569 idesc.id_fix = DONTKNOW; 570 idesc.id_name = name; 571 vp = vget(fs, parent); 572 dp = VTOD(vp); 573 size = lfs_dino_getsize(fs, dp); 574 if (size % LFS_DIRBLKSIZ) { 575 size = roundup(size, LFS_DIRBLKSIZ); 576 lfs_dino_setsize(fs, dp, size); 577 inodirty(VTOI(vp)); 578 } 579 if ((ckinode(dp, &idesc) & ALTERED) != 0) 580 return (1); 581 getpathname(pathbuf, sizeof(pathbuf), parent, parent); 582 vp = vget(fs, parent); 583 dp = VTOD(vp); 584 if (expanddir(vp, dp, pathbuf) == 0) 585 return (0); 586 return (ckinode(dp, &idesc) & ALTERED); 587 } 588 589 /* 590 * Initialize a completely empty directory block. 591 * (block size is LFS_DIRBLKSIZ) 592 */ 593 static void 594 zerodirblk(void *buf) 595 { 596 LFS_DIRHEADER *dirp; 597 598 dirp = buf; 599 lfs_dir_setino(fs, dirp, 0); 600 lfs_dir_setreclen(fs, dirp, LFS_DIRBLKSIZ); 601 lfs_dir_settype(fs, dirp, LFS_DT_UNKNOWN); 602 lfs_dir_setnamlen(fs, dirp, 0); 603 lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), "", 0, 604 LFS_DIRBLKSIZ); 605 } 606 607 /* 608 * Attempt to expand the size of a directory 609 */ 610 static int 611 expanddir(struct uvnode *vp, union lfs_dinode *dp, char *name) 612 { 613 daddr_t lastbn; 614 struct ubuf *bp; 615 char *cp, firstblk[LFS_DIRBLKSIZ]; 616 617 lastbn = lfs_lblkno(fs, lfs_dino_getsize(fs, dp)); 618 if (lastbn >= ULFS_NDADDR - 1 || lfs_dino_getdb(fs, dp, lastbn) == 0 || 619 lfs_dino_getsize(fs, dp) == 0) 620 return (0); 621 lfs_dino_setdb(fs, dp, lastbn + 1, lfs_dino_getdb(fs, dp, lastbn)); 622 lfs_dino_setdb(fs, dp, lastbn, 0); 623 bp = getblk(vp, lastbn, lfs_sb_getbsize(fs)); 624 VOP_BWRITE(bp); 625 lfs_dino_setsize(fs, dp, 626 lfs_dino_getsize(fs, dp) + lfs_sb_getbsize(fs)); 627 lfs_dino_setblocks(fs, dp, 628 lfs_dino_getblocks(fs, dp) + lfs_btofsb(fs, lfs_sb_getbsize(fs))); 629 bread(vp, lfs_dino_getdb(fs, dp, lastbn + 1), 630 (long) lfs_dblksize(fs, dp, lastbn + 1), 0, &bp); 631 if (bp->b_flags & B_ERROR) 632 goto bad; 633 memcpy(firstblk, bp->b_data, LFS_DIRBLKSIZ); 634 bread(vp, lastbn, lfs_sb_getbsize(fs), 0, &bp); 635 if (bp->b_flags & B_ERROR) 636 goto bad; 637 memcpy(bp->b_data, firstblk, LFS_DIRBLKSIZ); 638 for (cp = &bp->b_data[LFS_DIRBLKSIZ]; 639 cp < &bp->b_data[lfs_sb_getbsize(fs)]; 640 cp += LFS_DIRBLKSIZ) 641 zerodirblk(cp); 642 VOP_BWRITE(bp); 643 bread(vp, lfs_dino_getdb(fs, dp, lastbn + 1), 644 (long) lfs_dblksize(fs, dp, lastbn + 1), 0, &bp); 645 if (bp->b_flags & B_ERROR) 646 goto bad; 647 zerodirblk(bp->b_data); 648 pwarn("NO SPACE LEFT IN %s", name); 649 if (preen) 650 printf(" (EXPANDED)\n"); 651 else if (reply("EXPAND") == 0) 652 goto bad; 653 VOP_BWRITE(bp); 654 inodirty(VTOI(vp)); 655 return (1); 656 bad: 657 lfs_dino_setdb(fs, dp, lastbn, lfs_dino_getdb(fs, dp, lastbn + 1)); 658 lfs_dino_setdb(fs, dp, lastbn + 1, 0); 659 lfs_dino_setsize(fs, dp, 660 lfs_dino_getsize(fs, dp) - lfs_sb_getbsize(fs)); 661 lfs_dino_setblocks(fs, dp, 662 lfs_dino_getblocks(fs, dp) - lfs_btofsb(fs, lfs_sb_getbsize(fs))); 663 return (0); 664 } 665 666 /* 667 * allocate a new directory 668 */ 669 int 670 allocdir(ino_t parent, ino_t request, int mode) 671 { 672 ino_t ino; 673 char *cp; 674 union lfs_dinode *dp; 675 struct ubuf *bp; 676 LFS_DIRHEADER *dirp; 677 struct uvnode *vp; 678 679 ino = allocino(request, LFS_IFDIR | mode); 680 vp = vget(fs, ino); 681 dp = VTOD(vp); 682 bread(vp, lfs_dino_getdb(fs, dp, 0), lfs_sb_getfsize(fs), 0, &bp); 683 if (bp->b_flags & B_ERROR) { 684 brelse(bp, 0); 685 freeino(ino); 686 return (0); 687 } 688 dirp = (LFS_DIRHEADER *)bp->b_data; 689 /* . */ 690 lfs_dir_setino(fs, dirp, ino); 691 lfs_dir_setreclen(fs, dirp, LFS_DIRECTSIZ(fs, 1)); 692 lfs_dir_settype(fs, dirp, LFS_DT_DIR); 693 lfs_dir_setnamlen(fs, dirp, 1); 694 lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), ".", 1, 695 LFS_DIRECTSIZ(fs, 1)); 696 /* .. */ 697 dirp = LFS_NEXTDIR(fs, dirp); 698 lfs_dir_setino(fs, dirp, parent); 699 lfs_dir_setreclen(fs, dirp, LFS_DIRBLKSIZ - LFS_DIRECTSIZ(fs, 1)); 700 lfs_dir_settype(fs, dirp, LFS_DT_DIR); 701 lfs_dir_setnamlen(fs, dirp, 2); 702 lfs_copydirname(fs, lfs_dir_nameptr(fs, dirp), "..", 2, 703 LFS_DIRBLKSIZ - LFS_DIRECTSIZ(fs, 1)); 704 for (cp = &bp->b_data[LFS_DIRBLKSIZ]; 705 cp < &bp->b_data[lfs_sb_getfsize(fs)]; 706 cp += LFS_DIRBLKSIZ) { 707 zerodirblk(cp); 708 } 709 VOP_BWRITE(bp); 710 lfs_dino_setnlink(fs, dp, 2); 711 inodirty(VTOI(vp)); 712 if (ino == ULFS_ROOTINO) { 713 lncntp[ino] = lfs_dino_getnlink(fs, dp); 714 cacheino(dp, ino); 715 return (ino); 716 } 717 if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { 718 freeino(ino); 719 return (0); 720 } 721 cacheino(dp, ino); 722 statemap[ino] = statemap[parent]; 723 if (statemap[ino] == DSTATE) { 724 lncntp[ino] = lfs_dino_getnlink(fs, dp); 725 lncntp[parent]++; 726 } 727 vp = vget(fs, parent); 728 dp = VTOD(vp); 729 lfs_dino_setnlink(fs, dp, lfs_dino_getnlink(fs, dp) + 1); 730 inodirty(VTOI(vp)); 731 return (ino); 732 } 733 734 /* 735 * free a directory inode 736 */ 737 static void 738 freedir(ino_t ino, ino_t parent) 739 { 740 struct uvnode *vp; 741 742 if (ino != parent) { 743 vp = vget(fs, parent); 744 lfs_dino_setnlink(fs, VTOI(vp)->i_din, 745 lfs_dino_getnlink(fs, VTOI(vp)->i_din) - 1); 746 inodirty(VTOI(vp)); 747 } 748 freeino(ino); 749 } 750 751 /* 752 * generate a temporary name for the lost+found directory. 753 */ 754 static int 755 lftempname(char *bufp, ino_t ino) 756 { 757 ino_t in; 758 char *cp; 759 int namlen; 760 761 cp = bufp + 2; 762 for (in = maxino; in > 0; in /= 10) 763 cp++; 764 *--cp = 0; 765 namlen = cp - bufp; 766 in = ino; 767 while (cp > bufp) { 768 *--cp = (in % 10) + '0'; 769 in /= 10; 770 } 771 *cp = '#'; 772 return (namlen); 773 } 774