URI:
       tcheck.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tcheck.c (17620B)
       ---
            1 #include "stdinc.h"
            2 #include "dat.h"
            3 #include "fns.h"
            4 
            5 static void        checkDirs(Fsck*);
            6 static void        checkEpochs(Fsck*);
            7 static void        checkLeak(Fsck*);
            8 static void        closenop(Fsck*, Block*, u32int);
            9 static void        clrenop(Fsck*, Block*, int);
           10 static void        clrinop(Fsck*, char*, MetaBlock*, int, Block*);
           11 static void        error(Fsck*, char*, ...);
           12 static int        getBit(uchar*, u32int);
           13 static int        printnop(char*, ...);
           14 static void        setBit(uchar*, u32int);
           15 static int        walkEpoch(Fsck *chk, Block *b, uchar score[VtScoreSize],
           16                         int type, u32int tag, u32int epoch);
           17 static void        warn(Fsck*, char*, ...);
           18 
           19 #pragma varargck argpos error 2
           20 #pragma varargck argpos printnop 1
           21 #pragma varargck argpos warn 2
           22 
           23 static Fsck*
           24 checkInit(Fsck *chk)
           25 {
           26         chk->cache = chk->fs->cache;
           27         chk->nblocks = cacheLocalSize(chk->cache, PartData);;
           28         chk->bsize = chk->fs->blockSize;
           29         chk->walkdepth = 0;
           30         chk->hint = 0;
           31         chk->quantum = chk->nblocks/100;
           32         if(chk->quantum == 0)
           33                 chk->quantum = 1;
           34         if(chk->print == nil)
           35                 chk->print = printnop;
           36         if(chk->clre == nil)
           37                 chk->clre = clrenop;
           38         if(chk->close == nil)
           39                 chk->close = closenop;
           40         if(chk->clri == nil)
           41                 chk->clri = clrinop;
           42         return chk;
           43 }
           44 
           45 /*
           46  * BUG: Should merge checkEpochs and checkDirs so that
           47  * bad blocks are only reported once, and so that errors in checkEpochs
           48  * can have the affected file names attached, and so that the file system
           49  * is only read once.
           50  *
           51  * Also should summarize the errors instead of printing for every one
           52  * (e.g., XXX bad or unreachable blocks in /active/usr/rsc/foo).
           53  */
           54 
           55 void
           56 fsCheck(Fsck *chk)
           57 {
           58         Block *b;
           59         Super super;
           60 
           61         checkInit(chk);
           62         b = superGet(chk->cache, &super);
           63         if(b == nil){
           64                 chk->print("could not load super block: %r");
           65                 return;
           66         }
           67         blockPut(b);
           68 
           69         chk->hint = super.active;
           70         checkEpochs(chk);
           71 
           72         chk->smap = vtmallocz(chk->nblocks/8+1);
           73         checkDirs(chk);
           74         vtfree(chk->smap);
           75 }
           76 
           77 static void checkEpoch(Fsck*, u32int);
           78 
           79 /*
           80  * Walk through all the blocks in the write buffer.
           81  * Then we can look for ones we missed -- those are leaks.
           82  */
           83 static void
           84 checkEpochs(Fsck *chk)
           85 {
           86         u32int e;
           87         uint nb;
           88 
           89         nb = chk->nblocks;
           90         chk->amap = vtmallocz(nb/8+1);
           91         chk->emap = vtmallocz(nb/8+1);
           92         chk->xmap = vtmallocz(nb/8+1);
           93         chk->errmap = vtmallocz(nb/8+1);
           94 
           95         for(e = chk->fs->ehi; e >= chk->fs->elo; e--){
           96                 memset(chk->emap, 0, chk->nblocks/8+1);
           97                 memset(chk->xmap, 0, chk->nblocks/8+1);
           98                 checkEpoch(chk, e);
           99         }
          100         checkLeak(chk);
          101         vtfree(chk->amap);
          102         vtfree(chk->emap);
          103         vtfree(chk->xmap);
          104         vtfree(chk->errmap);
          105 }
          106 
          107 static void
          108 checkEpoch(Fsck *chk, u32int epoch)
          109 {
          110         u32int a;
          111         Block *b;
          112         Entry e;
          113         Label l;
          114 
          115         chk->print("checking epoch %ud...\n", epoch);
          116 
          117         for(a=0; a<chk->nblocks; a++){
          118                 if(!readLabel(chk->cache, &l, (a+chk->hint)%chk->nblocks)){
          119                         error(chk, "could not read label for addr 0x%.8#ux", a);
          120                         continue;
          121                 }
          122                 if(l.tag == RootTag && l.epoch == epoch)
          123                         break;
          124         }
          125 
          126         if(a == chk->nblocks){
          127                 chk->print("could not find root block for epoch %ud", epoch);
          128                 return;
          129         }
          130 
          131         a = (a+chk->hint)%chk->nblocks;
          132         b = cacheLocalData(chk->cache, a, BtDir, RootTag, OReadOnly, 0);
          133         if(b == nil){
          134                 error(chk, "could not read root block 0x%.8#ux: %r", a);
          135                 return;
          136         }
          137 
          138         /* no one should point at root blocks */
          139         setBit(chk->amap, a);
          140         setBit(chk->emap, a);
          141         setBit(chk->xmap, a);
          142 
          143         /*
          144          * First entry is the rest of the file system.
          145          * Second entry is link to previous epoch root,
          146          * just a convenience to help the search.
          147          */
          148         if(!entryUnpack(&e, b->data, 0)){
          149                 error(chk, "could not unpack root block 0x%.8#ux: %r", a);
          150                 blockPut(b);
          151                 return;
          152         }
          153         walkEpoch(chk, b, e.score, BtDir, e.tag, epoch);
          154         if(entryUnpack(&e, b->data, 1))
          155                 chk->hint = globalToLocal(e.score);
          156         blockPut(b);
          157 }
          158 
          159 /*
          160  * When b points at bb, need to check:
          161  *
          162  * (i) b.e in [bb.e, bb.eClose)
          163  * (ii) if b.e==bb.e,  then no other b' in e points at bb.
          164  * (iii) if !(b.state&Copied) and b.e==bb.e then no other b' points at bb.
          165  * (iv) if b is active then no other active b' points at bb.
          166  * (v) if b is a past life of b' then only one of b and b' is active
          167  *        (too hard to check)
          168  */
          169 static int
          170 walkEpoch(Fsck *chk, Block *b, uchar score[VtScoreSize], int type, u32int tag,
          171         u32int epoch)
          172 {
          173         int i, ret;
          174         u32int addr, ep;
          175         Block *bb;
          176         Entry e;
          177 
          178         if(b && chk->walkdepth == 0 && chk->printblocks)
          179                 chk->print("%V %d %#.8ux %#.8ux\n", b->score, b->l.type,
          180                         b->l.tag, b->l.epoch);
          181 
          182         if(!chk->useventi && globalToLocal(score) == NilBlock)
          183                 return 1;
          184 
          185         chk->walkdepth++;
          186 
          187         bb = cacheGlobal(chk->cache, score, type, tag, OReadOnly);
          188         if(bb == nil){
          189                 error(chk, "could not load block %V type %d tag %ux: %r",
          190                         score, type, tag);
          191                 chk->walkdepth--;
          192                 return 0;
          193         }
          194         if(chk->printblocks)
          195                 chk->print("%*s%V %d %#.8ux %#.8ux\n", chk->walkdepth*2, "",
          196                         score, type, tag, bb->l.epoch);
          197 
          198         ret = 0;
          199         addr = globalToLocal(score);
          200         if(addr == NilBlock){
          201                 ret = 1;
          202                 goto Exit;
          203         }
          204 
          205         if(b){
          206                 /* (i) */
          207                 if(b->l.epoch < bb->l.epoch || bb->l.epochClose <= b->l.epoch){
          208                         error(chk, "walk: block %#ux [%ud, %ud) points at %#ux [%ud, %ud)",
          209                                 b->addr, b->l.epoch, b->l.epochClose,
          210                                 bb->addr, bb->l.epoch, bb->l.epochClose);
          211                         goto Exit;
          212                 }
          213 
          214                 /* (ii) */
          215                 if(b->l.epoch == epoch && bb->l.epoch == epoch){
          216                         if(getBit(chk->emap, addr)){
          217                                 error(chk, "walk: epoch join detected: addr %#ux %L",
          218                                         bb->addr, &bb->l);
          219                                 goto Exit;
          220                         }
          221                         setBit(chk->emap, addr);
          222                 }
          223 
          224                 /* (iii) */
          225                 if(!(b->l.state&BsCopied) && b->l.epoch == bb->l.epoch){
          226                         if(getBit(chk->xmap, addr)){
          227                                 error(chk, "walk: copy join detected; addr %#ux %L",
          228                                         bb->addr, &bb->l);
          229                                 goto Exit;
          230                         }
          231                         setBit(chk->xmap, addr);
          232                 }
          233         }
          234 
          235         /* (iv) */
          236         if(epoch == chk->fs->ehi){
          237                 /*
          238                  * since epoch==fs->ehi is first, amap is same as
          239                  * ``have seen active''
          240                  */
          241                 if(getBit(chk->amap, addr)){
          242                         error(chk, "walk: active join detected: addr %#ux %L",
          243                                 bb->addr, &bb->l);
          244                         goto Exit;
          245                 }
          246                 if(bb->l.state&BsClosed)
          247                         error(chk, "walk: addr %#ux: block is in active tree but is closed",
          248                                 addr);
          249         }else
          250                 if(!getBit(chk->amap, addr))
          251                         if(!(bb->l.state&BsClosed)){
          252                                 // error(chk, "walk: addr %#ux: block is not in active tree, not closed (%d)",
          253                                 // addr, bb->l.epochClose);
          254                                 chk->close(chk, bb, epoch+1);
          255                                 chk->nclose++;
          256                         }
          257 
          258         if(getBit(chk->amap, addr)){
          259                 ret = 1;
          260                 goto Exit;
          261         }
          262         setBit(chk->amap, addr);
          263 
          264         if(chk->nseen++%chk->quantum == 0)
          265                 chk->print("check: visited %d/%d blocks (%.0f%%)\n",
          266                         chk->nseen, chk->nblocks, chk->nseen*100./chk->nblocks);
          267 
          268         b = nil;                /* make sure no more refs to parent */
          269         USED(b);
          270 
          271         switch(type){
          272         default:
          273                 /* pointer block */
          274                 for(i = 0; i < chk->bsize/VtScoreSize; i++)
          275                         if(!walkEpoch(chk, bb, bb->data + i*VtScoreSize,
          276                             type-1, tag, epoch)){
          277                                 setBit(chk->errmap, bb->addr);
          278                                 chk->clrp(chk, bb, i);
          279                                 chk->nclrp++;
          280                         }
          281                 break;
          282         case BtData:
          283                 break;
          284         case BtDir:
          285                 for(i = 0; i < chk->bsize/VtEntrySize; i++){
          286                         if(!entryUnpack(&e, bb->data, i)){
          287                                 // error(chk, "walk: could not unpack entry: %ux[%d]: %r",
          288                                 //        addr, i);
          289                                 setBit(chk->errmap, bb->addr);
          290                                 chk->clre(chk, bb, i);
          291                                 chk->nclre++;
          292                                 continue;
          293                         }
          294                         if(!(e.flags & VtEntryActive))
          295                                 continue;
          296 if(0)                        fprint(2, "%x[%d] tag=%x snap=%d score=%V\n",
          297                                 addr, i, e.tag, e.snap, e.score);
          298                         ep = epoch;
          299                         if(e.snap != 0){
          300                                 if(e.snap >= epoch){
          301                                         // error(chk, "bad snap in entry: %ux[%d] snap = %ud: epoch = %ud",
          302                                         //        addr, i, e.snap, epoch);
          303                                         setBit(chk->errmap, bb->addr);
          304                                         chk->clre(chk, bb, i);
          305                                         chk->nclre++;
          306                                         continue;
          307                                 }
          308                                 continue;
          309                         }
          310                         if(e.flags & VtEntryLocal){
          311                                 if(e.tag < UserTag)
          312                                 if(e.tag != RootTag || tag != RootTag || i != 1){
          313                                         // error(chk, "bad tag in entry: %ux[%d] tag = %ux",
          314                                         //        addr, i, e.tag);
          315                                         setBit(chk->errmap, bb->addr);
          316                                         chk->clre(chk, bb, i);
          317                                         chk->nclre++;
          318                                         continue;
          319                                 }
          320                         }else
          321                                 if(e.tag != 0){
          322                                         // error(chk, "bad tag in entry: %ux[%d] tag = %ux",
          323                                         //        addr, i, e.tag);
          324                                         setBit(chk->errmap, bb->addr);
          325                                         chk->clre(chk, bb, i);
          326                                         chk->nclre++;
          327                                         continue;
          328                                 }
          329                         if(!walkEpoch(chk, bb, e.score, entryType(&e),
          330                             e.tag, ep)){
          331                                 setBit(chk->errmap, bb->addr);
          332                                 chk->clre(chk, bb, i);
          333                                 chk->nclre++;
          334                         }
          335                 }
          336                 break;
          337         }
          338 
          339         ret = 1;
          340 
          341 Exit:
          342         chk->walkdepth--;
          343         blockPut(bb);
          344         return ret;
          345 }
          346 
          347 /*
          348  * We've just walked the whole write buffer.  Notice blocks that
          349  * aren't marked available but that we didn't visit.  They are lost.
          350  */
          351 static void
          352 checkLeak(Fsck *chk)
          353 {
          354         u32int a, nfree, nlost;
          355         Block *b;
          356         Label l;
          357 
          358         nfree = 0;
          359         nlost = 0;
          360 
          361         for(a = 0; a < chk->nblocks; a++){
          362                 if(!readLabel(chk->cache, &l, a)){
          363                         error(chk, "could not read label: addr 0x%ux %d %d: %r",
          364                                 a, l.type, l.state);
          365                         continue;
          366                 }
          367                 if(getBit(chk->amap, a))
          368                         continue;
          369                 if(l.state == BsFree || l.epochClose <= chk->fs->elo ||
          370                     l.epochClose == l.epoch){
          371                         nfree++;
          372                         setBit(chk->amap, a);
          373                         continue;
          374                 }
          375                 if(l.state&BsClosed)
          376                         continue;
          377                 nlost++;
          378 //                warn(chk, "unreachable block: addr 0x%ux type %d tag 0x%ux "
          379 //                        "state %s epoch %ud close %ud", a, l.type, l.tag,
          380 //                        bsStr(l.state), l.epoch, l.epochClose);
          381                 b = cacheLocal(chk->cache, PartData, a, OReadOnly);
          382                 if(b == nil){
          383                         error(chk, "could not read block 0x%#.8ux", a);
          384                         continue;
          385                 }
          386                 chk->close(chk, b, 0);
          387                 chk->nclose++;
          388                 setBit(chk->amap, a);
          389                 blockPut(b);
          390         }
          391         chk->print("fsys blocks: total=%ud used=%ud(%.1f%%) free=%ud(%.1f%%) lost=%ud(%.1f%%)\n",
          392                 chk->nblocks,
          393                 chk->nblocks - nfree-nlost,
          394                 100.*(chk->nblocks - nfree - nlost)/chk->nblocks,
          395                 nfree, 100.*nfree/chk->nblocks,
          396                 nlost, 100.*nlost/chk->nblocks);
          397 }
          398 
          399 
          400 /*
          401  * Check that all sources in the tree are accessible.
          402  */
          403 static Source *
          404 openSource(Fsck *chk, Source *s, char *name, uchar *bm, u32int offset,
          405         u32int gen, int dir, MetaBlock *mb, int i, Block *b)
          406 {
          407         Source *r;
          408 
          409         r = nil;
          410         if(getBit(bm, offset)){
          411                 warn(chk, "multiple references to source: %s -> %d",
          412                         name, offset);
          413                 goto Err;
          414         }
          415         setBit(bm, offset);
          416 
          417         r = sourceOpen(s, offset, OReadOnly, 0);
          418         if(r == nil){
          419                 warn(chk, "could not open source: %s -> %d: %r", name, offset);
          420                 goto Err;
          421         }
          422 
          423         if(r->gen != gen){
          424                 warn(chk, "source has been removed: %s -> %d", name, offset);
          425                 goto Err;
          426         }
          427 
          428         if(r->dir != dir){
          429                 warn(chk, "dir mismatch: %s -> %d", name, offset);
          430                 goto Err;
          431         }
          432         return r;
          433 Err:
          434         chk->clri(chk, name, mb, i, b);
          435         chk->nclri++;
          436         if(r)
          437                 sourceClose(r);
          438         return nil;
          439 }
          440 
          441 typedef struct MetaChunk MetaChunk;
          442 struct MetaChunk {
          443         ushort        offset;
          444         ushort        size;
          445         ushort        index;
          446 };
          447 
          448 static int
          449 offsetCmp(const void *s0, const void *s1)
          450 {
          451         MetaChunk *mc0, *mc1;
          452 
          453         mc0 = (MetaChunk*)s0;
          454         mc1 = (MetaChunk*)s1;
          455         if(mc0->offset < mc1->offset)
          456                 return -1;
          457         if(mc0->offset > mc1->offset)
          458                 return 1;
          459         return 0;
          460 }
          461 
          462 /*
          463  * Fsck that MetaBlock has reasonable header, sorted entries,
          464  */
          465 static int
          466 chkMetaBlock(MetaBlock *mb)
          467 {
          468         MetaChunk *mc;
          469         int oo, o, n, i;
          470         uchar *p;
          471 
          472         mc = vtmalloc(mb->nindex*sizeof(MetaChunk));
          473         p = mb->buf + MetaHeaderSize;
          474         for(i = 0; i < mb->nindex; i++){
          475                 mc[i].offset = p[0]<<8 | p[1];
          476                 mc[i].size =   p[2]<<8 | p[3];
          477                 mc[i].index = i;
          478                 p += MetaIndexSize;
          479         }
          480 
          481         qsort(mc, mb->nindex, sizeof(MetaChunk), offsetCmp);
          482 
          483         /* check block looks ok */
          484         oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
          485         o = oo;
          486         n = 0;
          487         for(i = 0; i < mb->nindex; i++){
          488                 o = mc[i].offset;
          489                 n = mc[i].size;
          490                 if(o < oo)
          491                         goto Err;
          492                 oo += n;
          493         }
          494         if(o+n > mb->size || mb->size - oo != mb->free)
          495                 goto Err;
          496 
          497         vtfree(mc);
          498         return 1;
          499 
          500 Err:
          501 if(0){
          502         fprint(2, "metaChunks failed!\n");
          503         oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
          504         for(i=0; i<mb->nindex; i++){
          505                 fprint(2, "\t%d: %d %d\n", i, mc[i].offset,
          506                         mc[i].offset + mc[i].size);
          507                 oo += mc[i].size;
          508         }
          509         fprint(2, "\tused=%d size=%d free=%d free2=%d\n",
          510                 oo, mb->size, mb->free, mb->size - oo);
          511 }
          512         vtfree(mc);
          513         return 0;
          514 }
          515 
          516 static void
          517 scanSource(Fsck *chk, char *name, Source *r)
          518 {
          519         u32int a, nb, o;
          520         Block *b;
          521         Entry e;
          522 
          523         if(!chk->useventi && globalToLocal(r->score)==NilBlock)
          524                 return;
          525         if(!sourceGetEntry(r, &e)){
          526                 error(chk, "could not get entry for %s", name);
          527                 return;
          528         }
          529         a = globalToLocal(e.score);
          530         if(!chk->useventi && a==NilBlock)
          531                 return;
          532         if(getBit(chk->smap, a))
          533                 return;
          534         setBit(chk->smap, a);
          535 
          536         nb = (sourceGetSize(r) + r->dsize-1) / r->dsize;
          537         for(o = 0; o < nb; o++){
          538                 b = sourceBlock(r, o, OReadOnly);
          539                 if(b == nil){
          540                         error(chk, "could not read block in data file %s", name);
          541                         continue;
          542                 }
          543                 if(b->addr != NilBlock && getBit(chk->errmap, b->addr)){
          544                         warn(chk, "previously reported error in block %ux is in file %s",
          545                                 b->addr, name);
          546                 }
          547                 blockPut(b);
          548         }
          549 }
          550 
          551 /*
          552  * Walk the source tree making sure that the BtData
          553  * sources containing directory entries are okay.
          554  */
          555 static void
          556 chkDir(Fsck *chk, char *name, Source *source, Source *meta)
          557 {
          558         int i;
          559         u32int a1, a2, nb, o;
          560         char *s, *nn;
          561         uchar *bm;
          562         Block *b, *bb;
          563         DirEntry de;
          564         Entry e1, e2;
          565         MetaBlock mb;
          566         MetaEntry me;
          567         Source *r, *mr;
          568 
          569         if(!chk->useventi && globalToLocal(source->score)==NilBlock &&
          570             globalToLocal(meta->score)==NilBlock)
          571                 return;
          572 
          573         if(!sourceLock2(source, meta, OReadOnly)){
          574                 warn(chk, "could not lock sources for %s: %r", name);
          575                 return;
          576         }
          577         if(!sourceGetEntry(source, &e1) || !sourceGetEntry(meta, &e2)){
          578                 warn(chk, "could not load entries for %s: %r", name);
          579                 return;
          580         }
          581         a1 = globalToLocal(e1.score);
          582         a2 = globalToLocal(e2.score);
          583         if((!chk->useventi && a1==NilBlock && a2==NilBlock)
          584         || (getBit(chk->smap, a1) && getBit(chk->smap, a2))){
          585                 sourceUnlock(source);
          586                 sourceUnlock(meta);
          587                 return;
          588         }
          589         setBit(chk->smap, a1);
          590         setBit(chk->smap, a2);
          591 
          592         bm = vtmallocz(sourceGetDirSize(source)/8 + 1);
          593 
          594         nb = (sourceGetSize(meta) + meta->dsize - 1)/meta->dsize;
          595         for(o = 0; o < nb; o++){
          596                 b = sourceBlock(meta, o, OReadOnly);
          597                 if(b == nil){
          598                         error(chk, "could not read block in meta file: %s[%ud]: %r",
          599                                 name, o);
          600                         continue;
          601                 }
          602 if(0)                fprint(2, "source %V:%d block %d addr %d\n", source->score,
          603                         source->offset, o, b->addr);
          604                 if(b->addr != NilBlock && getBit(chk->errmap, b->addr))
          605                         warn(chk, "previously reported error in block %ux is in %s",
          606                                 b->addr, name);
          607 
          608                 if(!mbUnpack(&mb, b->data, meta->dsize)){
          609                         error(chk, "could not unpack meta block: %s[%ud]: %r",
          610                                 name, o);
          611                         blockPut(b);
          612                         continue;
          613                 }
          614                 if(!chkMetaBlock(&mb)){
          615                         error(chk, "bad meta block: %s[%ud]: %r", name, o);
          616                         blockPut(b);
          617                         continue;
          618                 }
          619                 s = nil;
          620                 for(i=mb.nindex-1; i>=0; i--){
          621                         meUnpack(&me, &mb, i);
          622                         if(!deUnpack(&de, &me)){
          623                                 error(chk,
          624                                   "could not unpack dir entry: %s[%ud][%d]: %r",
          625                                         name, o, i);
          626                                 continue;
          627                         }
          628                         if(s && strcmp(s, de.elem) <= 0)
          629                                 error(chk,
          630                            "dir entry out of order: %s[%ud][%d] = %s last = %s",
          631                                         name, o, i, de.elem, s);
          632                         vtfree(s);
          633                         s = vtstrdup(de.elem);
          634                         nn = smprint("%s/%s", name, de.elem);
          635                         if(nn == nil){
          636                                 error(chk, "out of memory");
          637                                 continue;
          638                         }
          639                         if(chk->printdirs)
          640                                 if(de.mode&ModeDir)
          641                                         chk->print("%s/\n", nn);
          642                         if(chk->printfiles)
          643                                 if(!(de.mode&ModeDir))
          644                                         chk->print("%s\n", nn);
          645                         if(!(de.mode & ModeDir)){
          646                                 r = openSource(chk, source, nn, bm, de.entry,
          647                                         de.gen, 0, &mb, i, b);
          648                                 if(r != nil){
          649                                         if(sourceLock(r, OReadOnly)){
          650                                                 scanSource(chk, nn, r);
          651                                                 sourceUnlock(r);
          652                                         }
          653                                         sourceClose(r);
          654                                 }
          655                                 deCleanup(&de);
          656                                 free(nn);
          657                                 continue;
          658                         }
          659 
          660                         r = openSource(chk, source, nn, bm, de.entry,
          661                                 de.gen, 1, &mb, i, b);
          662                         if(r == nil){
          663                                 deCleanup(&de);
          664                                 free(nn);
          665                                 continue;
          666                         }
          667 
          668                         mr = openSource(chk, source, nn, bm, de.mentry,
          669                                 de.mgen, 0, &mb, i, b);
          670                         if(mr == nil){
          671                                 sourceClose(r);
          672                                 deCleanup(&de);
          673                                 free(nn);
          674                                 continue;
          675                         }
          676 
          677                         if(!(de.mode&ModeSnapshot) || chk->walksnapshots)
          678                                 chkDir(chk, nn, r, mr);
          679 
          680                         sourceClose(mr);
          681                         sourceClose(r);
          682                         deCleanup(&de);
          683                         free(nn);
          684                         deCleanup(&de);
          685 
          686                 }
          687                 vtfree(s);
          688                 blockPut(b);
          689         }
          690 
          691         nb = sourceGetDirSize(source);
          692         for(o=0; o<nb; o++){
          693                 if(getBit(bm, o))
          694                         continue;
          695                 r = sourceOpen(source, o, OReadOnly, 0);
          696                 if(r == nil)
          697                         continue;
          698                 warn(chk, "non referenced entry in source %s[%d]", name, o);
          699                 if((bb = sourceBlock(source, o/(source->dsize/VtEntrySize),
          700                     OReadOnly)) != nil){
          701                         if(bb->addr != NilBlock){
          702                                 setBit(chk->errmap, bb->addr);
          703                                 chk->clre(chk, bb, o%(source->dsize/VtEntrySize));
          704                                 chk->nclre++;
          705                         }
          706                         blockPut(bb);
          707                 }
          708                 sourceClose(r);
          709         }
          710 
          711         sourceUnlock(source);
          712         sourceUnlock(meta);
          713         vtfree(bm);
          714 }
          715 
          716 static void
          717 checkDirs(Fsck *chk)
          718 {
          719         Source *r, *mr;
          720 
          721         sourceLock(chk->fs->source, OReadOnly);
          722         r = sourceOpen(chk->fs->source, 0, OReadOnly, 0);
          723         mr = sourceOpen(chk->fs->source, 1, OReadOnly, 0);
          724         sourceUnlock(chk->fs->source);
          725         chkDir(chk, "", r, mr);
          726 
          727         sourceClose(r);
          728         sourceClose(mr);
          729 }
          730 
          731 static void
          732 setBit(uchar *bmap, u32int addr)
          733 {
          734         if(addr == NilBlock)
          735                 return;
          736 
          737         bmap[addr>>3] |= 1 << (addr & 7);
          738 }
          739 
          740 static int
          741 getBit(uchar *bmap, u32int addr)
          742 {
          743         if(addr == NilBlock)
          744                 return 0;
          745 
          746         return (bmap[addr>>3] >> (addr & 7)) & 1;
          747 }
          748 
          749 static void
          750 error(Fsck *chk, char *fmt, ...)
          751 {
          752         char buf[256];
          753         va_list arg;
          754 
          755         va_start(arg, fmt);
          756         vseprint(buf, buf+sizeof buf, fmt, arg);
          757         va_end(arg);
          758 
          759         chk->print("error: %s\n", buf);
          760 
          761 //        if(nerr++ > 20)
          762 //                sysfatal("too many errors");
          763 }
          764 
          765 static void
          766 warn(Fsck *chk, char *fmt, ...)
          767 {
          768         char buf[256];
          769         va_list arg;
          770 
          771         va_start(arg, fmt);
          772         vseprint(buf, buf+sizeof buf, fmt, arg);
          773         va_end(arg);
          774 
          775         chk->print("error: %s\n", buf);
          776 }
          777 
          778 static void
          779 clrenop(Fsck *chk, Block *b, int i)
          780 {
          781         USED(chk);
          782         USED(b);
          783         USED(i);
          784 }
          785 
          786 static void
          787 closenop(Fsck *chk, Block *b, u32int i)
          788 {
          789         USED(chk);
          790         USED(b);
          791         USED(i);
          792 }
          793 
          794 static void
          795 clrinop(Fsck *chk, char *c, MetaBlock *mb, int i, Block *b)
          796 {
          797         USED(chk);
          798         USED(c);
          799         USED(mb);
          800         USED(i);
          801         USED(b);
          802 }
          803 
          804 static int
          805 printnop(char *c, ...)
          806 {
          807         USED(c);
          808         return 0;
          809 }