check.c 17 KB

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