From 16f27524d31768baadccbe0a7efd8daaed694da7 Mon Sep 17 00:00:00 2001 From: sageweil Date: Wed, 21 Nov 2007 21:15:22 +0000 Subject: [PATCH] more csum fun; need to clean up bh handling, i think git-svn-id: https://ceph.svn.sf.net/svnroot/ceph@2104 29311d96-e01e-0410-9327-a35deaab8ce9 --- branches/ebofs/ebofs/BufferCache.cc | 113 ++++++++++++++++++++- branches/ebofs/ebofs/BufferCache.h | 91 +---------------- branches/ebofs/ebofs/Ebofs.cc | 56 ++++++++--- branches/ebofs/ebofs/Onode.h | 147 +++++++++++++++++----------- branches/ebofs/ebofs/csum.h | 87 ++++++++++++++++ branches/ebofs/ebofs/test.ebofs.cc | 16 ++- branches/ebofs/ebofs/types.h | 3 +- 7 files changed, 345 insertions(+), 168 deletions(-) create mode 100644 branches/ebofs/ebofs/csum.h diff --git a/branches/ebofs/ebofs/BufferCache.cc b/branches/ebofs/ebofs/BufferCache.cc index c7b102c8f5925..10da7510fb46c 100644 --- a/branches/ebofs/ebofs/BufferCache.cc +++ b/branches/ebofs/ebofs/BufferCache.cc @@ -18,15 +18,120 @@ #include "Onode.h" +void do_apply_partial(bufferlist& bl, map& pm) +{ + assert(bl.length() == (unsigned)EBOFS_BLOCK_SIZE); + //assert(partial_is_complete()); + //cout << "apply_partial" << std::endl; + for (map::iterator i = pm.begin(); + i != pm.end(); + i++) { + int pos = i->first; + //cout << " frag at opos " << i->first << " bhpos " << pos << " len " << i->second.length() << std::endl; + bl.copy_in(pos, i->second.length(), i->second); + } + pm.clear(); +} + + + /*********** BufferHead **************/ #undef dout -#define dout(x) if (x <= g_conf.debug_ebofs) *_dout << dbeginl << g_clock.now() << " ebofs.bh." - +#define dout(x) if (x <= g_conf.debug_ebofs) *_dout << dbeginl << g_clock.now() << " ebofs." << *this << "." +void BufferHead::add_partial(off_t off, bufferlist& p) +{ + unsigned len = p.length(); + assert(len <= (unsigned)EBOFS_BLOCK_SIZE); + assert(off >= 0); + assert(off + len <= EBOFS_BLOCK_SIZE); + + csum_t csum_diff = calc_csum_realign(p.c_str(), p.length(), off); + + // trim any existing that overlaps + map::iterator i = partial.begin(); + while (i != partial.end()) { + // is [off,off+len)... + // past i? + if (off >= i->first + i->second.length()) { + i++; + continue; + } + // before i? + if (i->first >= off+len) break; + + // does [off,off+len)... + // overlap all of i? + if (off <= i->first && off+len >= i->first + i->second.length()) { + // erase it and move on. + csum_diff -= calc_csum_realign(i->second.c_str(), i->second.length(), i->first); + partial.erase(i++); + continue; + } + // overlap tail of i? + if (off > i->first && off+len >= i->first + i->second.length()) { + // shorten i. + unsigned taillen = off - i->first; + csum_diff -= calc_csum_realign(i->second.c_str()+taillen, taillen, off); + bufferlist o; + o.claim( i->second ); + i->second.substr_of(o, 0, taillen); + i++; + continue; + } + // overlap head of i? + if (off <= i->first && off+len < i->first + i->second.length()) { + // move i (make new tail). + off_t tailoff = off+len; + unsigned trim = tailoff - i->first; + csum_diff -= calc_csum_realign(i->second.c_str(), trim, i->first); + partial[tailoff].substr_of(i->second, trim, i->second.length()-trim); + partial.erase(i++); // should now be at tailoff + i++; + continue; + } + // split i? + if (off > i->first && off+len < i->first + i->second.length()) { + bufferlist o; + o.claim( i->second ); + // shorten head + unsigned headlen = off - i->first; + i->second.substr_of(o, 0, headlen); + // new tail + unsigned tailoff = off+len - i->first; + unsigned taillen = o.length() - len - headlen; + partial[off+len].substr_of(o, tailoff, taillen); + csum_diff -= calc_csum_realign(o.c_str()+headlen, taillen, off); + break; + } + assert(0); + } + + // insert and adjust csum + partial[off] = p; + csum_t *csum = oc->on->get_extent_csum_ptr(start()); + csum[0] += csum_diff; + oc->on->data_csum += csum_diff; + + dout(10) << "add_partial off " << off << "~" << p.length() + << " csum_diff " << hex << csum_diff << " now " + << csum[0] << dec << dendl; +} +void BufferHead::apply_partial() +{ + do_apply_partial(data, partial); + csum_t new_csum = calc_csum(data.c_str(), EBOFS_BLOCK_SIZE); + csum_t *oldp = oc->on->get_extent_csum_ptr(start()); + if (new_csum != *oldp) { + dout(10) << "apply_partial old_csum " << hex << *oldp << " calced_csum " << new_csum << dec << dendl; + assert(*oldp == new_csum); + } + partial.clear(); +} /************ ObjectCache **************/ @@ -433,9 +538,9 @@ int ObjectCache::map_read(block_t start, block_t len, * - break up bufferheads that don't fall completely within the range * - cancel rx ops we obsolete. * - resubmit rx ops if we split bufferheads + * - break over disk extent boundaries * * - leave potentially obsoleted tx ops alone (for now) - * - don't worry about disk extent boundaries (yet) */ int ObjectCache::map_write(block_t start, block_t len, map& hits, @@ -1091,7 +1196,7 @@ void BufferCache::rx_finish(ObjectCache *oc, bufferptr bp = buffer::create_page_aligned(EBOFS_BLOCK_SIZE); combined.push_back( bp ); combined.copy_in((pblock-diskstart)*EBOFS_BLOCK_SIZE, (pblock-diskstart+1)*EBOFS_BLOCK_SIZE, bl); - BufferHead::apply_partial( combined, p->second.partial ); + do_apply_partial( combined, p->second.partial ); // write it! dev.write( pblock, 1, combined, diff --git a/branches/ebofs/ebofs/BufferCache.h b/branches/ebofs/ebofs/BufferCache.h index dda4926ea1f09..24074b0ac1790 100644 --- a/branches/ebofs/ebofs/BufferCache.h +++ b/branches/ebofs/ebofs/BufferCache.h @@ -147,11 +147,6 @@ class BufferHead : public LRUObject { bool is_rx() { return state == STATE_RX; } bool is_partial() { return state == STATE_PARTIAL; } - //bool is_partial_writes() { return !partial_write.empty(); } - //void finish_partials(); - //void cancel_partials(); - //void queue_partial_write(block_t b); - void add_shadow(BufferHead *dup) { shadows.insert(dup); dup->shadow_of = this; @@ -240,92 +235,10 @@ class BufferHead : public LRUObject { bool partial_is_complete(off_t size) { return have_partial_range( 0, MIN(size, EBOFS_BLOCK_SIZE) ); - //(off_t)(start()*EBOFS_BLOCK_SIZE), - //MIN( size, (off_t)(end()*EBOFS_BLOCK_SIZE) ) ); - } - void apply_partial() { - apply_partial(data, partial); - partial.clear(); - } - static void apply_partial(bufferlist& bl, map& pm) { - assert(bl.length() == (unsigned)EBOFS_BLOCK_SIZE); - //assert(partial_is_complete()); - //cout << "apply_partial" << std::endl; - for (map::iterator i = pm.begin(); - i != pm.end(); - i++) { - int pos = i->first; - //cout << " frag at opos " << i->first << " bhpos " << pos << " len " << i->second.length() << std::endl; - bl.copy_in(pos, i->second.length(), i->second); - } - pm.clear(); } - void add_partial(off_t off, bufferlist& p) { - unsigned len = p.length(); - assert(len <= (unsigned)EBOFS_BLOCK_SIZE); - //assert(off >= (off_t)(start()*EBOFS_BLOCK_SIZE)); - //assert(off + len <= (off_t)(end()*EBOFS_BLOCK_SIZE)); - assert(off >= 0); - assert(off + len <= EBOFS_BLOCK_SIZE); - - // trim any existing that overlaps - map::iterator i = partial.begin(); - while (i != partial.end()) { - // is [off,off+len)... - // past i? - if (off >= i->first + i->second.length()) { - i++; - continue; - } - // before i? - if (i->first >= off+len) break; - - // does [off,off+len)... - // overlap all of i? - if (off <= i->first && off+len >= i->first + i->second.length()) { - // erase it and move on. - partial.erase(i++); - continue; - } - // overlap tail of i? - if (off > i->first && off+len >= i->first + i->second.length()) { - // shorten i. - bufferlist o; - o.claim( i->second ); - unsigned taillen = off - i->first; - i->second.substr_of(o, 0, taillen); - i++; - continue; - } - // overlap head of i? - if (off <= i->first && off+len < i->first + i->second.length()) { - // move i (make new tail). - off_t tailoff = off+len; - unsigned trim = tailoff - i->first; - partial[tailoff].substr_of(i->second, trim, i->second.length()-trim); - partial.erase(i++); // should now be at tailoff - i++; - continue; - } - // split i? - if (off > i->first && off+len < i->first + i->second.length()) { - bufferlist o; - o.claim( i->second ); - // shorten head - unsigned headlen = off - i->first; - i->second.substr_of(o, 0, headlen); - // new tail - unsigned tailoff = off+len - i->first; - unsigned taillen = o.length() - len - headlen; - partial[off+len].substr_of(o, tailoff, taillen); - break; - } - assert(0); - } - // insert - partial[off] = p; - } + void apply_partial(); + void add_partial(off_t off, bufferlist& p); }; diff --git a/branches/ebofs/ebofs/Ebofs.cc b/branches/ebofs/ebofs/Ebofs.cc index 87b830a1c793a..008a3e57f8430 100644 --- a/branches/ebofs/ebofs/Ebofs.cc +++ b/branches/ebofs/ebofs/Ebofs.cc @@ -777,8 +777,8 @@ Onode* Ebofs::get_onode(object_t oid) on->extent_map[n].ex = ex; if (ex.start) { on->extent_map[n].csum.resize(ex.length); - memcpy(&on->extent_map[n].csum[0], p, sizeof(__u64)*ex.length); - p += sizeof(__u64)*ex.length; + memcpy(&on->extent_map[n].csum[0], p, sizeof(csum_t)*ex.length); + p += sizeof(csum_t)*ex.length; } dout(15) << "get_onode " << *on << " ex " << i << ": " << ex << dendl; n += ex.length; @@ -854,8 +854,8 @@ void Ebofs::encode_onode(Onode *on, bufferlist& bl, unsigned& off) bl.copy_in(off, sizeof(Extent), (char*)&(o.ex)); off += sizeof(Extent); if (o.ex.start) { - bl.copy_in(off, sizeof(__u64)*o.ex.length, (char*)&o.csum[0]); - off += sizeof(__u64)*o.ex.length; + bl.copy_in(off, sizeof(csum_t)*o.ex.length, (char*)&o.csum[0]); + off += sizeof(csum_t)*o.ex.length; } dout(15) << "write_onode " << *on << " ex " << i->first << ": " << o.ex << dendl; } @@ -1461,7 +1461,7 @@ void Ebofs::alloc_write(Onode *on, // take note if first/last blocks in write range are remapped.. in case we need to do a partial read/write thing // these are for partial, so we don't care about TX bh's, so don't worry about bits canceling stuff below. - if (!old.empty()) { + if (!old.empty() && old[0].start) { // ..if not a hole.. if (i->first == start) { old_bfirst = old[0].start; dout(20) << "alloc_write old_bfirst " << old_bfirst << " of " << old[0] << dendl; @@ -1590,6 +1590,7 @@ void Ebofs::apply_write(Onode *on, off_t off, size_t len, const bufferlist& bl) block_t blast = (len+off-1) / EBOFS_BLOCK_SIZE; block_t blen = blast-bstart+1; + block_t oldlastblock = on->last_block; // allocate write on disk. interval_set alloc; @@ -1624,6 +1625,13 @@ void Ebofs::apply_write(Onode *on, off_t off, size_t len, const bufferlist& bl) bh->set_version(highv); bh->epoch_modified = super_epoch; + // newly allocated? + if (bh->start() >= oldlastblock) { + assert(bh->is_missing()); + bc.mark_clean(bh); // now a hole + dout(10) << "apply_write treating new (past old last_block) bh as a hole " << *bh << dendl; + } + // old write in progress? if (bh->is_tx()) { // copy the buffer to avoid munging up in-flight write dout(10) << "apply_write tx pending, copying buffer on " << *bh << dendl; @@ -1649,12 +1657,16 @@ void Ebofs::apply_write(Onode *on, off_t off, size_t len, const bufferlist& bl) } } + // locate ourselves in bh + unsigned off_in_bh = opos - bh->start()*EBOFS_BLOCK_SIZE; + assert(off_in_bh >= 0); + // partial at head or tail? - if ((bh->start() == bstart && opos % EBOFS_BLOCK_SIZE != 0) || // opos, not off, in case we're zeroing... - (bh->last() == blast && ((off_t)len+off) % EBOFS_BLOCK_SIZE != 0 && ((off_t)len+off) < on->object_size)) { - // locate ourselves in bh - unsigned off_in_bh = opos - bh->start()*EBOFS_BLOCK_SIZE; - assert(off_in_bh >= 0); + if ((bh->start() == bstart && + opos % EBOFS_BLOCK_SIZE != 0) || + (bh->last() == blast && + ((off_t)len+off) % EBOFS_BLOCK_SIZE != 0 && + ((off_t)len+off) < on->object_size)) { unsigned len_in_bh = MIN( (off_t)(left), (off_t)(bh->end()*EBOFS_BLOCK_SIZE)-opos ); @@ -1729,15 +1741,25 @@ void Ebofs::apply_write(Onode *on, off_t off, size_t len, const bufferlist& bl) temp.claim(bh->data); //bc.bufferpool.alloc(EBOFS_BLOCK_SIZE*bh->length(), bh->data); bh->data.push_back( buffer::create_page_aligned(EBOFS_BLOCK_SIZE*bh->length()) ); - if (temp.length()) + if (temp.length()) bh->data.copy_in(0, bh->length()*EBOFS_BLOCK_SIZE, temp); else bh->data.zero(); // was a hole + // new data bufferlist sub; sub.substr_of(bl, blpos, len_in_bh); bh->data.copy_in(off_in_bh, len_in_bh, sub); + // update csum + csum_t *csum = on->get_extent_csum_ptr(opos/EBOFS_BLOCK_SIZE); + unsigned blocks = (off_in_bh+len_in_bh+4095)/EBOFS_BLOCK_SIZE - off_in_bh/EBOFS_BLOCK_SIZE; + for (unsigned i=0; idata_csum -= csum[i]; + csum[i] = calc_csum(bh->data.c_str() + i*EBOFS_BLOCK_SIZE, EBOFS_BLOCK_SIZE); + on->data_csum += csum[i]; + } + blpos += len_in_bh; left -= len_in_bh; opos += len_in_bh; @@ -1751,6 +1773,8 @@ void Ebofs::apply_write(Onode *on, off_t off, size_t len, const bufferlist& bl) } // ok, we're talking full block(s) now (modulo last block of the object) + // starting at the front of the bh. + assert(off_in_bh == 0); assert(opos % EBOFS_BLOCK_SIZE == 0); assert((off_t)(left) >= (off_t)(EBOFS_BLOCK_SIZE*bh->length()) || opos+(off_t)(left) == on->object_size); @@ -1782,6 +1806,16 @@ void Ebofs::apply_write(Onode *on, off_t off, size_t len, const bufferlist& bl) bh->data.copy_in(0, len_in_bh, sub); } + // fill in csums + csum_t *csum = on->get_extent_csum_ptr(bh->start()); + unsigned blocks = len_in_bh / EBOFS_BLOCK_SIZE; + for (unsigned i=0; idata_csum -= csum[i]; + csum[i] = calc_csum(bh->data.c_str() + i*EBOFS_BLOCK_SIZE, EBOFS_BLOCK_SIZE); + on->data_csum += csum[i]; + } + on->verify_extents(); + blpos += len_in_bh; left -= len_in_bh; opos += len_in_bh; diff --git a/branches/ebofs/ebofs/Onode.h b/branches/ebofs/ebofs/Onode.h index dc136c8b01b23..7845ce3b997f2 100644 --- a/branches/ebofs/ebofs/Onode.h +++ b/branches/ebofs/ebofs/Onode.h @@ -36,7 +36,7 @@ struct ExtentCsum { Extent ex; - vector<__u64> csum; + vector csum; void resize_tail() { unsigned old = csum.size(); @@ -46,19 +46,19 @@ struct ExtentCsum { } void resize_head() { if (ex.length < csum.size()) { - memmove(&csum[0], &csum[csum.size()-ex.length], ex.length*sizeof(__u64)); + memmove(&csum[0], &csum[csum.size()-ex.length], ex.length*sizeof(csum_t)); csum.resize(ex.length); } else if (ex.length > csum.size()) { int old = csum.size(); csum.resize(ex.length); - memmove(&csum[ex.length-old], &csum[0], ex.length*sizeof(__u64)); + memmove(&csum[ex.length-old], &csum[0], ex.length*sizeof(csum_t)); for (block_t b = 0; b is; - __u64 csum = 0; + csum_t csum = 0; set s; - cout << "verifying" << std::endl; + cout << "verifying. data_csum=" << hex << data_csum << dec << std::endl; for (map::iterator p = extent_map.begin(); p != extent_map.end(); @@ -186,6 +186,7 @@ public: } } } + cout << " calculated csum=" << hex << csum << dec << std::endl; assert(s.size() == count); assert(count == alloc_blocks); @@ -193,6 +194,22 @@ public: assert(csum == data_csum); } } + + csum_t *get_extent_csum_ptr(block_t offset) { + map::iterator p = extent_map.lower_bound(offset); + if (p == extent_map.end() || p->first > offset) + p--; + assert(p->first <= offset); + assert(p->second.ex.start != 0); + assert(offset < p->first + p->second.ex.length); + return &p->second.csum[offset-p->first]; + } + + /* + * set_extent - adjust extent map. + * assume new extent will have csum of 0. + * factor clobbered extents out of csums. + */ void set_extent(block_t offset, Extent ex) { cout << "set_extent " << offset << " -> " << ex << " ... " << last_block << std::endl; @@ -254,11 +271,16 @@ public: right.ex.start += offset+ex.length - p->first; alloc_blocks += right.ex.length; right.resize_head(); + for (unsigned j=0; jfirst; + unsigned overlap = offset+ex.length - p->first; n.ex.length -= overlap; if (n.ex.start) { n.ex.start += overlap; alloc_blocks -= overlap; - n.resize_tail(); + for (unsigned j=0; j& extra) { + cout << " truncate to " << len << " .. last_block " << last_block << std::endl; + + verify_extents(); + + map::iterator p = extent_map.lower_bound(len); + if (p != extent_map.begin() && + (p == extent_map.end() || p->first > len && p->first)) { + p--; + ExtentCsum &o = p->second; + if (o.ex.length > len - p->first) { + int newlen = len - p->first; + if (o.ex.start) { + Extent ex; + ex.start = o.ex.start + newlen; + ex.length = o.ex.length - newlen; + cout << " truncating ex " << p->second.ex << " to " << newlen << ", releasing " << ex << std::endl; + for (unsigned i=newlen; i 0); + } + p++; + } + + while (p != extent_map.end()) { + assert(p->first >= len); + ExtentCsum &o = p->second; + if (o.ex.start) { + for (unsigned i=0; i::iterator n = p; + n++; + extent_map.erase(p); + p = n; + } + + last_block = len; + verify_extents(); + return 0; + } + /* map_extents(start, len, ls) * map teh given page range into extents (and csums) on disk. */ - int map_extents(block_t start, block_t len, vector& ls, vector<__u64> *csum) { + int map_extents(block_t start, block_t len, vector& ls, vector *csum) { //cout << "map_extents " << start << " " << len << std::endl; verify_extents(); - //assert(start+len <= object_blocks); - map::iterator p; // hack hack speed up common cases! @@ -383,51 +457,6 @@ public: return 0; } - int truncate_extents(block_t len, vector& extra) { - cout << " truncate to " << len << " .. last_block " << last_block << std::endl; - - verify_extents(); - - map::iterator p = extent_map.lower_bound(len); - if (p != extent_map.begin() && - (p == extent_map.end() || p->first > len && p->first)) { - p--; - ExtentCsum &o = p->second; - if (o.ex.length > len - p->first) { - int newlen = len - p->first; - if (o.ex.start) { - Extent ex; - ex.start = o.ex.start + newlen; - ex.length = o.ex.length - newlen; - cout << " truncating ex " << p->second.ex << " to " << newlen << ", releasing " << ex << std::endl; - o.ex.length = newlen; - extra.push_back(ex); - alloc_blocks -= ex.length; - o.resize_tail(); - } else - o.ex.length = newlen; - assert(o.ex.length > 0); - } - p++; - } - - while (p != extent_map.end()) { - assert(p->first >= len); - ExtentCsum &o = p->second; - if (o.ex.start) { - extra.push_back(o.ex); - alloc_blocks -= o.ex.length; - } - map::iterator n = p; - n++; - extent_map.erase(p); - p = n; - } - - last_block = len; - verify_extents(); - return 0; - } /* map_alloc_regions(start, len, map) @@ -473,7 +502,7 @@ public: return s; } int get_extent_bytes() { - return sizeof(Extent) * extent_map.size() + sizeof(__u64)*alloc_blocks; + return sizeof(Extent) * extent_map.size() + sizeof(csum_t)*alloc_blocks; } }; diff --git a/branches/ebofs/ebofs/csum.h b/branches/ebofs/ebofs/csum.h new file mode 100644 index 0000000000000..a1f48dfda22d0 --- /dev/null +++ b/branches/ebofs/ebofs/csum.h @@ -0,0 +1,87 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2004-2006 Sage Weil + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#ifndef __EBOFS_CSUM_H +#define __EBOFS_CSUM_H + +typedef __u64 csum_t; + +/* + * physically and logically aligned buffer. yay. + */ +inline __u64 calc_csum(char *start, int len) { + // must be 64-bit aligned + assert(((unsigned long)start & 7) == 0); + assert((len & 7) == 0); + + __u64 *p = (__u64*)start; + __u64 *end = (__u64*)(start + len); + __u64 csum = 0; + while (p < end) { + csum += *p; + p++; + } + return csum; +} + +/* + * arbitrarily aligned buffer. buffer alignment must match logical alignment. + */ +inline __u64 calc_csum_unaligned(char *start, int len) { + char *end = start + len; + __u64 csum = 0; + + // front + while (start < end && (unsigned long)start & 7) { + csum += (__u64)(*start) << (8*(8 - ((unsigned long)start & 7))); + start++; + } + if (start == end) + return csum; + + // middle, aligned + char *fastend = end - 7; + while (start < fastend) { + csum += *(__u64*)start; + start += sizeof(__u64); + } + + // tail + while (start < end) { + csum += (__u64)(*start) << (8*(8 - ((unsigned long)start & 7))); + start++; + } + return csum; +} + + +/* + * arbitrarily aligned buffer, with arbitrary logical alignment + */ +inline __u64 calc_csum_realign(char *start, int len, int off) { + char *end = start + len; + __u64 csum = 0; + + if (((unsigned long)start & 7) == (off & 7)) + return calc_csum_unaligned(start, len); // lucky us, start and off alignment matches. + + // do it the slow way. yucky! + while (start < end) { + csum += (__u64)(*start) << (8*(8 - (off & 7))); + start++; off++; + } + return csum; +} + +#endif diff --git a/branches/ebofs/ebofs/test.ebofs.cc b/branches/ebofs/ebofs/test.ebofs.cc index d5ca927683db1..8e49f6b9dd3e0 100644 --- a/branches/ebofs/ebofs/test.ebofs.cc +++ b/branches/ebofs/ebofs/test.ebofs.cc @@ -20,6 +20,13 @@ bool stop = false; +char fingerprint_byte_at(int pos, int seed) +{ + __u64 big = ((pos & ~7) / 133) ^ big; + return ((char*)&big)[pos & 7]; +} + + int nt = 0; class Tester : public Thread { Ebofs &fs; @@ -54,8 +61,11 @@ public: bl.copy(0, l, b); char *p = b; while (l--) { - assert(*p == 0 || - *p == (char)(off ^ oid.ino)); + char want = fingerprint_byte_at(off, oid.ino); + if (*p != 0 && *p != want) { + cout << t << " bad fingerprint at " << off << " got " << (int)*p << " want " << (int)want << std::endl; + assert(0); + } off++; p++; } @@ -67,7 +77,7 @@ public: { cout << t << " write " << hex << oid << dec << " at " << off << " len " << len << std::endl; for (int j=0;j