check.c 17 KB

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