check.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 }