flchk.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. #include "stdinc.h"
  2. #include "dat.h"
  3. #include "fns.h"
  4. typedef struct MetaChunk MetaChunk;
  5. struct MetaChunk {
  6. ushort offset;
  7. ushort size;
  8. ushort index;
  9. };
  10. static void usage(void);
  11. static void setBit(uchar *bmap, ulong addr);
  12. static int getBit(uchar *bmap, ulong addr);
  13. static int readLabel(Label *l, u32int addr);
  14. static void error(char*, ...);
  15. static void warn(char *fmt, ...);
  16. static void chkEpoch(u32int epoch);
  17. static void chkFree(void);
  18. static int readLabel(Label *l, u32int addr);
  19. static void chkDir(char *name, Source *source, Source *meta);
  20. #pragma varargck argpos error 1
  21. #pragma varargck argpos warn 1
  22. uchar *amap; /* bitmap: has been visited at all */
  23. uchar *emap; /* bitmap: see condition (ii) below */
  24. uchar *vmap; /* bitmap: has been visited in this epoch */
  25. uchar *xmap; /* bitmap: see condition (iii) below */
  26. Fs *fs;
  27. Cache *cache;
  28. int nblocks;
  29. int bsize;
  30. int badactive;
  31. int fast; /* don't check that all the venti blocks are there */
  32. u32int hint; /* a guess at where chkEpoch might look to find the next root */
  33. void
  34. main(int argc, char *argv[])
  35. {
  36. int csize = 1000;
  37. VtSession *z;
  38. char *host = nil;
  39. u32int e;
  40. Source *r, *mr;
  41. Block *b;
  42. Super super;
  43. ARGBEGIN{
  44. default:
  45. usage();
  46. case 'c':
  47. csize = atoi(ARGF());
  48. break;
  49. case 'f':
  50. fast = 1;
  51. break;
  52. case 'h':
  53. host = ARGF();
  54. break;
  55. }ARGEND;
  56. if(argc != 1)
  57. usage();
  58. vtAttach();
  59. fmtinstall('L', labelFmt);
  60. fmtinstall('V', scoreFmt);
  61. fmtinstall('R', vtErrFmt);
  62. /*
  63. * Connect to Venti.
  64. */
  65. z = vtDial(host, 0);
  66. if(z == nil)
  67. vtFatal("could not connect to server: %s", vtGetError());
  68. if(!vtConnect(z, 0))
  69. vtFatal("vtConnect: %s", vtGetError());
  70. /*
  71. * Initialize file system.
  72. */
  73. fs = fsOpen(argv[0], z, csize, OReadOnly);
  74. if(fs == nil)
  75. vtFatal("could not open file system: %R");
  76. cache = fs->cache;
  77. nblocks = cacheLocalSize(cache, PartData);
  78. bsize = fs->blockSize;
  79. b = superGet(cache, &super);
  80. if(b == nil)
  81. vtFatal("could not load super block: %R");
  82. blockPut(b);
  83. hint = super.active;
  84. /*
  85. * Run checks.
  86. */
  87. amap = vtMemAllocZ(nblocks/8 + 1);
  88. emap = vtMemAllocZ(nblocks/8 + 1);
  89. vmap = vtMemAllocZ(nblocks/8 + 1);
  90. xmap = vtMemAllocZ(nblocks/8 + 1);
  91. for(e=fs->ehi; e >= fs->elo; e--){
  92. memset(emap, 0, nblocks/8+1);
  93. memset(xmap, 0, nblocks/8+1);
  94. chkEpoch(e);
  95. }
  96. chkFree();
  97. vtMemFree(amap);
  98. vtMemFree(emap);
  99. vtMemFree(vmap);
  100. vtMemFree(xmap);
  101. sourceLock(fs->source, OReadOnly);
  102. r = sourceOpen(fs->source, 0, OReadOnly);
  103. mr = sourceOpen(fs->source, 1, OReadOnly);
  104. sourceUnlock(fs->source);
  105. chkDir("", r, mr);
  106. sourceClose(r);
  107. sourceClose(mr);
  108. fsClose(fs);
  109. exits(0);
  110. }
  111. static void
  112. usage(void)
  113. {
  114. fprint(2, "usage: %s [-c cachesize] [-h host] file\n", argv0);
  115. exits("usage");
  116. }
  117. /*
  118. * When b points at bb, need to check:
  119. *
  120. * (i) b.e in [bb.e, bb.eClose)
  121. * (ii) if b.e==bb.e, then no other b' in e points at bb.
  122. * (iii) if !(b.state&Copied) and b.e==bb.e then no other b' points at bb.
  123. * (iv) if b is active then no other active b' points at bb.
  124. * (v) if b is a past life of b' then only one of b and b' is active (too hard to check)
  125. *
  126. * Does not walk onto Venti.
  127. */
  128. static int
  129. walk(Block *b, uchar score[VtScoreSize], int type, u32int tag, u32int epoch)
  130. {
  131. Block *bb;
  132. u32int addr;
  133. int i, ret;
  134. u32int ep;
  135. Entry e;
  136. if(fast && globalToLocal(score) == NilBlock)
  137. return 1;
  138. bb = cacheGlobal(cache, score, type, tag, OReadOnly);
  139. if(bb == nil){
  140. error("could not load block %V type %d tag %ux: %R", score, type, tag);
  141. return 0;
  142. }
  143. ret = 0;
  144. addr = globalToLocal(score);
  145. if(addr == NilBlock){
  146. ret = 1;
  147. goto Exit;
  148. }
  149. /* (i) */
  150. if(b->l.epoch < bb->l.epoch || bb->l.epochClose <= b->l.epoch){
  151. error("walk: block %#ux [%ud, %ud) points at %#ux [%ud, %ud)\n",
  152. b->addr, b->l.epoch, b->l.epochClose,
  153. bb->addr, bb->l.epoch, bb->l.epochClose);
  154. goto Exit;
  155. }
  156. /* (ii) */
  157. if(b->l.epoch == epoch && bb->l.epoch == epoch){
  158. if(getBit(emap, addr)){
  159. error("walk: epoch join detected: addr %#ux %L\n", bb->addr, &bb->l);
  160. goto Exit;
  161. }
  162. setBit(emap, addr);
  163. }
  164. /* (iii) */
  165. if(!(b->l.state&BsCopied) && b->l.epoch == bb->l.epoch){
  166. if(getBit(xmap, addr)){
  167. error("walk: copy join detected; addr %#ux %L\n", bb->addr, &bb->l);
  168. goto Exit;
  169. }
  170. setBit(xmap, addr);
  171. }
  172. /* (iv) */
  173. if(epoch == fs->ehi){
  174. /* since epoch==fs->ehi is first, amap is same as ``have seen active'' */
  175. if(getBit(amap, addr)){
  176. error("walk: active join detected: addr %#ux %L\n", bb->addr, &bb->l);
  177. goto Exit;
  178. }
  179. }
  180. if(getBit(vmap, addr)){
  181. ret = 1;
  182. goto Exit;
  183. }
  184. setBit(vmap, addr);
  185. setBit(amap, addr);
  186. b = nil; /* make sure no more refs to parent */
  187. USED(b);
  188. switch(type){
  189. default:
  190. /* pointer block */
  191. for(i=0; i<bsize/VtScoreSize; i++)
  192. if(!walk(bb, bb->data + i*VtScoreSize, type-1, tag, epoch))
  193. print("# clrp %#ux %d\n", bb->addr, i);
  194. break;
  195. case BtData:
  196. break;
  197. case BtDir:
  198. for(i=0; i<bsize/VtEntrySize; i++){
  199. if(!entryUnpack(&e, bb->data, i)){
  200. error("walk: could not unpack entry: %ux[%d]: %R", addr, i);
  201. print("# clre %#ux %d\n", bb->addr, i);
  202. continue;
  203. }
  204. if(!(e.flags & VtEntryActive))
  205. continue;
  206. //fprint(2, "%x[%d] tag=%x snap=%d score=%V\n", addr, i, e.tag, e.snap, e.score);
  207. ep = epoch;
  208. if(e.snap != 0){
  209. if(e.snap >= epoch){
  210. error("bad snap in entry: %ux[%d] snap = %ud: epoch = %ud",
  211. addr, i, e.snap, epoch);
  212. print("# clre %#ux %d\n", bb->addr, i);
  213. continue;
  214. }
  215. continue;
  216. }
  217. if(e.flags & VtEntryLocal){
  218. if(e.tag < UserTag)
  219. if(e.tag != RootTag || tag != RootTag || i != 1){
  220. error("bad tag in entry: %ux[%d] tag = %ux", addr, i, e.tag);
  221. print("# clre %#ux %d\n", bb->addr, i);
  222. continue;
  223. }
  224. }else{
  225. if(e.tag != 0){
  226. error("bad tag in entry: %ux[%d] tag = %ux", addr, i, e.tag);
  227. print("# clre %#ux %d\n", bb->addr, i);
  228. continue;
  229. }
  230. }
  231. if(!walk(bb, e.score, entryType(&e), e.tag, ep))
  232. print("# clre %#ux %d\n", bb->addr, i);
  233. }
  234. break;
  235. }
  236. ret = 1;
  237. Exit:
  238. blockPut(bb);
  239. return ret;
  240. }
  241. static void
  242. chkEpoch(u32int epoch)
  243. {
  244. u32int a;
  245. Label l;
  246. Entry e;
  247. Block *b;
  248. print("chkEpoch %ud\n", epoch);
  249. /* find root block */
  250. for(a=0; a<nblocks; a++){
  251. if(!readLabel(&l, (a+hint)%nblocks)){
  252. error("could not read label: addr %ux %d %ux %ux: %R", a, l.type, l.state, l.state);
  253. continue;
  254. }
  255. if(l.tag == RootTag && l.epoch == epoch)
  256. break;
  257. }
  258. if(a == nblocks){
  259. print("chkEpoch: could not find root block for epoch: %ud\n", epoch);
  260. return;
  261. }
  262. a = (a+hint)%nblocks;
  263. b = cacheLocalData(cache, a, BtDir, RootTag, OReadOnly, 0);
  264. if(b == nil){
  265. error("could not read root block %ux: %R\n", a);
  266. return;
  267. }
  268. /* no one should point at the root blocks */
  269. setBit(amap, a);
  270. setBit(emap, a);
  271. setBit(vmap, a);
  272. setBit(xmap, a);
  273. /*
  274. * First entry is the rest of the file system.
  275. * Second entry is link to previous epoch root,
  276. * just a convenience to help the search.
  277. */
  278. if(!entryUnpack(&e, b->data, 0)){
  279. error("could not unpack root block %ux: %R", a);
  280. blockPut(b);
  281. return;
  282. }
  283. walk(b, e.score, BtDir, e.tag, epoch);
  284. if(entryUnpack(&e, b->data, 1))
  285. hint = globalToLocal(e.score);
  286. blockPut(b);
  287. }
  288. /*
  289. * We've just walked the whole write buffer. Notice blocks that
  290. * aren't marked available but that we didn't visit. They are lost.
  291. */
  292. static void
  293. chkFree(void)
  294. {
  295. u32int a;
  296. Label l;
  297. u32int nfree;
  298. u32int nlost;
  299. nfree = 0;
  300. nlost = 0;
  301. /* find root block */
  302. for(a=0; a<nblocks; a++){
  303. if(!readLabel(&l, a)){
  304. error("could not read label: addr %ux %d %d: %R",
  305. a, l.type, l.state);
  306. continue;
  307. }
  308. if(getBit(amap, a))
  309. continue;
  310. if(l.state == BsFree || l.epochClose <= fs->elo){
  311. nfree++;
  312. setBit(amap, a);
  313. continue;
  314. }
  315. nlost++;
  316. warn("unreachable block: addr %ux type %d tag %ux state %s epoch %ud",
  317. a, l.type, l.tag, bsStr(l.state), l.epoch);
  318. print("# bfree %#ux\n", a);
  319. setBit(amap, a);
  320. }
  321. fprint(2, "\tused=%ud free space = %ud(%f%%) lost=%ud\n",
  322. nblocks-nfree-nlost, nblocks, 100.*nfree/nblocks, nlost);
  323. }
  324. static Source *
  325. openSource(Source *s, char *name, uchar *bm, u32int offset, u32int gen, int dir)
  326. {
  327. Source *r;
  328. if(getBit(bm, offset)){
  329. warn("multiple references to source: %s -> %d", name, offset);
  330. print("# clri %s\n", name);
  331. return nil;
  332. }
  333. setBit(bm, offset);
  334. r = sourceOpen(s, offset, OReadOnly);
  335. if(r == nil){
  336. warn("could not open source: %s -> %d: %R", name, offset);
  337. print("# clri %s\n", name);
  338. return nil;
  339. }
  340. if(r->gen != gen){
  341. warn("source has been removed: %s -> %d", name, offset);
  342. print("# clri %s\n", name);
  343. goto Err;
  344. }
  345. if(r->dir != dir){
  346. warn("dir mismatch: %s -> %d", name, offset);
  347. print("# clri %s\n", name);
  348. goto Err;
  349. }
  350. return r;
  351. Err:
  352. sourceClose(r);
  353. return nil;
  354. }
  355. static int
  356. offsetCmp(void *s0, void *s1)
  357. {
  358. MetaChunk *mc0, *mc1;
  359. mc0 = s0;
  360. mc1 = s1;
  361. if(mc0->offset < mc1->offset)
  362. return -1;
  363. if(mc0->offset > mc1->offset)
  364. return 1;
  365. return 0;
  366. }
  367. /*
  368. * Check that MetaBlock has reasonable header, sorted entries,
  369. */
  370. int
  371. chkMetaBlock(MetaBlock *mb)
  372. {
  373. MetaChunk *mc;
  374. int oo, o, n, i;
  375. uchar *p;
  376. mc = vtMemAlloc(mb->nindex*sizeof(MetaChunk));
  377. p = mb->buf + MetaHeaderSize;
  378. for(i = 0; i<mb->nindex; i++){
  379. mc[i].offset = (p[0]<<8) | p[1];
  380. mc[i].size = (p[2]<<8) | p[3];
  381. mc[i].index = i;
  382. p += MetaIndexSize;
  383. }
  384. qsort(mc, mb->nindex, sizeof(MetaChunk), offsetCmp);
  385. /* check block looks ok */
  386. oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
  387. o = oo;
  388. n = 0;
  389. for(i=0; i<mb->nindex; i++){
  390. o = mc[i].offset;
  391. n = mc[i].size;
  392. if(o < oo)
  393. goto Err;
  394. oo += n;
  395. }
  396. if(o+n > mb->size)
  397. goto Err;
  398. if(mb->size - oo != mb->free)
  399. goto Err;
  400. vtMemFree(mc);
  401. return 1;
  402. Err:
  403. fprint(2, "metaChunks failed!\n");
  404. oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
  405. for(i=0; i<mb->nindex; i++){
  406. fprint(2, "\t%d: %d %d\n", i, mc[i].offset, mc[i].offset + mc[i].size);
  407. oo += mc[i].size;
  408. }
  409. fprint(2, "\tused=%d size=%d free=%d free2=%d\n", oo, mb->size, mb->free, mb->size - oo);
  410. vtMemFree(mc);
  411. return 0;
  412. }
  413. /*
  414. * Walk the source tree making sure that the BtData
  415. * sources containing directory entries are okay.
  416. *
  417. * Walks onto Venti, so takes a long time.
  418. */
  419. static void
  420. chkDir(char *name, Source *source, Source *meta)
  421. {
  422. uchar *bm;
  423. Block *b, *bb;
  424. u32int nb, o;
  425. MetaBlock mb;
  426. DirEntry de;
  427. MetaEntry me;
  428. int i;
  429. char *s, *nn;
  430. Source *r, *mr;
  431. if(fast && globalToLocal(source->score)==NilBlock && globalToLocal(meta->score)==NilBlock)
  432. return;
  433. if(!sourceLock2(source, meta, OReadOnly)){
  434. warn("could not lock sources for %s: %R", name);
  435. return;
  436. }
  437. bm = vtMemAllocZ(sourceGetDirSize(source)/8 + 1);
  438. nb = (sourceGetSize(meta) + meta->dsize - 1)/meta->dsize;
  439. for(o=0; o<nb; o++){
  440. b = sourceBlock(meta, o, OReadOnly);
  441. if(0)fprint(2, "source %V:%d block %d addr %d\n", source->score, source->offset, o, b->addr);
  442. if(b == nil){
  443. error("could not read block in meta file: %s[%ud]: %R", name, o);
  444. continue;
  445. }
  446. if(!mbUnpack(&mb, b->data, meta->dsize)){
  447. error("could not unpack meta block: %s[%ud]: %R", name, o);
  448. blockPut(b);
  449. continue;
  450. }
  451. if(!chkMetaBlock(&mb)){
  452. error("bad meta block: %s[%ud]: %R", name, o);
  453. blockPut(b);
  454. continue;
  455. }
  456. s = vtStrDup("");
  457. for(i=0; i<mb.nindex; i++){
  458. meUnpack(&me, &mb, i);
  459. if(!deUnpack(&de, &me)){
  460. error("cound not unpack dir entry: %s[%ud][%d]: %R", name, o, i);
  461. continue;
  462. }
  463. if(strcmp(s, de.elem) >= 0)
  464. error("dir entry out of order: %s[%ud][%d] = %s last = %s", name, o, i,
  465. de.elem, s);
  466. vtMemFree(s);
  467. s = vtStrDup(de.elem);
  468. nn = smprint("%s/%s", name, de.elem);
  469. if(!(de.mode & ModeDir)){
  470. r = openSource(source, nn, bm, de.entry, de.gen, 0);
  471. if(r != nil)
  472. sourceClose(r);
  473. deCleanup(&de);
  474. free(nn);
  475. continue;
  476. }
  477. r = openSource(source, nn, bm, de.entry, de.gen, 1);
  478. if(r == nil){
  479. deCleanup(&de);
  480. free(nn);
  481. continue;
  482. }
  483. mr = openSource(source, nn, bm, de.mentry, de.mgen, 0);
  484. if(mr == nil){
  485. sourceClose(r);
  486. deCleanup(&de);
  487. free(nn);
  488. continue;
  489. }
  490. chkDir(nn, r, mr);
  491. sourceClose(mr);
  492. sourceClose(r);
  493. deCleanup(&de);
  494. free(nn);
  495. deCleanup(&de);
  496. }
  497. vtMemFree(s);
  498. blockPut(b);
  499. }
  500. nb = sourceGetDirSize(source);
  501. for(o=0; o<nb; o++){
  502. if(getBit(bm, o))
  503. continue;
  504. r = sourceOpen(source, o, OReadOnly);
  505. if(r == nil)
  506. continue;
  507. warn("non referenced entry in source %s[%d]", name, o);
  508. if((bb = sourceBlock(source, o/(source->dsize/VtEntrySize), OReadOnly)) != nil){
  509. if(bb->addr != NilBlock)
  510. print("# clre %#ux %d\n", bb->addr, o%(source->dsize/VtEntrySize));
  511. blockPut(bb);
  512. }
  513. sourceClose(r);
  514. }
  515. sourceUnlock(source);
  516. sourceUnlock(meta);
  517. vtMemFree(bm);
  518. }
  519. static void
  520. setBit(uchar *bmap, ulong addr)
  521. {
  522. bmap[addr>>3] |= 1 << (addr & 7);
  523. }
  524. static int
  525. getBit(uchar *bmap, ulong addr)
  526. {
  527. return (bmap[addr>>3] >> (addr & 7)) & 1;
  528. }
  529. static int
  530. readLabel(Label *l, u32int addr)
  531. {
  532. int lpb;
  533. Block *b;
  534. u32int a;
  535. lpb = bsize / LabelSize;
  536. a = addr / lpb;
  537. b = cacheLocal(cache, PartLabel, a, OReadOnly);
  538. if(b == nil){
  539. blockPut(b);
  540. return 0;
  541. }
  542. if(!labelUnpack(l, b->data, addr%lpb)){
  543. print("labelUnpack %ux failed\n", addr);
  544. blockPut(b);
  545. return 0;
  546. }
  547. blockPut(b);
  548. return 1;
  549. }
  550. static void
  551. error(char *fmt, ...)
  552. {
  553. static nerr;
  554. va_list arg;
  555. char buf[128];
  556. va_start(arg, fmt);
  557. vseprint(buf, buf+sizeof(buf), fmt, arg);
  558. va_end(arg);
  559. print("error: %s\n", buf);
  560. // if(nerr++ > 20)
  561. // vtFatal("too many errors");
  562. }
  563. static void
  564. warn(char *fmt, ...)
  565. {
  566. static nerr;
  567. va_list arg;
  568. char buf[128];
  569. va_start(arg, fmt);
  570. vseprint(buf, buf+sizeof(buf), fmt, arg);
  571. va_end(arg);
  572. print("warn: %s\n", buf);
  573. }