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