1 /* 2 * Copyright (c) 1980, 1986 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)dir.c 5.20 (Berkeley) 11/04/91"; 10 #endif /* not lint */ 11 12 #include <sys/param.h> 13 #include <ufs/ufs/dinode.h> 14 #define KERNEL 15 #include <ufs/ufs/dir.h> 16 #undef KERNEL 17 #include <ufs/ffs/fs.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include "fsck.h" 21 22 char *lfname = "lost+found"; 23 int lfmode = 01777; 24 struct dirtemplate emptydir = { 0, DIRBLKSIZ }; 25 struct dirtemplate dirhead = { 0, 12, 1, ".", 0, DIRBLKSIZ - 12, 2, ".." }; 26 27 struct direct *fsck_readdir(); 28 struct bufarea *getdirblk(); 29 30 /* 31 * Propagate connected state through the tree. 32 */ 33 propagate() 34 { 35 register struct inoinfo **inpp, *inp; 36 struct inoinfo **inpend; 37 long change; 38 39 inpend = &inpsort[inplast]; 40 do { 41 change = 0; 42 for (inpp = inpsort; inpp < inpend; inpp++) { 43 inp = *inpp; 44 if (inp->i_parent == 0) 45 continue; 46 if (statemap[inp->i_parent] == DFOUND && 47 statemap[inp->i_number] == DSTATE) { 48 statemap[inp->i_number] = DFOUND; 49 change++; 50 } 51 } 52 } while (change > 0); 53 } 54 55 /* 56 * Scan each entry in a directory block. 57 */ 58 dirscan(idesc) 59 register struct inodesc *idesc; 60 { 61 register struct direct *dp; 62 register struct bufarea *bp; 63 int dsize, n; 64 long blksiz; 65 char dbuf[DIRBLKSIZ]; 66 67 if (idesc->id_type != DATA) 68 errexit("wrong type to dirscan %d\n", idesc->id_type); 69 if (idesc->id_entryno == 0 && 70 (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) 71 idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); 72 blksiz = idesc->id_numfrags * sblock.fs_fsize; 73 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 74 idesc->id_filesize -= blksiz; 75 return (SKIP); 76 } 77 idesc->id_loc = 0; 78 for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 79 dsize = dp->d_reclen; 80 bcopy((char *)dp, dbuf, (size_t)dsize); 81 idesc->id_dirp = (struct direct *)dbuf; 82 if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 83 bp = getdirblk(idesc->id_blkno, blksiz); 84 bcopy(dbuf, bp->b_un.b_buf + idesc->id_loc - dsize, 85 (size_t)dsize); 86 dirty(bp); 87 sbdirty(); 88 } 89 if (n & STOP) 90 return (n); 91 } 92 return (idesc->id_filesize > 0 ? KEEPON : STOP); 93 } 94 95 /* 96 * get next entry in a directory. 97 */ 98 struct direct * 99 fsck_readdir(idesc) 100 register struct inodesc *idesc; 101 { 102 register struct direct *dp, *ndp; 103 register struct bufarea *bp; 104 long size, blksiz, fix; 105 106 blksiz = idesc->id_numfrags * sblock.fs_fsize; 107 bp = getdirblk(idesc->id_blkno, blksiz); 108 if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 && 109 idesc->id_loc < blksiz) { 110 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 111 if (dircheck(idesc, dp)) 112 goto dpok; 113 idesc->id_loc += DIRBLKSIZ; 114 idesc->id_filesize -= DIRBLKSIZ; 115 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 116 bp = getdirblk(idesc->id_blkno, blksiz); 117 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 118 dp->d_reclen = DIRBLKSIZ; 119 dp->d_ino = 0; 120 dp->d_namlen = 0; 121 dp->d_name[0] = '\0'; 122 if (fix) 123 dirty(bp); 124 return (dp); 125 } 126 dpok: 127 if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 128 return NULL; 129 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 130 idesc->id_loc += dp->d_reclen; 131 idesc->id_filesize -= dp->d_reclen; 132 if ((idesc->id_loc % DIRBLKSIZ) == 0) 133 return (dp); 134 ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 135 if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && 136 dircheck(idesc, ndp) == 0) { 137 size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 138 idesc->id_loc += size; 139 idesc->id_filesize -= size; 140 fix = dofix(idesc, "DIRECTORY CORRUPTED"); 141 bp = getdirblk(idesc->id_blkno, blksiz); 142 dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); 143 dp->d_reclen += size; 144 if (fix) 145 dirty(bp); 146 } 147 return (dp); 148 } 149 150 /* 151 * Verify that a directory entry is valid. 152 * This is a superset of the checks made in the kernel. 153 */ 154 dircheck(idesc, dp) 155 struct inodesc *idesc; 156 register struct direct *dp; 157 { 158 register int size; 159 register char *cp; 160 int spaceleft; 161 162 size = DIRSIZ(dp); 163 spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); 164 if (dp->d_ino < maxino && 165 dp->d_reclen != 0 && 166 dp->d_reclen <= spaceleft && 167 (dp->d_reclen & 0x3) == 0 && 168 dp->d_reclen >= size && 169 idesc->id_filesize >= size && 170 dp->d_namlen <= MAXNAMLEN) { 171 if (dp->d_ino == 0) 172 return (1); 173 for (cp = dp->d_name, size = 0; size < dp->d_namlen; size++) 174 if (*cp == 0 || (*cp++ == '/')) 175 return (0); 176 if (*cp == 0) 177 return (1); 178 } 179 return (0); 180 } 181 182 direrror(ino, errmesg) 183 ino_t ino; 184 char *errmesg; 185 { 186 187 fileerror(ino, ino, errmesg); 188 } 189 190 fileerror(cwd, ino, errmesg) 191 ino_t cwd, ino; 192 char *errmesg; 193 { 194 register struct dinode *dp; 195 char pathbuf[MAXPATHLEN + 1]; 196 197 pwarn("%s ", errmesg); 198 pinode(ino); 199 printf("\n"); 200 getpathname(pathbuf, cwd, ino); 201 if (ino < ROOTINO || ino > maxino) { 202 pfatal("NAME=%s\n", pathbuf); 203 return; 204 } 205 dp = ginode(ino); 206 if (ftypeok(dp)) 207 pfatal("%s=%s\n", 208 (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf); 209 else 210 pfatal("NAME=%s\n", pathbuf); 211 } 212 213 adjust(idesc, lcnt) 214 register struct inodesc *idesc; 215 short lcnt; 216 { 217 register struct dinode *dp; 218 219 dp = ginode(idesc->id_number); 220 if (dp->di_nlink == lcnt) { 221 if (linkup(idesc->id_number, (ino_t)0) == 0) 222 clri(idesc, "UNREF", 0); 223 } else { 224 pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 225 ((dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE")); 226 pinode(idesc->id_number); 227 printf(" COUNT %d SHOULD BE %d", 228 dp->di_nlink, dp->di_nlink - lcnt); 229 if (preen) { 230 if (lcnt < 0) { 231 printf("\n"); 232 pfatal("LINK COUNT INCREASING"); 233 } 234 printf(" (ADJUSTED)\n"); 235 } 236 if (preen || reply("ADJUST") == 1) { 237 dp->di_nlink -= lcnt; 238 inodirty(); 239 } 240 } 241 } 242 243 mkentry(idesc) 244 struct inodesc *idesc; 245 { 246 register struct direct *dirp = idesc->id_dirp; 247 struct direct newent; 248 int newlen, oldlen; 249 250 newent.d_namlen = strlen(idesc->id_name); 251 newlen = DIRSIZ(&newent); 252 if (dirp->d_ino != 0) 253 oldlen = DIRSIZ(dirp); 254 else 255 oldlen = 0; 256 if (dirp->d_reclen - oldlen < newlen) 257 return (KEEPON); 258 newent.d_reclen = dirp->d_reclen - oldlen; 259 dirp->d_reclen = oldlen; 260 dirp = (struct direct *)(((char *)dirp) + oldlen); 261 dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */ 262 dirp->d_reclen = newent.d_reclen; 263 dirp->d_namlen = newent.d_namlen; 264 bcopy(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1); 265 return (ALTERED|STOP); 266 } 267 268 chgino(idesc) 269 struct inodesc *idesc; 270 { 271 register struct direct *dirp = idesc->id_dirp; 272 273 if (bcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) 274 return (KEEPON); 275 dirp->d_ino = idesc->id_parent; 276 return (ALTERED|STOP); 277 } 278 279 linkup(orphan, parentdir) 280 ino_t orphan; 281 ino_t parentdir; 282 { 283 register struct dinode *dp; 284 int lostdir; 285 ino_t oldlfdir; 286 struct inodesc idesc; 287 char tempname[BUFSIZ]; 288 extern int pass4check(); 289 290 bzero((char *)&idesc, sizeof(struct inodesc)); 291 dp = ginode(orphan); 292 lostdir = (dp->di_mode & IFMT) == IFDIR; 293 pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 294 pinode(orphan); 295 if (preen && dp->di_size == 0) 296 return (0); 297 if (preen) 298 printf(" (RECONNECTED)\n"); 299 else 300 if (reply("RECONNECT") == 0) 301 return (0); 302 if (lfdir == 0) { 303 dp = ginode(ROOTINO); 304 idesc.id_name = lfname; 305 idesc.id_type = DATA; 306 idesc.id_func = findino; 307 idesc.id_number = ROOTINO; 308 if ((ckinode(dp, &idesc) & FOUND) != 0) { 309 lfdir = idesc.id_parent; 310 } else { 311 pwarn("NO lost+found DIRECTORY"); 312 if (preen || reply("CREATE")) { 313 lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); 314 if (lfdir != 0) { 315 if (makeentry(ROOTINO, lfdir, lfname) != 0) { 316 if (preen) 317 printf(" (CREATED)\n"); 318 } else { 319 freedir(lfdir, ROOTINO); 320 lfdir = 0; 321 if (preen) 322 printf("\n"); 323 } 324 } 325 } 326 } 327 if (lfdir == 0) { 328 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 329 printf("\n\n"); 330 return (0); 331 } 332 } 333 dp = ginode(lfdir); 334 if ((dp->di_mode & IFMT) != IFDIR) { 335 pfatal("lost+found IS NOT A DIRECTORY"); 336 if (reply("REALLOCATE") == 0) 337 return (0); 338 oldlfdir = lfdir; 339 if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) { 340 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 341 return (0); 342 } 343 if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) { 344 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 345 return (0); 346 } 347 inodirty(); 348 idesc.id_type = ADDR; 349 idesc.id_func = pass4check; 350 idesc.id_number = oldlfdir; 351 adjust(&idesc, lncntp[oldlfdir] + 1); 352 lncntp[oldlfdir] = 0; 353 dp = ginode(lfdir); 354 } 355 if (statemap[lfdir] != DFOUND) { 356 pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 357 return (0); 358 } 359 (void)lftempname(tempname, orphan); 360 if (makeentry(lfdir, orphan, tempname) == 0) { 361 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 362 printf("\n\n"); 363 return (0); 364 } 365 lncntp[orphan]--; 366 if (lostdir) { 367 if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 368 parentdir != (ino_t)-1) 369 (void)makeentry(orphan, lfdir, ".."); 370 dp = ginode(lfdir); 371 dp->di_nlink++; 372 inodirty(); 373 lncntp[lfdir]++; 374 pwarn("DIR I=%lu CONNECTED. ", orphan); 375 if (parentdir != (ino_t)-1) 376 printf("PARENT WAS I=%lu\n", parentdir); 377 if (preen == 0) 378 printf("\n"); 379 } 380 return (1); 381 } 382 383 /* 384 * fix an entry in a directory. 385 */ 386 changeino(dir, name, newnum) 387 ino_t dir; 388 char *name; 389 ino_t newnum; 390 { 391 struct inodesc idesc; 392 393 bzero((char *)&idesc, sizeof(struct inodesc)); 394 idesc.id_type = DATA; 395 idesc.id_func = chgino; 396 idesc.id_number = dir; 397 idesc.id_fix = DONTKNOW; 398 idesc.id_name = name; 399 idesc.id_parent = newnum; /* new value for name */ 400 return (ckinode(ginode(dir), &idesc)); 401 } 402 403 /* 404 * make an entry in a directory 405 */ 406 makeentry(parent, ino, name) 407 ino_t parent, ino; 408 char *name; 409 { 410 struct dinode *dp; 411 struct inodesc idesc; 412 char pathbuf[MAXPATHLEN + 1]; 413 414 if (parent < ROOTINO || parent >= maxino || 415 ino < ROOTINO || ino >= maxino) 416 return (0); 417 bzero((char *)&idesc, sizeof(struct inodesc)); 418 idesc.id_type = DATA; 419 idesc.id_func = mkentry; 420 idesc.id_number = parent; 421 idesc.id_parent = ino; /* this is the inode to enter */ 422 idesc.id_fix = DONTKNOW; 423 idesc.id_name = name; 424 dp = ginode(parent); 425 if (dp->di_size % DIRBLKSIZ) { 426 dp->di_size = roundup(dp->di_size, DIRBLKSIZ); 427 inodirty(); 428 } 429 if ((ckinode(dp, &idesc) & ALTERED) != 0) 430 return (1); 431 getpathname(pathbuf, parent, parent); 432 dp = ginode(parent); 433 if (expanddir(dp, pathbuf) == 0) 434 return (0); 435 return (ckinode(dp, &idesc) & ALTERED); 436 } 437 438 /* 439 * Attempt to expand the size of a directory 440 */ 441 expanddir(dp, name) 442 register struct dinode *dp; 443 char *name; 444 { 445 daddr_t lastbn, newblk; 446 register struct bufarea *bp; 447 char *cp, firstblk[DIRBLKSIZ]; 448 449 lastbn = lblkno(&sblock, dp->di_size); 450 if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0) 451 return (0); 452 if ((newblk = allocblk(sblock.fs_frag)) == 0) 453 return (0); 454 dp->di_db[lastbn + 1] = dp->di_db[lastbn]; 455 dp->di_db[lastbn] = newblk; 456 dp->di_size += sblock.fs_bsize; 457 dp->di_blocks += btodb(sblock.fs_bsize); 458 bp = getdirblk(dp->di_db[lastbn + 1], 459 (long)dblksize(&sblock, dp, lastbn + 1)); 460 if (bp->b_errs) 461 goto bad; 462 bcopy(bp->b_un.b_buf, firstblk, DIRBLKSIZ); 463 bp = getdirblk(newblk, sblock.fs_bsize); 464 if (bp->b_errs) 465 goto bad; 466 bcopy(firstblk, bp->b_un.b_buf, DIRBLKSIZ); 467 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 468 cp < &bp->b_un.b_buf[sblock.fs_bsize]; 469 cp += DIRBLKSIZ) 470 bcopy((char *)&emptydir, cp, sizeof emptydir); 471 dirty(bp); 472 bp = getdirblk(dp->di_db[lastbn + 1], 473 (long)dblksize(&sblock, dp, lastbn + 1)); 474 if (bp->b_errs) 475 goto bad; 476 bcopy((char *)&emptydir, bp->b_un.b_buf, sizeof emptydir); 477 pwarn("NO SPACE LEFT IN %s", name); 478 if (preen) 479 printf(" (EXPANDED)\n"); 480 else if (reply("EXPAND") == 0) 481 goto bad; 482 dirty(bp); 483 inodirty(); 484 return (1); 485 bad: 486 dp->di_db[lastbn] = dp->di_db[lastbn + 1]; 487 dp->di_db[lastbn + 1] = 0; 488 dp->di_size -= sblock.fs_bsize; 489 dp->di_blocks -= btodb(sblock.fs_bsize); 490 freeblk(newblk, sblock.fs_frag); 491 return (0); 492 } 493 494 /* 495 * allocate a new directory 496 */ 497 allocdir(parent, request, mode) 498 ino_t parent, request; 499 int mode; 500 { 501 ino_t ino; 502 char *cp; 503 struct dinode *dp; 504 register struct bufarea *bp; 505 506 ino = allocino(request, IFDIR|mode); 507 dirhead.dot_ino = ino; 508 dirhead.dotdot_ino = parent; 509 dp = ginode(ino); 510 bp = getdirblk(dp->di_db[0], sblock.fs_fsize); 511 if (bp->b_errs) { 512 freeino(ino); 513 return (0); 514 } 515 bcopy((char *)&dirhead, bp->b_un.b_buf, sizeof dirhead); 516 for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; 517 cp < &bp->b_un.b_buf[sblock.fs_fsize]; 518 cp += DIRBLKSIZ) 519 bcopy((char *)&emptydir, cp, sizeof emptydir); 520 dirty(bp); 521 dp->di_nlink = 2; 522 inodirty(); 523 if (ino == ROOTINO) { 524 lncntp[ino] = dp->di_nlink; 525 cacheino(dp, ino); 526 return(ino); 527 } 528 if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { 529 freeino(ino); 530 return (0); 531 } 532 cacheino(dp, ino); 533 statemap[ino] = statemap[parent]; 534 if (statemap[ino] == DSTATE) { 535 lncntp[ino] = dp->di_nlink; 536 lncntp[parent]++; 537 } 538 dp = ginode(parent); 539 dp->di_nlink++; 540 inodirty(); 541 return (ino); 542 } 543 544 /* 545 * free a directory inode 546 */ 547 freedir(ino, parent) 548 ino_t ino, parent; 549 { 550 struct dinode *dp; 551 552 if (ino != parent) { 553 dp = ginode(parent); 554 dp->di_nlink--; 555 inodirty(); 556 } 557 freeino(ino); 558 } 559 560 /* 561 * generate a temporary name for the lost+found directory. 562 */ 563 lftempname(bufp, ino) 564 char *bufp; 565 ino_t ino; 566 { 567 register ino_t in; 568 register char *cp; 569 int namlen; 570 571 cp = bufp + 2; 572 for (in = maxino; in > 0; in /= 10) 573 cp++; 574 *--cp = 0; 575 namlen = cp - bufp; 576 in = ino; 577 while (cp > bufp) { 578 *--cp = (in % 10) + '0'; 579 in /= 10; 580 } 581 *cp = '#'; 582 return (namlen); 583 } 584 585 /* 586 * Get a directory block. 587 * Insure that it is held until another is requested. 588 */ 589 struct bufarea * 590 getdirblk(blkno, size) 591 daddr_t blkno; 592 long size; 593 { 594 595 if (pdirbp != 0) 596 pdirbp->b_flags &= ~B_INUSE; 597 pdirbp = getdatablk(blkno, size); 598 return (pdirbp); 599 } 600