00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include <sys/types.h>
00031 #include <assert.h>
00032 #include <limits.h>
00033 #include <stdint.h>
00034 #include <stdio.h>
00035 #include <stdlib.h>
00036 #include <string.h>
00037 #include <err.h>
00038
00039 #include "support.h"
00040 #include "kern/sfs.h"
00041
00042 #ifdef HOST
00043 #include <netinet/in.h>
00044 #include <arpa/inet.h>
00045 #include "hostcompat.h"
00046 #define SWAPL(x) ntohl(x)
00047 #define SWAPS(x) ntohs(x)
00048
00049 #else
00050
00051 #define SWAPL(x) (x)
00052 #define SWAPS(x) (x)
00053 #define NO_REALLOC
00054 #define NO_QSORT
00055
00056 #endif
00057
00058 #include "disk.h"
00059
00060
00061 #define EXIT_USAGE 4
00062 #define EXIT_FATAL 3
00063 #define EXIT_UNRECOV 2
00064 #define EXIT_RECOV 1
00065 #define EXIT_CLEAN 0
00066
00067 static int badness=0;
00068
00069 static
00070 void
00071 setbadness(int code)
00072 {
00073 if (badness < code) {
00074 badness = code;
00075 }
00076 }
00077
00078
00079
00080 static
00081 void
00082 swapsb(struct sfs_super *sp)
00083 {
00084 sp->sp_magic = SWAPL(sp->sp_magic);
00085 sp->sp_nblocks = SWAPL(sp->sp_nblocks);
00086 }
00087
00088 static
00089 void
00090 swapinode(struct sfs_inode *sfi)
00091 {
00092 int i;
00093
00094 sfi->sfi_size = SWAPL(sfi->sfi_size);
00095 sfi->sfi_type = SWAPS(sfi->sfi_type);
00096 sfi->sfi_linkcount = SWAPS(sfi->sfi_linkcount);
00097
00098 for (i=0; i<SFS_NDIRECT; i++) {
00099 sfi->sfi_direct[i] = SWAPL(sfi->sfi_direct[i]);
00100 }
00101
00102 #ifdef SFS_NIDIRECT
00103 for (i=0; i<SFS_NIDIRECT; i++) {
00104 sfi->sfi_indirect[i] = SWAPL(sfi->sfi_indirect[i]);
00105 }
00106 #else
00107 sfi->sfi_indirect = SWAPL(sfi->sfi_indirect);
00108 #endif
00109
00110 #ifdef SFS_NDIDIRECT
00111 for (i=0; i<SFS_NDIDIRECT; i++) {
00112 sfi->sfi_dindirect[i] = SWAPL(sfi->sfi_dindirect[i]);
00113 }
00114 #else
00115 #ifdef HAS_DIDIRECT
00116 sfi->sfi_dindirect = SWAPL(sfi->sfi_dindirect);
00117 #endif
00118 #endif
00119
00120 #ifdef SFS_NTIDIRECT
00121 for (i=0; i<SFS_NTIDIRECT; i++) {
00122 sfi->sfi_tindirect[i] = SWAPL(sfi->sfi_tindirect[i]);
00123 }
00124 #else
00125 #ifdef HAS_TIDIRECT
00126 sfi->sfi_tindirect = SWAPL(sfi->sfi_tindirect);
00127 #endif
00128 #endif
00129 }
00130
00131 static
00132 void
00133 swapdir(struct sfs_dir *sfd)
00134 {
00135 sfd->sfd_ino = SWAPL(sfd->sfd_ino);
00136 }
00137
00138 static
00139 void
00140 swapindir(uint32_t *entries)
00141 {
00142 int i;
00143 for (i=0; i<SFS_DBPERIDB; i++) {
00144 entries[i] = SWAPL(entries[i]);
00145 }
00146 }
00147
00148 static
00149 void
00150 swapbits(uint8_t *bits)
00151 {
00152
00153 (void)bits;
00154 }
00155
00156
00157
00158 static
00159 void *
00160 domalloc(size_t len)
00161 {
00162 void *x;
00163 x = malloc(len);
00164 if (x==NULL) {
00165 errx(EXIT_FATAL, "Out of memory");
00166 }
00167 return x;
00168 }
00169
00170
00171
00172 typedef enum {
00173 B_SUPERBLOCK,
00174 B_BITBLOCK,
00175 B_INODE,
00176 B_IBLOCK,
00177 B_DIRDATA,
00178 B_DATA,
00179 B_TOFREE,
00180 B_PASTEND,
00181 } blockusage_t;
00182
00183 static uint32_t nblocks, bitblocks;
00184 static uint32_t uniquecounter = 1;
00185
00186 static unsigned long count_blocks=0, count_dirs=0, count_files=0;
00187
00188
00189
00190 static uint8_t *bitmapdata;
00191 static uint8_t *tofreedata;
00192
00193 static
00194 void
00195 bitmap_init(uint32_t bitblocks)
00196 {
00197 size_t i, mapsize = bitblocks * SFS_BLOCKSIZE;
00198 bitmapdata = domalloc(mapsize * sizeof(uint8_t));
00199 tofreedata = domalloc(mapsize * sizeof(uint8_t));
00200 for (i=0; i<mapsize; i++) {
00201 bitmapdata[i] = tofreedata[i] = 0;
00202 }
00203 }
00204
00205 static
00206 const char *
00207 blockusagestr(blockusage_t how, uint32_t howdesc)
00208 {
00209 static char rv[256];
00210 switch (how) {
00211 case B_SUPERBLOCK: return "superblock";
00212 case B_BITBLOCK: return "bitmap block";
00213 case B_INODE: return "inode";
00214 case B_IBLOCK:
00215 snprintf(rv, sizeof(rv), "indirect block of inode %lu",
00216 (unsigned long) howdesc);
00217 break;
00218 case B_DIRDATA:
00219 snprintf(rv, sizeof(rv), "directory data from inode %lu",
00220 (unsigned long) howdesc);
00221 break;
00222 case B_DATA:
00223 snprintf(rv, sizeof(rv), "file data from inode %lu",
00224 (unsigned long) howdesc);
00225 break;
00226 case B_TOFREE:
00227 assert(0);
00228 break;
00229 case B_PASTEND:
00230 return "past the end of the fs";
00231 }
00232 return rv;
00233 }
00234
00235 static
00236 void
00237 bitmap_mark(uint32_t block, blockusage_t how, uint32_t howdesc)
00238 {
00239 unsigned index = block/8;
00240 uint8_t mask = ((uint8_t)1)<<(block%8);
00241
00242 if (how == B_TOFREE) {
00243 if (tofreedata[index] & mask) {
00244
00245 return;
00246 }
00247 if (bitmapdata[index] & mask) {
00248
00249 return;
00250 }
00251 tofreedata[index] |= mask;
00252 return;
00253 }
00254
00255 if (tofreedata[index] & mask) {
00256
00257 tofreedata[index] &= ~mask;
00258 }
00259
00260 if (bitmapdata[index] & mask) {
00261 warnx("Block %lu (used as %s) already in use! (NOT FIXED)",
00262 (unsigned long) block, blockusagestr(how, howdesc));
00263 setbadness(EXIT_UNRECOV);
00264 }
00265
00266 bitmapdata[index] |= mask;
00267
00268 if (how != B_PASTEND) {
00269 count_blocks++;
00270 }
00271 }
00272
00273 static
00274 int
00275 countbits(uint8_t val)
00276 {
00277 uint8_t x;
00278 int ct=0;
00279
00280 for (x=1; x; x<<=1) {
00281 if (val & x) ct++;
00282 }
00283 return ct;
00284 }
00285
00286 static
00287 void
00288 reportbits(uint32_t bitblock, uint32_t byte, uint8_t val, const char *what)
00289 {
00290 uint8_t x, y;
00291 uint32_t blocknum;
00292
00293 for (x=1, y=0; x; x<<=1, y++) {
00294 if (val & x) {
00295 blocknum = bitblock*SFS_BLOCKBITS + byte*CHAR_BIT + y;
00296 warnx("Block %lu erroneously shown %s in bitmap",
00297 (unsigned long) blocknum, what);
00298 }
00299 }
00300 }
00301
00302 static
00303 void
00304 check_bitmap(void)
00305 {
00306 uint8_t bits[SFS_BLOCKSIZE], *found, *tofree, tmp;
00307 uint32_t alloccount=0, freecount=0, i, j;
00308 int bchanged;
00309
00310 for (i=0; i<bitblocks; i++) {
00311 diskread(bits, SFS_MAP_LOCATION+i);
00312 swapbits(bits);
00313 found = bitmapdata + i*SFS_BLOCKSIZE;
00314 tofree = tofreedata + i*SFS_BLOCKSIZE;
00315 bchanged = 0;
00316
00317 for (j=0; j<SFS_BLOCKSIZE; j++) {
00318
00319 assert((found[j] & tofree[j])==0);
00320
00321 if (bits[j]==found[j]) {
00322 continue;
00323 }
00324
00325 if (bits[j]==(found[j] | tofree[j])) {
00326 bits[j] = found[j];
00327 bchanged = 1;
00328 continue;
00329 }
00330
00331
00332 bits[j] &= ~tofree[j];
00333
00334
00335 if ((bits[j] & found[j]) != found[j]) {
00336 tmp = found[j] & ~bits[j];
00337 alloccount += countbits(tmp);
00338 if (tmp != 0) {
00339 reportbits(i, j, tmp, "free");
00340 }
00341 }
00342
00343
00344 if ((bits[j] & found[j]) != bits[j]) {
00345 tmp = bits[j] & ~found[j];
00346 freecount += countbits(tmp);
00347 if (tmp != 0) {
00348 reportbits(i, j, tmp, "allocated");
00349 }
00350 }
00351
00352 bits[j] = found[j];
00353 bchanged = 1;
00354 }
00355
00356 if (bchanged) {
00357 swapbits(bits);
00358 diskwrite(bits, SFS_MAP_LOCATION+i);
00359 }
00360 }
00361
00362 if (alloccount > 0) {
00363 warnx("%lu blocks erroneously shown free in bitmap (fixed)",
00364 (unsigned long) alloccount);
00365 setbadness(EXIT_RECOV);
00366 }
00367 if (freecount > 0) {
00368 warnx("%lu blocks erroneously shown used in bitmap (fixed)",
00369 (unsigned long) freecount);
00370 setbadness(EXIT_RECOV);
00371 }
00372 }
00373
00374
00375
00376 struct inodememory {
00377 uint32_t ino;
00378 uint32_t linkcount;
00379 };
00380
00381 static struct inodememory *inodes = NULL;
00382 static int ninodes=0, maxinodes=0;
00383
00384 static
00385 void
00386 addmemory(uint32_t ino, uint32_t linkcount)
00387 {
00388 assert(ninodes <= maxinodes);
00389 if (ninodes == maxinodes) {
00390 #ifdef NO_REALLOC
00391 int newmax = (maxinodes+1)*2;
00392 void *p = domalloc(newmax * sizeof(struct inodememory));
00393 if (inodes) {
00394 memcpy(p, inodes, ninodes);
00395 free(inodes);
00396 }
00397 inodes = p;
00398 #else
00399 maxinodes = (maxinodes+1)*2;
00400 inodes = realloc(inodes, maxinodes * sizeof(uint32_t));
00401 if (inodes==NULL) {
00402 errx(EXIT_FATAL, "Out of memory");
00403 }
00404 #endif
00405 }
00406 inodes[ninodes].ino = ino;
00407 inodes[ninodes].linkcount = linkcount;
00408 }
00409
00410
00411 static
00412 int
00413 remember_dir(uint32_t ino, const char *pathsofar)
00414 {
00415 int i;
00416
00417
00418 (void)pathsofar;
00419
00420 for (i=0; i<ninodes; i++) {
00421 if (inodes[i].ino==ino) {
00422 assert(inodes[i].linkcount==0);
00423 return 1;
00424 }
00425 }
00426
00427 addmemory(ino, 0);
00428
00429 return 0;
00430 }
00431
00432 static
00433 void
00434 observe_filelink(uint32_t ino)
00435 {
00436 int i;
00437 for (i=0; i<ninodes; i++) {
00438 if (inodes[i].ino==ino) {
00439 assert(inodes[i].linkcount>0);
00440 inodes[i].linkcount++;
00441 return;
00442 }
00443 }
00444 bitmap_mark(ino, B_INODE, ino);
00445 addmemory(ino, 1);
00446 }
00447
00448 static
00449 void
00450 adjust_filelinks(void)
00451 {
00452 struct sfs_inode sfi;
00453 int i;
00454
00455 for (i=0; i<ninodes; i++) {
00456 if (inodes[i].linkcount==0) {
00457
00458 continue;
00459 }
00460 diskread(&sfi, inodes[i].ino);
00461 swapinode(&sfi);
00462 assert(sfi.sfi_type == SFS_TYPE_FILE);
00463 if (sfi.sfi_linkcount != inodes[i].linkcount) {
00464 warnx("File %lu link count %lu should be %lu (fixed)",
00465 (unsigned long) inodes[i].ino,
00466 (unsigned long) sfi.sfi_linkcount,
00467 (unsigned long) inodes[i].linkcount);
00468 sfi.sfi_linkcount = inodes[i].linkcount;
00469 setbadness(EXIT_RECOV);
00470 swapinode(&sfi);
00471 diskwrite(&sfi, inodes[i].ino);
00472 }
00473 count_files++;
00474 }
00475 }
00476
00477
00478
00479 static
00480 int
00481 checknullstring(char *buf, size_t maxlen)
00482 {
00483 size_t i;
00484 for (i=0; i<maxlen; i++) {
00485 if (buf[i]==0) {
00486 return 0;
00487 }
00488 }
00489 buf[maxlen-1] = 0;
00490 return 1;
00491 }
00492
00493 static
00494 int
00495 checkbadstring(char *buf)
00496 {
00497 size_t i;
00498 int rv=0;
00499
00500 for (i=0; buf[i]; i++) {
00501 if (buf[i]==':' || buf[i]=='/') {
00502 buf[i] = '_';
00503 rv = 1;
00504 }
00505 }
00506 return rv;
00507 }
00508
00509
00510
00511 static
00512 void
00513 check_sb(void)
00514 {
00515 struct sfs_super sp;
00516 uint32_t i;
00517 int schanged=0;
00518
00519 diskread(&sp, SFS_SB_LOCATION);
00520 swapsb(&sp);
00521 if (sp.sp_magic != SFS_MAGIC) {
00522 errx(EXIT_UNRECOV, "Not an sfs filesystem");
00523 }
00524
00525 assert(nblocks==0);
00526 assert(bitblocks==0);
00527 nblocks = sp.sp_nblocks;
00528 bitblocks = SFS_BITBLOCKS(nblocks);
00529 assert(nblocks>0);
00530 assert(bitblocks>0);
00531
00532 bitmap_init(bitblocks);
00533 for (i=nblocks; i<bitblocks*SFS_BLOCKBITS; i++) {
00534 bitmap_mark(i, B_PASTEND, 0);
00535 }
00536
00537 if (checknullstring(sp.sp_volname, sizeof(sp.sp_volname))) {
00538 warnx("Volume name not null-terminated (fixed)");
00539 setbadness(EXIT_RECOV);
00540 schanged = 1;
00541 }
00542 if (checkbadstring(sp.sp_volname)) {
00543 warnx("Volume name contains illegal characters (fixed)");
00544 setbadness(EXIT_RECOV);
00545 schanged = 1;
00546 }
00547
00548 if (schanged) {
00549 swapsb(&sp);
00550 diskwrite(&sp, SFS_SB_LOCATION);
00551 }
00552
00553 bitmap_mark(SFS_SB_LOCATION, B_SUPERBLOCK, 0);
00554 for (i=0; i<bitblocks; i++) {
00555 bitmap_mark(SFS_MAP_LOCATION+i, B_BITBLOCK, i);
00556 }
00557 }
00558
00559
00560
00561 static
00562 void
00563 check_indirect_block(uint32_t ino, uint32_t *ientry, uint32_t *blockp,
00564 uint32_t nblocks, uint32_t *badcountp,
00565 int isdir, int indirection)
00566 {
00567 uint32_t entries[SFS_DBPERIDB];
00568 uint32_t i, ct;
00569
00570 if (*ientry !=0) {
00571 diskread(entries, *ientry);
00572 swapindir(entries);
00573 bitmap_mark(*ientry, B_IBLOCK, ino);
00574 }
00575 else {
00576 for (i=0; i<SFS_DBPERIDB; i++) {
00577 entries[i] = 0;
00578 }
00579 }
00580
00581 if (indirection > 1) {
00582 for (i=0; i<SFS_DBPERIDB; i++) {
00583 check_indirect_block(ino, &entries[i],
00584 blockp, nblocks,
00585 badcountp,
00586 isdir,
00587 indirection-1);
00588 }
00589 }
00590 else {
00591 assert(indirection==1);
00592
00593 for (i=0; i<SFS_DBPERIDB; i++) {
00594 if (*blockp < nblocks) {
00595 if (entries[i] != 0) {
00596 bitmap_mark(entries[i],
00597 isdir ? B_DIRDATA : B_DATA,
00598 ino);
00599 }
00600 }
00601 else {
00602 if (entries[i] != 0) {
00603 (*badcountp)++;
00604 bitmap_mark(entries[i],
00605 isdir ? B_DIRDATA : B_DATA,
00606 ino);
00607 entries[i] = 0;
00608 }
00609 }
00610 (*blockp)++;
00611 }
00612 }
00613
00614 ct=0;
00615 for (i=ct=0; i<SFS_DBPERIDB; i++) {
00616 if (entries[i]!=0) ct++;
00617 }
00618 if (ct==0) {
00619 if (*ientry != 0) {
00620 (*badcountp)++;
00621 bitmap_mark(*ientry, B_TOFREE, 0);
00622 *ientry = 0;
00623 }
00624 }
00625 else {
00626 assert(*ientry != 0);
00627 if (*badcountp > 0) {
00628 swapindir(entries);
00629 diskwrite(entries, *ientry);
00630 }
00631 }
00632 }
00633
00634
00635 static
00636 int
00637 check_inode_blocks(uint32_t ino, struct sfs_inode *sfi, int isdir)
00638 {
00639 uint32_t size, block, nblocks, badcount;
00640
00641 badcount = 0;
00642
00643 size = SFS_ROUNDUP(sfi->sfi_size, SFS_BLOCKSIZE);
00644 nblocks = size/SFS_BLOCKSIZE;
00645
00646 for (block=0; block<SFS_NDIRECT; block++) {
00647 if (block < nblocks) {
00648 if (sfi->sfi_direct[block] != 0) {
00649 bitmap_mark(sfi->sfi_direct[block],
00650 isdir ? B_DIRDATA : B_DATA, ino);
00651 }
00652 }
00653 else {
00654 if (sfi->sfi_direct[block] != 0) {
00655 badcount++;
00656 bitmap_mark(sfi->sfi_direct[block],
00657 B_TOFREE, 0);
00658 }
00659 }
00660 }
00661
00662 #ifdef SFS_NIDIRECT
00663 for (i=0; i<SFS_NIDIRECT; i++) {
00664 check_indirect_block(ino, &sfi->sfi_indirect[i],
00665 &block, nblocks, &badcount, isdir, 1);
00666 }
00667 #else
00668 check_indirect_block(ino, &sfi->sfi_indirect,
00669 &block, nblocks, &badcount, isdir, 1);
00670 #endif
00671
00672 #ifdef SFS_NDIDIRECT
00673 for (i=0; i<SFS_NDIDIRECT; i++) {
00674 check_indirect_block(ino, &sfi->sfi_dindirect[i],
00675 &block, nblocks, &badcount, isdir, 2);
00676 }
00677 #else
00678 #ifdef HAS_DIDIRECT
00679 check_indirect_block(ino, &sfi->sfi_dindirect,
00680 &block, nblocks, &badcount, isdir, 2);
00681 #endif
00682 #endif
00683
00684 #ifdef SFS_NTIDIRECT
00685 for (i=0; i<SFS_NTIDIRECT; i++) {
00686 check_indirect_block(ino, &sfi->sfi_tindirect[i],
00687 &block, nblocks, &badcount, isdir, 3);
00688 }
00689 #else
00690 #ifdef HAS_TIDIRECT
00691 check_indirect_block(ino, &sfi->sfi_tindirect,
00692 &block, nblocks, &badcount, isdir, 3);
00693 #endif
00694 #endif
00695
00696 if (badcount > 0) {
00697 warnx("Inode %lu: %lu blocks after EOF (freed)",
00698 (unsigned long) ino, (unsigned long) badcount);
00699 setbadness(EXIT_RECOV);
00700 return 1;
00701 }
00702
00703 return 0;
00704 }
00705
00706
00707
00708 static
00709 uint32_t
00710 ibmap(uint32_t iblock, uint32_t offset, uint32_t entrysize)
00711 {
00712 uint32_t entries[SFS_DBPERIDB];
00713
00714 if (iblock == 0) {
00715 return 0;
00716 }
00717
00718 diskread(entries, iblock);
00719 swapindir(entries);
00720
00721 if (entrysize > 1) {
00722 uint32_t index = offset / entrysize;
00723 offset %= entrysize;
00724 return ibmap(entries[index], offset, entrysize/SFS_DBPERIDB);
00725 }
00726 else {
00727 assert(offset < SFS_DBPERIDB);
00728 return entries[offset];
00729 }
00730 }
00731
00732 #define BMAP_ND SFS_NDIRECT
00733 #define BMAP_D(sfi, x) ((sfi)->sfi_direct[(x)])
00734
00735 #ifdef SFS_NIDIRECT
00736 #define BMAP_NI SFS_NIDIRECT
00737 #define BMAP_I(sfi, x) ((sfi)->sfi_indirect[(x)])
00738 #else
00739 #define BMAP_NI 1
00740 #define BMAP_I(sfi, x) ((void)(x), (sfi)->sfi_indirect)
00741 #endif
00742
00743 #ifdef SFS_NDIDIRECT
00744 #define BMAP_NII SFS_NDIDIRECT
00745 #define BMAP_II(sfi, x) ((sfi)->sfi_dindirect[(x)])
00746 #else
00747 #ifdef HAS_DIDIRECT
00748 #define BMAP_NII 1
00749 #define BMAP_II(sfi, x) ((void)(x), (sfi)->sfi_dindirect)
00750 #else
00751 #define BMAP_NII 0
00752 #define BMAP_II(sfi, x) ((void)(x), (void)(sfi), 0)
00753 #endif
00754 #endif
00755
00756 #ifdef SFS_NTIDIRECT
00757 #define BMAP_NIII SFS_NTIDIRECT
00758 #define BMAP_III(sfi, x) ((sfi)->sfi_tindirect[(x)])
00759 #else
00760 #ifdef HAS_TIDIRECT
00761 #define BMAP_NIII 1
00762 #define BMAP_III(sfi, x) ((void)(x), (sfi)->sfi_tindirect)
00763 #else
00764 #define BMAP_NIII 0
00765 #define BMAP_III(sfi, x) ((void)(x), (void)(sfi), 0)
00766 #endif
00767 #endif
00768
00769 #define BMAP_DMAX BMAP_ND
00770 #define BMAP_IMAX (BMAP_DMAX+SFS_DBPERIDB*BMAP_NI)
00771 #define BMAP_IIMAX (BMAP_IMAX+SFS_DBPERIDB*BMAP_NII)
00772 #define BMAP_IIIMAX (BMAP_IIMAX+SFS_DBPERIDB*BMAP_NIII)
00773
00774 #define BMAP_DSIZE 1
00775 #define BMAP_ISIZE (BMAP_DSIZE*SFS_DBPERIDB)
00776 #define BMAP_IISIZE (BMAP_ISIZE*SFS_DBPERIDB)
00777 #define BMAP_IIISIZE (BMAP_IISIZE*SFS_DBPERIDB)
00778
00779 static
00780 uint32_t
00781 dobmap(const struct sfs_inode *sfi, uint32_t fileblock)
00782 {
00783 uint32_t iblock, offset;
00784
00785 if (fileblock < BMAP_DMAX) {
00786 return BMAP_D(sfi, fileblock);
00787 }
00788 else if (fileblock < BMAP_IMAX) {
00789 iblock = (fileblock - BMAP_DMAX)/BMAP_ISIZE;
00790 offset = (fileblock - BMAP_DMAX)%BMAP_ISIZE;
00791 return ibmap(BMAP_I(sfi, iblock), offset, BMAP_DSIZE);
00792 }
00793 else if (fileblock < BMAP_IIMAX) {
00794 iblock = (fileblock - BMAP_IMAX)/BMAP_IISIZE;
00795 offset = (fileblock - BMAP_IMAX)%BMAP_IISIZE;
00796 return ibmap(BMAP_II(sfi, iblock), offset, BMAP_ISIZE);
00797 }
00798 else if (fileblock < BMAP_IIIMAX) {
00799 iblock = (fileblock - BMAP_IIMAX)/BMAP_IIISIZE;
00800 offset = (fileblock - BMAP_IIMAX)%BMAP_IIISIZE;
00801 return ibmap(BMAP_III(sfi, iblock), offset, BMAP_IISIZE);
00802 }
00803 return 0;
00804 }
00805
00806 static
00807 void
00808 dirread(struct sfs_inode *sfi, struct sfs_dir *d, unsigned nd)
00809 {
00810 const unsigned atonce = SFS_BLOCKSIZE/sizeof(struct sfs_dir);
00811 unsigned nblocks = SFS_ROUNDUP(nd, atonce) / atonce;
00812 unsigned i, j;
00813
00814 for (i=0; i<nblocks; i++) {
00815 uint32_t block = dobmap(sfi, i);
00816 if (block!=0) {
00817 diskread(d + i*atonce, block);
00818 for (j=0; j<atonce; j++) {
00819 swapdir(&d[i*atonce+j]);
00820 }
00821 }
00822 else {
00823 warnx("Warning: sparse directory found");
00824 bzero(d + i*atonce, SFS_BLOCKSIZE);
00825 }
00826 }
00827 }
00828
00829 static
00830 void
00831 dirwrite(const struct sfs_inode *sfi, struct sfs_dir *d, int nd)
00832 {
00833 const unsigned atonce = SFS_BLOCKSIZE/sizeof(struct sfs_dir);
00834 unsigned nblocks = SFS_ROUNDUP(nd, atonce) / atonce;
00835 unsigned i, j, bad;
00836
00837 for (i=0; i<nblocks; i++) {
00838 uint32_t block = dobmap(sfi, i);
00839 if (block!=0) {
00840 for (j=0; j<atonce; j++) {
00841 swapdir(&d[i*atonce+j]);
00842 }
00843 diskwrite(d + i*atonce, block);
00844 }
00845 else {
00846 for (j=bad=0; j<atonce; j++) {
00847 if (d[i*atonce+j].sfd_ino != SFS_NOINO ||
00848 d[i*atonce+j].sfd_name[0] != 0) {
00849 bad = 1;
00850 }
00851 }
00852 if (bad) {
00853 warnx("Cannot write to missing block in "
00854 "sparse directory (ERROR)");
00855 setbadness(EXIT_UNRECOV);
00856 }
00857 }
00858 }
00859 }
00860
00861
00862
00863 static struct sfs_dir *global_sortdirs;
00864 static
00865 int
00866 dirsortfunc(const void *aa, const void *bb)
00867 {
00868 const int *a = (const int *)aa;
00869 const int *b = (const int *)bb;
00870 const struct sfs_dir *ad = &global_sortdirs[*a];
00871 const struct sfs_dir *bd = &global_sortdirs[*b];
00872 return strcmp(ad->sfd_name, bd->sfd_name);
00873 }
00874
00875 #ifdef NO_QSORT
00876 static
00877 void
00878 qsort(int *data, int num, size_t size, int (*f)(const void *, const void *))
00879 {
00880 int i, j;
00881 (void)size;
00882
00883
00884 for (i=0; i<num-1; i++) {
00885 for (j=i+1; j<num; j++) {
00886 if (f(&data[i], &data[j]) < 0) {
00887 int tmp = data[i];
00888 data[i] = data[j];
00889 data[j] = tmp;
00890 }
00891 }
00892 }
00893 }
00894 #endif
00895
00896 static
00897 void
00898 sortdir(int *vector, struct sfs_dir *d, int nd)
00899 {
00900 global_sortdirs = d;
00901 qsort(vector, nd, sizeof(int), dirsortfunc);
00902 }
00903
00904
00905 static
00906 int
00907 dir_tryadd(struct sfs_dir *d, int nd, const char *name, uint32_t ino)
00908 {
00909 int i;
00910 for (i=0; i<nd; i++) {
00911 if (d[i].sfd_ino==SFS_NOINO) {
00912 d[i].sfd_ino = ino;
00913 assert(strlen(name) < sizeof(d[i].sfd_name));
00914 strcpy(d[i].sfd_name, name);
00915 return 0;
00916 }
00917 }
00918 return -1;
00919 }
00920
00921 static
00922 int
00923 check_dir_entry(const char *pathsofar, uint32_t index, struct sfs_dir *sfd)
00924 {
00925 int dchanged = 0;
00926
00927 if (sfd->sfd_ino == SFS_NOINO) {
00928 if (sfd->sfd_name[0] != 0) {
00929 setbadness(EXIT_RECOV);
00930 warnx("Directory /%s entry %lu has name but no file",
00931 pathsofar, (unsigned long) index);
00932 sfd->sfd_name[0] = 0;
00933 dchanged = 1;
00934 }
00935 }
00936 else {
00937 if (sfd->sfd_name[0] == 0) {
00938 snprintf(sfd->sfd_name, sizeof(sfd->sfd_name),
00939 "FSCK.%lu.%lu",
00940 (unsigned long) sfd->sfd_ino,
00941 (unsigned long) uniquecounter++);
00942 setbadness(EXIT_RECOV);
00943 warnx("Directory /%s entry %lu has file but "
00944 "no name (fixed: %s)",
00945 pathsofar, (unsigned long) index,
00946 sfd->sfd_name);
00947 dchanged = 1;
00948 }
00949 if (checknullstring(sfd->sfd_name, sizeof(sfd->sfd_name))) {
00950 setbadness(EXIT_RECOV);
00951 warnx("Directory /%s entry %lu not "
00952 "null-terminated (fixed)",
00953 pathsofar, (unsigned long) index);
00954 dchanged = 1;
00955 }
00956 if (checkbadstring(sfd->sfd_name)) {
00957 setbadness(EXIT_RECOV);
00958 warnx("Directory /%s entry %lu contains invalid "
00959 "characters (fixed)",
00960 pathsofar, (unsigned long) index);
00961 dchanged = 1;
00962 }
00963 }
00964 return dchanged;
00965 }
00966
00967
00968
00969 static
00970 int
00971 check_dir(uint32_t ino, uint32_t parentino, const char *pathsofar)
00972 {
00973 struct sfs_inode sfi;
00974 struct sfs_dir *direntries;
00975 int *sortvector;
00976 uint32_t dirsize, ndirentries, maxdirentries, subdircount, i;
00977 int ichanged=0, dchanged=0, dotseen=0, dotdotseen=0;
00978
00979 diskread(&sfi, ino);
00980 swapinode(&sfi);
00981
00982 if (remember_dir(ino, pathsofar)) {
00983
00984 return 1;
00985 }
00986
00987 bitmap_mark(ino, B_INODE, ino);
00988 count_dirs++;
00989
00990 if (sfi.sfi_size % sizeof(struct sfs_dir) != 0) {
00991 setbadness(EXIT_RECOV);
00992 warnx("Directory /%s has illegal size %lu (fixed)",
00993 pathsofar, (unsigned long) sfi.sfi_size);
00994 sfi.sfi_size = SFS_ROUNDUP(sfi.sfi_size,
00995 sizeof(struct sfs_dir));
00996 ichanged = 1;
00997 }
00998
00999 if (check_inode_blocks(ino, &sfi, 1)) {
01000 ichanged = 1;
01001 }
01002
01003 ndirentries = sfi.sfi_size/sizeof(struct sfs_dir);
01004 maxdirentries = SFS_ROUNDUP(ndirentries,
01005 SFS_BLOCKSIZE/sizeof(struct sfs_dir));
01006 dirsize = maxdirentries * sizeof(struct sfs_dir);
01007 direntries = domalloc(dirsize);
01008 sortvector = domalloc(ndirentries * sizeof(int));
01009
01010 dirread(&sfi, direntries, ndirentries);
01011 for (i=ndirentries; i<maxdirentries; i++) {
01012 direntries[i].sfd_ino = SFS_NOINO;
01013 bzero(direntries[i].sfd_name, sizeof(direntries[i].sfd_name));
01014 }
01015
01016 for (i=0; i<ndirentries; i++) {
01017 if (check_dir_entry(pathsofar, i, &direntries[i])) {
01018 dchanged = 1;
01019 }
01020 sortvector[i] = i;
01021 }
01022
01023 sortdir(sortvector, direntries, ndirentries);
01024
01025
01026 for (i=0; i+1<ndirentries; i++) {
01027 struct sfs_dir *d1 = &direntries[sortvector[i]];
01028 struct sfs_dir *d2 = &direntries[sortvector[i+1]];
01029 assert(d1 != d2);
01030
01031 if (d1->sfd_ino == SFS_NOINO) {
01032 continue;
01033 }
01034
01035 if (!strcmp(d1->sfd_name, d2->sfd_name)) {
01036 if (d1->sfd_ino == d2->sfd_ino) {
01037 setbadness(EXIT_RECOV);
01038 warnx("Directory /%s: Duplicate entries for "
01039 "%s (merged)",
01040 pathsofar, d1->sfd_name);
01041 d1->sfd_ino = SFS_NOINO;
01042 d1->sfd_name[0] = 0;
01043 }
01044 else {
01045 snprintf(d1->sfd_name, sizeof(d1->sfd_name),
01046 "FSCK.%lu.%lu",
01047 (unsigned long) d1->sfd_ino,
01048 (unsigned long) uniquecounter++);
01049 setbadness(EXIT_RECOV);
01050 warnx("Directory /%s: Duplicate names %s "
01051 "(one renamed: %s)",
01052 pathsofar, d2->sfd_name, d1->sfd_name);
01053 }
01054 dchanged = 1;
01055 }
01056 }
01057
01058 for (i=0; i<ndirentries; i++) {
01059 if (!strcmp(direntries[i].sfd_name, ".")) {
01060 if (direntries[i].sfd_ino != ino) {
01061 setbadness(EXIT_RECOV);
01062 warnx("Directory /%s: Incorrect `.' entry "
01063 "(fixed)", pathsofar);
01064 direntries[i].sfd_ino = ino;
01065 dchanged = 1;
01066 }
01067 assert(dotseen==0);
01068 dotseen = 1;
01069 }
01070 else if (!strcmp(direntries[i].sfd_name, "..")) {
01071 if (direntries[i].sfd_ino != parentino) {
01072 setbadness(EXIT_RECOV);
01073 warnx("Directory /%s: Incorrect `..' entry "
01074 "(fixed)", pathsofar);
01075 direntries[i].sfd_ino = parentino;
01076 dchanged = 1;
01077 }
01078 assert(dotdotseen==0);
01079 dotdotseen = 1;
01080 }
01081 }
01082
01083 if (!dotseen) {
01084 if (dir_tryadd(direntries, ndirentries, ".", ino)==0) {
01085 setbadness(EXIT_RECOV);
01086 warnx("Directory /%s: No `.' entry (added)",
01087 pathsofar);
01088 dchanged = 1;
01089 }
01090 else if (dir_tryadd(direntries, maxdirentries, ".", ino)==0) {
01091 setbadness(EXIT_RECOV);
01092 warnx("Directory /%s: No `.' entry (added)",
01093 pathsofar);
01094 ndirentries++;
01095 dchanged = 1;
01096 sfi.sfi_size += sizeof(struct sfs_dir);
01097 ichanged = 1;
01098 }
01099 else {
01100 setbadness(EXIT_UNRECOV);
01101 warnx("Directory /%s: No `.' entry (NOT FIXED)",
01102 pathsofar);
01103 }
01104 }
01105
01106 if (!dotdotseen) {
01107 if (dir_tryadd(direntries, ndirentries, "..", parentino)==0) {
01108 setbadness(EXIT_RECOV);
01109 warnx("Directory /%s: No `..' entry (added)",
01110 pathsofar);
01111 dchanged = 1;
01112 }
01113 else if (dir_tryadd(direntries, maxdirentries, "..",
01114 parentino)==0) {
01115 setbadness(EXIT_RECOV);
01116 warnx("Directory /%s: No `..' entry (added)",
01117 pathsofar);
01118 ndirentries++;
01119 dchanged = 1;
01120 sfi.sfi_size += sizeof(struct sfs_dir);
01121 ichanged = 1;
01122 }
01123 else {
01124 setbadness(EXIT_UNRECOV);
01125 warnx("Directory /%s: No `..' entry (NOT FIXED)",
01126 pathsofar);
01127 }
01128 }
01129
01130 subdircount=0;
01131 for (i=0; i<ndirentries; i++) {
01132 if (!strcmp(direntries[i].sfd_name, ".")) {
01133
01134 }
01135 else if (!strcmp(direntries[i].sfd_name, "..")) {
01136
01137 }
01138 else if (direntries[i].sfd_ino == SFS_NOINO) {
01139
01140 }
01141 else {
01142 char path[strlen(pathsofar)+SFS_NAMELEN+1];
01143 struct sfs_inode subsfi;
01144
01145 diskread(&subsfi, direntries[i].sfd_ino);
01146 swapinode(&subsfi);
01147 snprintf(path, sizeof(path), "%s/%s",
01148 pathsofar, direntries[i].sfd_name);
01149
01150 switch (subsfi.sfi_type) {
01151 case SFS_TYPE_FILE:
01152 if (check_inode_blocks(direntries[i].sfd_ino,
01153 &subsfi, 0)) {
01154 swapinode(&subsfi);
01155 diskwrite(&subsfi,
01156 direntries[i].sfd_ino);
01157 }
01158 observe_filelink(direntries[i].sfd_ino);
01159 break;
01160 case SFS_TYPE_DIR:
01161 if (check_dir(direntries[i].sfd_ino,
01162 ino,
01163 path)) {
01164 setbadness(EXIT_RECOV);
01165 warnx("Directory /%s: Crosslink to "
01166 "other directory (removed)",
01167 path);
01168 direntries[i].sfd_ino = SFS_NOINO;
01169 direntries[i].sfd_name[0] = 0;
01170 dchanged = 1;
01171 }
01172 else {
01173 subdircount++;
01174 }
01175 break;
01176 default:
01177 setbadness(EXIT_RECOV);
01178 warnx("Object /%s: Invalid inode type "
01179 "(removed)", path);
01180 direntries[i].sfd_ino = SFS_NOINO;
01181 direntries[i].sfd_name[0] = 0;
01182 dchanged = 1;
01183 break;
01184 }
01185 }
01186 }
01187
01188 if (sfi.sfi_linkcount != subdircount+2) {
01189 setbadness(EXIT_RECOV);
01190 warnx("Directory /%s: Link count %lu should be %lu (fixed)",
01191 pathsofar, (unsigned long) sfi.sfi_linkcount,
01192 (unsigned long) subdircount+2);
01193 sfi.sfi_linkcount = subdircount+2;
01194 ichanged = 1;
01195 }
01196
01197 if (dchanged) {
01198 dirwrite(&sfi, direntries, ndirentries);
01199 }
01200
01201 if (ichanged) {
01202 swapinode(&sfi);
01203 diskwrite(&sfi, ino);
01204 }
01205
01206 free(direntries);
01207 free(sortvector);
01208
01209 return 0;
01210 }
01211
01212
01213 static
01214 void
01215 check_root_dir(void)
01216 {
01217 struct sfs_inode sfi;
01218 diskread(&sfi, SFS_ROOT_LOCATION);
01219 swapinode(&sfi);
01220
01221 switch (sfi.sfi_type) {
01222 case SFS_TYPE_DIR:
01223 break;
01224 case SFS_TYPE_FILE:
01225 warnx("Root directory inode is a regular file (fixed)");
01226 goto fix;
01227 default:
01228 warnx("Root directory inode has invalid type %lu (fixed)",
01229 (unsigned long) sfi.sfi_type);
01230 fix:
01231 setbadness(EXIT_RECOV);
01232 sfi.sfi_type = SFS_TYPE_DIR;
01233 swapinode(&sfi);
01234 diskwrite(&sfi, SFS_ROOT_LOCATION);
01235 break;
01236 }
01237
01238 check_dir(SFS_ROOT_LOCATION, SFS_ROOT_LOCATION, "");
01239 }
01240
01241
01242
01243 int
01244 main(int argc, char **argv)
01245 {
01246 #ifdef HOST
01247 hostcompat_init(argc, argv);
01248 #endif
01249
01250 if (argc!=2) {
01251 errx(EXIT_USAGE, "Usage: sfsck device/diskfile");
01252 }
01253
01254 assert(sizeof(struct sfs_super)==SFS_BLOCKSIZE);
01255 assert(sizeof(struct sfs_inode)==SFS_BLOCKSIZE);
01256 assert(SFS_BLOCKSIZE % sizeof(struct sfs_dir) == 0);
01257
01258 opendisk(argv[1]);
01259
01260 check_sb();
01261 check_root_dir();
01262 check_bitmap();
01263 adjust_filelinks();
01264
01265 closedisk();
01266
01267 warnx("%lu blocks used (of %lu); %lu directories; %lu files",
01268 count_blocks, (unsigned long) nblocks, count_dirs, count_files);
01269
01270 switch (badness) {
01271 case EXIT_USAGE:
01272 case EXIT_FATAL:
01273 default:
01274
01275 assert(0);
01276 break;
01277 case EXIT_UNRECOV:
01278 warnx("WARNING - unrecoverable errors. Maybe try again?");
01279 break;
01280 case EXIT_RECOV:
01281 warnx("Caution - filesystem modified. Run again for luck.");
01282 break;
01283 case EXIT_CLEAN:
01284 break;
01285 }
01286
01287 return badness;
01288 }