Merge remote-tracking branch 'ejt/master'
This commit is contained in:
		
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,19 +1,25 @@
 | 
			
		||||
*~
 | 
			
		||||
*.o
 | 
			
		||||
*.a
 | 
			
		||||
*.gmo
 | 
			
		||||
*_t
 | 
			
		||||
*.d
 | 
			
		||||
*.d.[0-9]*
 | 
			
		||||
test.data
 | 
			
		||||
cachegrind.*
 | 
			
		||||
\#*\#
 | 
			
		||||
 | 
			
		||||
thin_check
 | 
			
		||||
thin_dump
 | 
			
		||||
thin_restore
 | 
			
		||||
thin_repair
 | 
			
		||||
thin_rmap
 | 
			
		||||
thin_metadata_size
 | 
			
		||||
 | 
			
		||||
cache_check
 | 
			
		||||
cache_dump
 | 
			
		||||
cache_restore
 | 
			
		||||
cache_repair
 | 
			
		||||
 | 
			
		||||
*.metadata
 | 
			
		||||
bad-metadata
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								Gemfile.lock
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								Gemfile.lock
									
									
									
									
									
								
							@@ -8,22 +8,22 @@ GEM
 | 
			
		||||
    builder (3.2.2)
 | 
			
		||||
    childprocess (0.3.9)
 | 
			
		||||
      ffi (~> 1.0, >= 1.0.11)
 | 
			
		||||
    cucumber (1.3.5)
 | 
			
		||||
    cucumber (1.3.8)
 | 
			
		||||
      builder (>= 2.1.2)
 | 
			
		||||
      diff-lcs (>= 1.1.3)
 | 
			
		||||
      gherkin (~> 2.12.0)
 | 
			
		||||
      multi_json (~> 1.7.5)
 | 
			
		||||
      gherkin (~> 2.12.1)
 | 
			
		||||
      multi_json (>= 1.7.5, < 2.0)
 | 
			
		||||
      multi_test (>= 0.0.2)
 | 
			
		||||
    diff-lcs (1.2.4)
 | 
			
		||||
    ejt_command_line (0.0.2)
 | 
			
		||||
    ffi (1.9.0)
 | 
			
		||||
    gherkin (2.12.0)
 | 
			
		||||
    gherkin (2.12.1)
 | 
			
		||||
      multi_json (~> 1.3)
 | 
			
		||||
    multi_json (1.7.7)
 | 
			
		||||
    multi_json (1.8.0)
 | 
			
		||||
    multi_test (0.0.2)
 | 
			
		||||
    rspec-expectations (2.14.0)
 | 
			
		||||
    rspec-expectations (2.14.3)
 | 
			
		||||
      diff-lcs (>= 1.1.3, < 2.0)
 | 
			
		||||
    thinp_xml (0.0.6)
 | 
			
		||||
    thinp_xml (0.0.12)
 | 
			
		||||
      ejt_command_line (= 0.0.2)
 | 
			
		||||
 | 
			
		||||
PLATFORMS
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										78
									
								
								Makefile.in
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								Makefile.in
									
									
									
									
									
								
							@@ -20,6 +20,11 @@
 | 
			
		||||
 | 
			
		||||
V=@
 | 
			
		||||
PROGRAMS=\
 | 
			
		||||
	cache_check \
 | 
			
		||||
	cache_dump \
 | 
			
		||||
	cache_restore \
 | 
			
		||||
	cache_repair \
 | 
			
		||||
	\
 | 
			
		||||
	thin_check \
 | 
			
		||||
	thin_dump \
 | 
			
		||||
	thin_restore \
 | 
			
		||||
@@ -29,29 +34,35 @@ PROGRAMS=\
 | 
			
		||||
 | 
			
		||||
all: $(PROGRAMS)
 | 
			
		||||
 | 
			
		||||
PDATA_SOURCE=\
 | 
			
		||||
SOURCE=\
 | 
			
		||||
	base/base64.cc \
 | 
			
		||||
	base/error_state.cc \
 | 
			
		||||
	\
 | 
			
		||||
	caching/hint_array.cc \
 | 
			
		||||
	caching/superblock.cc \
 | 
			
		||||
	caching/mapping_array.cc \
 | 
			
		||||
	caching/metadata.cc \
 | 
			
		||||
	caching/metadata_dump.cc \
 | 
			
		||||
	caching/restore_emitter.cc \
 | 
			
		||||
	caching/xml_format.cc \
 | 
			
		||||
	\
 | 
			
		||||
	persistent-data/checksum.cc \
 | 
			
		||||
	persistent-data/endian_utils.cc \
 | 
			
		||||
	persistent-data/error_set.cc \
 | 
			
		||||
	persistent-data/file_utils.cc \
 | 
			
		||||
	persistent-data/hex_dump.cc \
 | 
			
		||||
	persistent-data/lock_tracker.cc \
 | 
			
		||||
	persistent-data/transaction_manager.cc \
 | 
			
		||||
	\
 | 
			
		||||
	persistent-data/data-structures/bitset.cc \
 | 
			
		||||
	persistent-data/data-structures/btree.cc \
 | 
			
		||||
	\
 | 
			
		||||
	persistent-data/space_map.cc \
 | 
			
		||||
	persistent-data/space-maps/disk.cc \
 | 
			
		||||
	persistent-data/space-maps/recursive.cc \
 | 
			
		||||
	persistent-data/space-maps/careful_alloc.cc
 | 
			
		||||
#PDATA_OBJECTS=$(subst .cc,.o,$(PDATA_SOURCE))
 | 
			
		||||
 | 
			
		||||
SOURCE=\
 | 
			
		||||
	$(PDATA_SOURCE) \
 | 
			
		||||
	\
 | 
			
		||||
	cache/metadata_disk_structures.cc \
 | 
			
		||||
	persistent-data/space-maps/careful_alloc.cc \
 | 
			
		||||
	\
 | 
			
		||||
	thin-provisioning/device_tree.cc \
 | 
			
		||||
	thin-provisioning/file_utils.cc \
 | 
			
		||||
	thin-provisioning/human_readable_format.cc \
 | 
			
		||||
	thin-provisioning/mapping_tree.cc \
 | 
			
		||||
	thin-provisioning/metadata.cc \
 | 
			
		||||
@@ -66,7 +77,8 @@ SOURCE=\
 | 
			
		||||
PDATA_OBJECTS=$(subst .cc,.o,$(SOURCE))
 | 
			
		||||
 | 
			
		||||
CXX_PROGRAM_SOURCE=\
 | 
			
		||||
	cache/check.cc \
 | 
			
		||||
	caching/cache_check.cc \
 | 
			
		||||
	caching/cache_restore.cc \
 | 
			
		||||
	\
 | 
			
		||||
	thin-provisioning/thin_check.cc \
 | 
			
		||||
	thin-provisioning/thin_dump.cc \
 | 
			
		||||
@@ -140,9 +152,11 @@ THIN_DUMP_SOURCE=$(SOURCE)
 | 
			
		||||
THIN_REPAIR_SOURCE=$(SOURCE)
 | 
			
		||||
THIN_RESTORE_SOURCE=$(SOURCE)
 | 
			
		||||
THIN_CHECK_SOURCE=\
 | 
			
		||||
	base/error_state.cc \
 | 
			
		||||
	persistent-data/checksum.cc \
 | 
			
		||||
	persistent-data/endian_utils.cc \
 | 
			
		||||
	persistent-data/error_set.cc \
 | 
			
		||||
	persistent-data/file_utils.cc \
 | 
			
		||||
	persistent-data/hex_dump.cc \
 | 
			
		||||
	persistent-data/lock_tracker.cc \
 | 
			
		||||
	persistent-data/data-structures/btree.cc \
 | 
			
		||||
@@ -151,7 +165,6 @@ THIN_CHECK_SOURCE=\
 | 
			
		||||
	persistent-data/space-maps/recursive.cc \
 | 
			
		||||
	persistent-data/space-maps/careful_alloc.cc \
 | 
			
		||||
	persistent-data/transaction_manager.cc \
 | 
			
		||||
	thin-provisioning/file_utils.cc \
 | 
			
		||||
	thin-provisioning/device_tree.cc \
 | 
			
		||||
	thin-provisioning/mapping_tree.cc \
 | 
			
		||||
	thin-provisioning/metadata.cc \
 | 
			
		||||
@@ -162,6 +175,7 @@ THIN_RMAP_SOURCE=\
 | 
			
		||||
	persistent-data/checksum.cc \
 | 
			
		||||
	persistent-data/endian_utils.cc \
 | 
			
		||||
	persistent-data/error_set.cc \
 | 
			
		||||
	persistent-data/file_utils.cc \
 | 
			
		||||
	persistent-data/hex_dump.cc \
 | 
			
		||||
	persistent-data/lock_tracker.cc \
 | 
			
		||||
	persistent-data/data-structures/btree.cc \
 | 
			
		||||
@@ -170,7 +184,6 @@ THIN_RMAP_SOURCE=\
 | 
			
		||||
	persistent-data/space-maps/recursive.cc \
 | 
			
		||||
	persistent-data/space-maps/careful_alloc.cc \
 | 
			
		||||
	persistent-data/transaction_manager.cc \
 | 
			
		||||
	thin-provisioning/file_utils.cc \
 | 
			
		||||
	thin-provisioning/device_tree.cc \
 | 
			
		||||
	thin-provisioning/mapping_tree.cc \
 | 
			
		||||
	thin-provisioning/metadata.cc \
 | 
			
		||||
@@ -211,30 +224,61 @@ thin_rmap: $(THIN_RMAP_OBJECTS) thin-provisioning/thin_rmap.o
 | 
			
		||||
 | 
			
		||||
thin_metadata_size: thin-provisioning/thin_metadata_size.o
 | 
			
		||||
	@echo "    [LD]  $@"
 | 
			
		||||
	$(V) $(CC) $(CFLAGS) -o $@ $+ -lm
 | 
			
		||||
	$(V) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $+ -lm
 | 
			
		||||
 | 
			
		||||
#----------------------------------------------------------------
 | 
			
		||||
# Cache tools
 | 
			
		||||
 | 
			
		||||
CACHE_CHECK_SOURCE=\
 | 
			
		||||
	base/base64.cc \
 | 
			
		||||
	base/error_state.cc \
 | 
			
		||||
	persistent-data/checksum.cc \
 | 
			
		||||
	persistent-data/endian_utils.cc \
 | 
			
		||||
	persistent-data/error_set.cc \
 | 
			
		||||
	persistent-data/file_utils.cc \
 | 
			
		||||
	persistent-data/hex_dump.cc \
 | 
			
		||||
	persistent-data/lock_tracker.cc \
 | 
			
		||||
	persistent-data/data-structures/btree.cc \
 | 
			
		||||
	persistent-data/data-structures/bitset.cc \
 | 
			
		||||
	persistent-data/space_map.cc \
 | 
			
		||||
	persistent-data/space-maps/disk.cc \
 | 
			
		||||
	persistent-data/space-maps/recursive.cc \
 | 
			
		||||
	persistent-data/space-maps/careful_alloc.cc \
 | 
			
		||||
	persistent-data/transaction_manager.cc \
 | 
			
		||||
	cache/metadata_disk_structures.cc
 | 
			
		||||
	caching/hint_array.cc \
 | 
			
		||||
	caching/superblock.cc \
 | 
			
		||||
	caching/mapping_array.cc \
 | 
			
		||||
	caching/metadata.cc \
 | 
			
		||||
	caching/metadata_dump.cc \
 | 
			
		||||
	caching/restore_emitter.cc \
 | 
			
		||||
	caching/xml_format.cc
 | 
			
		||||
 | 
			
		||||
CACHE_CHECK_OBJECTS=$(subst .cc,.o,$(CACHE_CHECK_SOURCE))
 | 
			
		||||
 | 
			
		||||
cache_check: $(CACHE_CHECK_OBJECTS) cache/check.o
 | 
			
		||||
CACHE_DUMP_SOURCE=$(SOURCE)
 | 
			
		||||
CACHE_DUMP_OBJECTS=$(subst .cc,.o,$(CACHE_DUMP_SOURCE))
 | 
			
		||||
 | 
			
		||||
CACHE_REPAIR_SOURCE=$(SOURCE)
 | 
			
		||||
CACHE_REPAIR_OBJECTS=$(subst .cc,.o,$(CACHE_REPAIR_SOURCE))
 | 
			
		||||
 | 
			
		||||
CACHE_RESTORE_SOURCE=$(SOURCE)
 | 
			
		||||
CACHE_RESTORE_OBJECTS=$(subst .cc,.o,$(CACHE_RESTORE_SOURCE))
 | 
			
		||||
 | 
			
		||||
cache_check: $(CACHE_CHECK_OBJECTS) caching/cache_check.o
 | 
			
		||||
	@echo "    [LD]  $@"
 | 
			
		||||
	$(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS)
 | 
			
		||||
	$(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT)
 | 
			
		||||
 | 
			
		||||
cache_dump: $(CACHE_DUMP_OBJECTS) caching/cache_dump.o
 | 
			
		||||
	@echo "    [LD]  $@"
 | 
			
		||||
	$(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT)
 | 
			
		||||
 | 
			
		||||
cache_repair: $(CACHE_REPAIR_OBJECTS) caching/cache_repair.o
 | 
			
		||||
	@echo "    [LD]  $@"
 | 
			
		||||
	$(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT)
 | 
			
		||||
 | 
			
		||||
cache_restore: $(CACHE_RESTORE_OBJECTS) caching/cache_restore.o
 | 
			
		||||
	@echo "    [LD]  $@"
 | 
			
		||||
	$(V) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT)
 | 
			
		||||
 | 
			
		||||
DEPEND_FILES=\
 | 
			
		||||
	$(subst .cc,.d,$(SOURCE)) \
 | 
			
		||||
@@ -276,7 +320,7 @@ include unit-tests/Makefile
 | 
			
		||||
 | 
			
		||||
.PHONEY: features
 | 
			
		||||
 | 
			
		||||
features: thin_check cache_check
 | 
			
		||||
features: $(PROGRAMS)
 | 
			
		||||
	cucumber --no-color --format progress
 | 
			
		||||
 | 
			
		||||
test: features unit-test
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										186
									
								
								base/base64.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								base/base64.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,186 @@
 | 
			
		||||
#include "base/base64.h"
 | 
			
		||||
 | 
			
		||||
#include <boost/optional.hpp>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
 | 
			
		||||
using namespace base;
 | 
			
		||||
using namespace boost;
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	char const *table_ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 | 
			
		||||
 | 
			
		||||
	struct index_set {
 | 
			
		||||
		unsigned nr_valid_;
 | 
			
		||||
		unsigned index_[4];
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	index_set split1(unsigned char c) {
 | 
			
		||||
		index_set r;
 | 
			
		||||
 | 
			
		||||
		r.nr_valid_ = 2;
 | 
			
		||||
		r.index_[0] = c >> 2;
 | 
			
		||||
		r.index_[1] = (c & 3) << 4;
 | 
			
		||||
 | 
			
		||||
		return r;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	index_set split2(unsigned char c1, unsigned char c2) {
 | 
			
		||||
		index_set r;
 | 
			
		||||
 | 
			
		||||
		r.nr_valid_ = 3;
 | 
			
		||||
		r.index_[0] = c1 >> 2;
 | 
			
		||||
		r.index_[1] = ((c1 & 3) << 4) | (c2 >> 4);
 | 
			
		||||
		r.index_[2] = (c2 & 15) << 2;
 | 
			
		||||
 | 
			
		||||
		return r;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	index_set split3(unsigned char c1, unsigned char c2, unsigned c3) {
 | 
			
		||||
		index_set r;
 | 
			
		||||
 | 
			
		||||
		r.nr_valid_ = 4;
 | 
			
		||||
		r.index_[0] = c1 >> 2;
 | 
			
		||||
		r.index_[1] = ((c1 & 3) << 4) | (c2 >> 4);
 | 
			
		||||
		r.index_[2] = ((c2 & 15) << 2) | (c3 >> 6);
 | 
			
		||||
		r.index_[3] = c3 & 63;
 | 
			
		||||
 | 
			
		||||
		return r;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	index_set split(vector<unsigned char> const &raw, unsigned index) {
 | 
			
		||||
		unsigned remaining = std::min<unsigned>(raw.size() - index, 3);
 | 
			
		||||
 | 
			
		||||
		switch (remaining) {
 | 
			
		||||
		case 1:
 | 
			
		||||
			return split1(raw.at(index));
 | 
			
		||||
 | 
			
		||||
		case 2:
 | 
			
		||||
			return split2(raw.at(index), raw.at(index + 1));
 | 
			
		||||
 | 
			
		||||
		case 3:
 | 
			
		||||
			return split3(raw.at(index), raw.at(index + 1), raw.at(index + 2));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		throw std::runtime_error("internal error, in split");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	optional<unsigned> char_to_index(char c) {
 | 
			
		||||
		// FIXME: very slow
 | 
			
		||||
		for (unsigned i = 0; i < 64; i++)
 | 
			
		||||
			if (table_[i] == c)
 | 
			
		||||
				return optional<unsigned>(i);
 | 
			
		||||
 | 
			
		||||
		return optional<unsigned>();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	decoded_or_error success(vector<unsigned char> const &decoded) {
 | 
			
		||||
		return decoded_or_error(decoded);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	decoded_or_error fail(string msg) {
 | 
			
		||||
		return decoded_or_error(msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	decoded_or_error fail_char(char c) {
 | 
			
		||||
		ostringstream msg;
 | 
			
		||||
		msg << "bad input character: '" << c << "'";
 | 
			
		||||
		return fail(msg.str());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	decoded_or_error decode_quad(char c1, char c2, char c3, char c4) {
 | 
			
		||||
		typedef optional<unsigned> oi;
 | 
			
		||||
		unsigned char d1, d2, d3;
 | 
			
		||||
		vector<unsigned char> decoded;
 | 
			
		||||
 | 
			
		||||
		oi i1 = char_to_index(c1);
 | 
			
		||||
		if (!i1)
 | 
			
		||||
			return fail_char(c1);
 | 
			
		||||
 | 
			
		||||
		oi i2 = char_to_index(c2);
 | 
			
		||||
		if (!i2)
 | 
			
		||||
			return fail_char(c2);
 | 
			
		||||
 | 
			
		||||
		d1 = (*i1 << 2) | (*i2 >> 4);
 | 
			
		||||
		decoded.push_back(d1);
 | 
			
		||||
 | 
			
		||||
		d2 = (*i2 & 15) << 4;
 | 
			
		||||
 | 
			
		||||
		if (c3 == '=') {
 | 
			
		||||
			// FIXME: I really think the push should be here
 | 
			
		||||
//			decoded.push_back(d2);
 | 
			
		||||
			return success(decoded);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		oi i3 = char_to_index(c3);
 | 
			
		||||
		if (!i3)
 | 
			
		||||
			return fail_char(c3);
 | 
			
		||||
 | 
			
		||||
		d2 = d2 | (*i3 >> 2);
 | 
			
		||||
		decoded.push_back(d2);
 | 
			
		||||
 | 
			
		||||
		d3 = (*i3 & 3) << 6;
 | 
			
		||||
 | 
			
		||||
		if (c4 == '=') {
 | 
			
		||||
			// FIXME: I really think the push should be here
 | 
			
		||||
//			decoded.push_back(d3);
 | 
			
		||||
			return success(decoded);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		oi i4 = char_to_index(c4);
 | 
			
		||||
		if (!i4)
 | 
			
		||||
			return fail_char(c4);
 | 
			
		||||
 | 
			
		||||
		d3 = d3 | *i4;
 | 
			
		||||
		decoded.push_back(d3);
 | 
			
		||||
 | 
			
		||||
		return success(decoded);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
string
 | 
			
		||||
base::base64_encode(vector<unsigned char> const &raw)
 | 
			
		||||
{
 | 
			
		||||
	string r;
 | 
			
		||||
 | 
			
		||||
	for (unsigned i = 0; i < raw.size(); i += 3) {
 | 
			
		||||
		unsigned j;
 | 
			
		||||
		index_set is = split(raw, i);
 | 
			
		||||
 | 
			
		||||
		for (j = 0; j < is.nr_valid_; j++)
 | 
			
		||||
			r.push_back(table_[is.index_[j]]);
 | 
			
		||||
 | 
			
		||||
		for (; j < 4; j++)
 | 
			
		||||
			r.push_back('=');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
base::decoded_or_error
 | 
			
		||||
base::base64_decode(string const &encoded)
 | 
			
		||||
{
 | 
			
		||||
	if (encoded.length() % 4)
 | 
			
		||||
		return decoded_or_error("bad input length");
 | 
			
		||||
 | 
			
		||||
	vector<unsigned char> decoded;
 | 
			
		||||
 | 
			
		||||
	for (unsigned i = 0; i < encoded.length(); i += 4) {
 | 
			
		||||
		decoded_or_error doe = decode_quad(encoded[i], encoded[i + 1], encoded[i + 2], encoded[i + 3]);
 | 
			
		||||
 | 
			
		||||
		vector<unsigned char> *v = get<vector<unsigned char> >(&doe);
 | 
			
		||||
		if (!v)
 | 
			
		||||
			return doe;
 | 
			
		||||
 | 
			
		||||
		decoded.insert(decoded.end(), v->begin(), v->end());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return decoded_or_error(decoded);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
							
								
								
									
										20
									
								
								base/base64.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								base/base64.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
#ifndef BASE_BASE64_H
 | 
			
		||||
#define BASE_BASE64_H
 | 
			
		||||
 | 
			
		||||
#include <boost/variant.hpp>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace base {
 | 
			
		||||
	std::string base64_encode(std::vector<unsigned char> const &raw);
 | 
			
		||||
 | 
			
		||||
	// Returns either the decoded data or an error string
 | 
			
		||||
	typedef boost::variant<std::vector<unsigned char>, std::string> decoded_or_error;
 | 
			
		||||
	decoded_or_error base64_decode(std::string const &encoded);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										19
									
								
								base/error_state.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								base/error_state.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
#include "base/error_state.h"
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
base::error_state
 | 
			
		||||
base::combine_errors(error_state lhs, error_state rhs) {
 | 
			
		||||
	switch (lhs) {
 | 
			
		||||
	case NO_ERROR:
 | 
			
		||||
		return rhs;
 | 
			
		||||
 | 
			
		||||
	case NON_FATAL:
 | 
			
		||||
		return (rhs == FATAL) ? FATAL : lhs;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return lhs;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
							
								
								
									
										18
									
								
								base/error_state.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								base/error_state.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
#ifndef BASE_ERROR_STATE_H
 | 
			
		||||
#define BASE_ERROR_STATE_H
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace base {
 | 
			
		||||
	enum error_state {
 | 
			
		||||
		NO_ERROR,
 | 
			
		||||
		NON_FATAL,	// eg, lost blocks
 | 
			
		||||
		FATAL		// needs fixing before pool can be activated
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	error_state combine_errors(error_state lhs, error_state rhs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										94
									
								
								base/nested_output.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								base/nested_output.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
#ifndef BASE_NESTED_OUTPUT_H
 | 
			
		||||
#define BASE_NESTED_OUTPUT_H
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace base {
 | 
			
		||||
	class end_message {};
 | 
			
		||||
 | 
			
		||||
	class nested_output {
 | 
			
		||||
	public:
 | 
			
		||||
		nested_output(std::ostream &out, unsigned step)
 | 
			
		||||
			: out_(out),
 | 
			
		||||
			  step_(step),
 | 
			
		||||
			  beginning_of_line_(true),
 | 
			
		||||
			  enabled_(true),
 | 
			
		||||
			  indent_(0) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		template <typename T>
 | 
			
		||||
		nested_output &operator <<(T const &t) {
 | 
			
		||||
			if (beginning_of_line_) {
 | 
			
		||||
				beginning_of_line_ = false;
 | 
			
		||||
				indent();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (enabled_)
 | 
			
		||||
				out_ << t;
 | 
			
		||||
 | 
			
		||||
			return *this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		nested_output &operator <<(end_message const &m) {
 | 
			
		||||
			beginning_of_line_ = true;
 | 
			
		||||
 | 
			
		||||
			if (enabled_)
 | 
			
		||||
				out_ << std::endl;
 | 
			
		||||
 | 
			
		||||
			return *this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void inc_indent() {
 | 
			
		||||
			indent_ += step_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void dec_indent() {
 | 
			
		||||
			indent_ -= step_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		struct nest {
 | 
			
		||||
			nest(nested_output &out)
 | 
			
		||||
			: out_(out) {
 | 
			
		||||
				out_.inc_indent();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			~nest() {
 | 
			
		||||
				out_.dec_indent();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			nested_output &out_;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		nest push() {
 | 
			
		||||
			return nest(*this);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void enable() {
 | 
			
		||||
			enabled_ = true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void disable() {
 | 
			
		||||
			enabled_ = false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		void indent() {
 | 
			
		||||
			if (enabled_)
 | 
			
		||||
				for (unsigned i = 0; i < indent_; i++)
 | 
			
		||||
					out_ << ' ';
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::ostream &out_;
 | 
			
		||||
		unsigned step_;
 | 
			
		||||
 | 
			
		||||
		bool beginning_of_line_;
 | 
			
		||||
		bool enabled_;
 | 
			
		||||
		unsigned indent_;
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										166
									
								
								cache/check.cc
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										166
									
								
								cache/check.cc
									
									
									
									
										vendored
									
									
								
							@@ -1,166 +0,0 @@
 | 
			
		||||
// Copyright (C) 2011 Red Hat, Inc. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// This file is part of the thin-provisioning-tools source.
 | 
			
		||||
//
 | 
			
		||||
// thin-provisioning-tools is free software: you can redistribute it
 | 
			
		||||
// and/or modify it under the terms of the GNU General Public License
 | 
			
		||||
// as published by the Free Software Foundation, either version 3 of
 | 
			
		||||
// the License, or (at your option) any later version.
 | 
			
		||||
//
 | 
			
		||||
// thin-provisioning-tools is distributed in the hope that it will be
 | 
			
		||||
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 | 
			
		||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License along
 | 
			
		||||
// with thin-provisioning-tools.  If not, see
 | 
			
		||||
// <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	struct stat guarded_stat(string const &path) {
 | 
			
		||||
		struct stat info;
 | 
			
		||||
 | 
			
		||||
		int r = ::stat(path.c_str(), &info);
 | 
			
		||||
		if (r) {
 | 
			
		||||
			ostringstream msg;
 | 
			
		||||
			char buffer[128], *ptr;
 | 
			
		||||
 | 
			
		||||
			ptr = ::strerror_r(errno, buffer, sizeof(buffer));
 | 
			
		||||
			msg << path << ": " << ptr;
 | 
			
		||||
			throw runtime_error(msg.str());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return info;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int open_file(string const &path, int mode) {
 | 
			
		||||
		int fd = open(path.c_str(), mode);
 | 
			
		||||
		if (fd < 0) {
 | 
			
		||||
			ostringstream msg;
 | 
			
		||||
			char buffer[128], *ptr;
 | 
			
		||||
 | 
			
		||||
			ptr = strerror_r(errno, buffer, sizeof(buffer));
 | 
			
		||||
			msg << path << ": " << ptr;
 | 
			
		||||
			throw runtime_error(msg.str());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return fd;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int check(string const &path, bool quiet) {
 | 
			
		||||
		struct stat info = guarded_stat(path);
 | 
			
		||||
 | 
			
		||||
		if (!S_ISREG(info.st_mode) && !S_ISBLK(info.st_mode)) {
 | 
			
		||||
			ostringstream msg;
 | 
			
		||||
			msg << path << ": " << "Not a block device or regular file";
 | 
			
		||||
			throw runtime_error(msg.str());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		int fd = open_file(path, O_RDONLY);
 | 
			
		||||
 | 
			
		||||
		ostringstream msg;
 | 
			
		||||
		msg << path << ": " << "No superblock found";
 | 
			
		||||
		throw runtime_error(msg.str());
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
		try {
 | 
			
		||||
			metadata::ptr md(new metadata(path, metadata::OPEN));
 | 
			
		||||
 | 
			
		||||
			optional<error_set::ptr> maybe_errors = metadata_check(md);
 | 
			
		||||
			if (maybe_errors) {
 | 
			
		||||
				if (!quiet)
 | 
			
		||||
					cerr << error_selector(*maybe_errors, 3);
 | 
			
		||||
				return 1;
 | 
			
		||||
			}
 | 
			
		||||
		} catch (std::exception &e) {
 | 
			
		||||
			if (!quiet)
 | 
			
		||||
				cerr << e.what() << endl;
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
#endif
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void usage(ostream &out, string const &cmd) {
 | 
			
		||||
		out << "Usage: " << cmd << " [options] {device|file}" << endl
 | 
			
		||||
		    << "Options:" << endl
 | 
			
		||||
		    << "  {-q|--quiet}" << endl
 | 
			
		||||
		    << "  {-h|--help}" << endl
 | 
			
		||||
		    << "  {-V|--version}" << endl;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	char const *TOOLS_VERSION = "0.1.6";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int c;
 | 
			
		||||
	bool quiet = false;
 | 
			
		||||
	const char shortopts[] = "qhV";
 | 
			
		||||
	const struct option longopts[] = {
 | 
			
		||||
		{ "quiet", no_argument, NULL, 'q'},
 | 
			
		||||
		{ "help", no_argument, NULL, 'h'},
 | 
			
		||||
		{ "version", no_argument, NULL, 'V'},
 | 
			
		||||
		{ NULL, no_argument, NULL, 0 }
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
 | 
			
		||||
		switch(c) {
 | 
			
		||||
		case 'h':
 | 
			
		||||
			usage(cout, basename(argv[0]));
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		case 'q':
 | 
			
		||||
			quiet = true;
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case 'V':
 | 
			
		||||
			cout << TOOLS_VERSION << endl;
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			usage(cerr, basename(argv[0]));
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (argc == optind) {
 | 
			
		||||
		cerr << "No input file provided." << endl;
 | 
			
		||||
		usage(cerr, basename(argv[0]));
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	try {
 | 
			
		||||
		check(argv[optind], quiet);
 | 
			
		||||
 | 
			
		||||
	} catch (exception const &e) {
 | 
			
		||||
		cerr << e.what() << endl;
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
							
								
								
									
										113
									
								
								cache/metadata_disk_structures.cc
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										113
									
								
								cache/metadata_disk_structures.cc
									
									
									
									
										vendored
									
									
								
							@@ -1,113 +0,0 @@
 | 
			
		||||
// Copyright (C) 2012 Red Hat, Inc. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// This file is part of the thin-provisioning-tools source.
 | 
			
		||||
//
 | 
			
		||||
// thin-provisioning-tools is free software: you can redistribute it
 | 
			
		||||
// and/or modify it under the terms of the GNU General Public License
 | 
			
		||||
// as published by the Free Software Foundation, either version 3 of
 | 
			
		||||
// the License, or (at your option) any later version.
 | 
			
		||||
//
 | 
			
		||||
// thin-provisioning-tools is distributed in the hope that it will be
 | 
			
		||||
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 | 
			
		||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License along
 | 
			
		||||
// with thin-provisioning-tools.  If not, see
 | 
			
		||||
// <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
#include "metadata_disk_structures.h"
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
using namespace cache_tools;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
superblock_traits::unpack(superblock_disk const &disk, superblock &core)
 | 
			
		||||
{
 | 
			
		||||
	core.csum = to_cpu<uint32_t>(disk.csum);
 | 
			
		||||
	core.flags = to_cpu<uint32_t>(disk.flags);
 | 
			
		||||
	core.blocknr = to_cpu<uint64_t>(disk.blocknr);
 | 
			
		||||
 | 
			
		||||
	::memcpy(core.uuid, disk.uuid, sizeof(core.uuid));
 | 
			
		||||
	core.magic = to_cpu<uint64_t>(disk.magic);
 | 
			
		||||
	core.version = to_cpu<uint32_t>(disk.version);
 | 
			
		||||
 | 
			
		||||
	::memcpy(core.policy_name, disk.policy_name, sizeof(core.policy_name));
 | 
			
		||||
 | 
			
		||||
	for (unsigned i = 0; i < CACHE_POLICY_VERSION_SIZE; i++)
 | 
			
		||||
		core.policy_version[i] = to_cpu<uint32_t>(disk.policy_version[i]);
 | 
			
		||||
 | 
			
		||||
	core.policy_hint_size = to_cpu<uint32_t>(disk.policy_hint_size);
 | 
			
		||||
 | 
			
		||||
	::memcpy(core.metadata_space_map_root,
 | 
			
		||||
		 disk.metadata_space_map_root,
 | 
			
		||||
		 sizeof(core.metadata_space_map_root));
 | 
			
		||||
 | 
			
		||||
	core.mapping_root = to_cpu<uint64_t>(disk.mapping_root);
 | 
			
		||||
	core.hint_root = to_cpu<uint64_t>(disk.hint_root);
 | 
			
		||||
 | 
			
		||||
	core.discard_root = to_cpu<uint64_t>(disk.discard_root);
 | 
			
		||||
	core.discard_block_size = to_cpu<uint64_t>(disk.discard_block_size);
 | 
			
		||||
	core.discard_nr_blocks = to_cpu<uint64_t>(disk.discard_nr_blocks);
 | 
			
		||||
 | 
			
		||||
	core.data_block_size = to_cpu<uint32_t>(disk.data_block_size);
 | 
			
		||||
	core.metadata_block_size = to_cpu<uint32_t>(disk.metadata_block_size);
 | 
			
		||||
	core.cache_blocks = to_cpu<uint32_t>(disk.cache_blocks);
 | 
			
		||||
 | 
			
		||||
	core.compat_flags = to_cpu<uint32_t>(disk.compat_flags);
 | 
			
		||||
	core.compat_ro_flags = to_cpu<uint32_t>(disk.compat_ro_flags);
 | 
			
		||||
	core.incompat_flags = to_cpu<uint32_t>(disk.incompat_flags);
 | 
			
		||||
 | 
			
		||||
	core.read_hits = to_cpu<uint32_t>(disk.read_hits);
 | 
			
		||||
	core.read_misses = to_cpu<uint32_t>(disk.read_misses);
 | 
			
		||||
	core.write_hits = to_cpu<uint32_t>(disk.write_hits);
 | 
			
		||||
	core.write_misses = to_cpu<uint32_t>(disk.write_misses);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
superblock_traits::pack(superblock const &core, superblock_disk &disk)
 | 
			
		||||
{
 | 
			
		||||
	disk.csum = to_disk<le32>(core.csum);
 | 
			
		||||
	disk.flags = to_disk<le32>(core.flags);
 | 
			
		||||
	disk.blocknr = to_disk<le64>(core.blocknr);
 | 
			
		||||
 | 
			
		||||
	::memcpy(disk.uuid, core.uuid, sizeof(disk.uuid));
 | 
			
		||||
	disk.magic = to_disk<le64>(core.magic);
 | 
			
		||||
	disk.version = to_disk<le32>(core.version);
 | 
			
		||||
 | 
			
		||||
	::memcpy(disk.policy_name, core.policy_name, sizeof(disk.policy_name));
 | 
			
		||||
 | 
			
		||||
	for (unsigned i = 0; i < CACHE_POLICY_VERSION_SIZE; i++)
 | 
			
		||||
		disk.policy_version[i] = to_disk<le32>(core.policy_version[i]);
 | 
			
		||||
 | 
			
		||||
	disk.policy_hint_size = to_disk<le32>(core.policy_hint_size);
 | 
			
		||||
 | 
			
		||||
	::memcpy(disk.metadata_space_map_root,
 | 
			
		||||
		 core.metadata_space_map_root,
 | 
			
		||||
		 sizeof(disk.metadata_space_map_root));
 | 
			
		||||
 | 
			
		||||
	disk.mapping_root = to_disk<le64>(core.mapping_root);
 | 
			
		||||
	disk.hint_root = to_disk<le64>(core.hint_root);
 | 
			
		||||
 | 
			
		||||
	disk.discard_root = to_disk<le64>(core.discard_root);
 | 
			
		||||
	disk.discard_block_size = to_disk<le64>(core.discard_block_size);
 | 
			
		||||
	disk.discard_nr_blocks = to_disk<le64>(core.discard_nr_blocks);
 | 
			
		||||
 | 
			
		||||
	disk.data_block_size = to_disk<le32>(core.data_block_size);
 | 
			
		||||
	disk.metadata_block_size = to_disk<le32>(core.metadata_block_size);
 | 
			
		||||
	disk.cache_blocks = to_disk<le32>(core.cache_blocks);
 | 
			
		||||
 | 
			
		||||
	disk.compat_flags = to_disk<le32>(core.compat_flags);
 | 
			
		||||
	disk.compat_ro_flags = to_disk<le32>(core.compat_ro_flags);
 | 
			
		||||
	disk.incompat_flags = to_disk<le32>(core.incompat_flags);
 | 
			
		||||
 | 
			
		||||
	disk.read_hits = to_disk<le32>(core.read_hits);
 | 
			
		||||
	disk.read_misses = to_disk<le32>(core.read_misses);
 | 
			
		||||
	disk.write_hits = to_disk<le32>(core.write_hits);
 | 
			
		||||
	disk.write_misses = to_disk<le32>(core.write_misses);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
							
								
								
									
										120
									
								
								cache/metadata_disk_structures.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										120
									
								
								cache/metadata_disk_structures.h
									
									
									
									
										vendored
									
									
								
							@@ -1,120 +0,0 @@
 | 
			
		||||
// Copyright (C) 2012 Red Hat, Inc. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// This file is part of the thin-provisioning-tools source.
 | 
			
		||||
//
 | 
			
		||||
// thin-provisioning-tools is free software: you can redistribute it
 | 
			
		||||
// and/or modify it under the terms of the GNU General Public License
 | 
			
		||||
// as published by the Free Software Foundation, either version 3 of
 | 
			
		||||
// the License, or (at your option) any later version.
 | 
			
		||||
//
 | 
			
		||||
// thin-provisioning-tools is distributed in the hope that it will be
 | 
			
		||||
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 | 
			
		||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License along
 | 
			
		||||
// with thin-provisioning-tools.  If not, see
 | 
			
		||||
// <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
#ifndef CACHE_METADATA_DISK_STRUCTURES_H
 | 
			
		||||
#define CACHE_METADATA_DISK_STRUCTURES_H
 | 
			
		||||
 | 
			
		||||
#include "persistent-data/endian_utils.h"
 | 
			
		||||
#include "persistent-data/data-structures/btree.h"
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// FIXME: rename to just METADATA_DISK_STRUCTURES
 | 
			
		||||
namespace cache_tools {
 | 
			
		||||
	using namespace base;	// FIXME: don't use namespaces in headers.
 | 
			
		||||
 | 
			
		||||
	unsigned const SPACE_MAP_ROOT_SIZE = 128;
 | 
			
		||||
	unsigned const CACHE_POLICY_NAME_SIZE = 16;
 | 
			
		||||
	unsigned const CACHE_POLICY_VERSION_SIZE = 3;
 | 
			
		||||
 | 
			
		||||
	typedef unsigned char __u8;
 | 
			
		||||
 | 
			
		||||
	struct superblock_disk {
 | 
			
		||||
		le32 csum;
 | 
			
		||||
		le32 flags;
 | 
			
		||||
		le64 blocknr;
 | 
			
		||||
 | 
			
		||||
		__u8 uuid[16];
 | 
			
		||||
		le64 magic;
 | 
			
		||||
		le32 version;
 | 
			
		||||
 | 
			
		||||
		__u8 policy_name[CACHE_POLICY_NAME_SIZE];
 | 
			
		||||
		le32 policy_version[CACHE_POLICY_VERSION_SIZE];
 | 
			
		||||
		le32 policy_hint_size;
 | 
			
		||||
 | 
			
		||||
		__u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
 | 
			
		||||
 | 
			
		||||
		le64 mapping_root;
 | 
			
		||||
		le64 hint_root;
 | 
			
		||||
 | 
			
		||||
		le64 discard_root;
 | 
			
		||||
		le64 discard_block_size;
 | 
			
		||||
		le64 discard_nr_blocks;
 | 
			
		||||
 | 
			
		||||
		le32 data_block_size; /* in 512-byte sectors */
 | 
			
		||||
		le32 metadata_block_size; /* in 512-byte sectors */
 | 
			
		||||
		le32 cache_blocks;
 | 
			
		||||
 | 
			
		||||
		le32 compat_flags;
 | 
			
		||||
		le32 compat_ro_flags;
 | 
			
		||||
		le32 incompat_flags;
 | 
			
		||||
 | 
			
		||||
		le32 read_hits;
 | 
			
		||||
		le32 read_misses;
 | 
			
		||||
		le32 write_hits;
 | 
			
		||||
		le32 write_misses;
 | 
			
		||||
	} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
	struct superblock {
 | 
			
		||||
		uint32_t csum;
 | 
			
		||||
		uint32_t flags;
 | 
			
		||||
		uint64_t blocknr;
 | 
			
		||||
 | 
			
		||||
		__u8 uuid[16];
 | 
			
		||||
		uint64_t magic;
 | 
			
		||||
		uint32_t version;
 | 
			
		||||
 | 
			
		||||
		__u8 policy_name[CACHE_POLICY_NAME_SIZE];
 | 
			
		||||
		uint32_t policy_version[CACHE_POLICY_VERSION_SIZE];
 | 
			
		||||
		uint32_t policy_hint_size;
 | 
			
		||||
 | 
			
		||||
		__u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
 | 
			
		||||
 | 
			
		||||
		uint64_t mapping_root;
 | 
			
		||||
		uint64_t hint_root;
 | 
			
		||||
 | 
			
		||||
		uint64_t discard_root;
 | 
			
		||||
		uint64_t discard_block_size;
 | 
			
		||||
		uint64_t discard_nr_blocks;
 | 
			
		||||
 | 
			
		||||
		uint32_t data_block_size; /* in 512-byte sectors */
 | 
			
		||||
		uint32_t metadata_block_size; /* in 512-byte sectors */
 | 
			
		||||
		uint32_t cache_blocks;
 | 
			
		||||
 | 
			
		||||
		uint32_t compat_flags;
 | 
			
		||||
		uint32_t compat_ro_flags;
 | 
			
		||||
		uint32_t incompat_flags;
 | 
			
		||||
 | 
			
		||||
		uint32_t read_hits;
 | 
			
		||||
		uint32_t read_misses;
 | 
			
		||||
		uint32_t write_hits;
 | 
			
		||||
		uint32_t write_misses;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct superblock_traits {
 | 
			
		||||
		typedef superblock_disk disk_type;
 | 
			
		||||
		typedef superblock value_type;
 | 
			
		||||
 | 
			
		||||
		static void unpack(superblock_disk const &disk, superblock &value);
 | 
			
		||||
		static void pack(superblock const &value, superblock_disk &disk);
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										377
									
								
								caching/cache_check.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										377
									
								
								caching/cache_check.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,377 @@
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "base/error_state.h"
 | 
			
		||||
#include "base/nested_output.h"
 | 
			
		||||
#include "caching/metadata.h"
 | 
			
		||||
#include "persistent-data/block.h"
 | 
			
		||||
#include "persistent-data/file_utils.h"
 | 
			
		||||
#include "persistent-data/space-maps/core.h"
 | 
			
		||||
#include "version.h"
 | 
			
		||||
 | 
			
		||||
using namespace boost;
 | 
			
		||||
using namespace caching;
 | 
			
		||||
using namespace persistent_data;
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
	class reporter_base {
 | 
			
		||||
	public:
 | 
			
		||||
		reporter_base(nested_output &o)
 | 
			
		||||
		: out_(o),
 | 
			
		||||
		  err_(NO_ERROR) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual ~reporter_base() {}
 | 
			
		||||
 | 
			
		||||
		nested_output &out() {
 | 
			
		||||
			return out_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		nested_output::nest push() {
 | 
			
		||||
			return out_.push();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		base::error_state get_error() const {
 | 
			
		||||
			return err_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void mplus_error(error_state err) {
 | 
			
		||||
			err_ = combine_errors(err_, err);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		nested_output &out_;
 | 
			
		||||
		error_state err_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class superblock_reporter : public superblock_damage::damage_visitor, reporter_base {
 | 
			
		||||
	public:
 | 
			
		||||
		superblock_reporter(nested_output &o)
 | 
			
		||||
		: reporter_base(o) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void visit(superblock_damage::superblock_corrupt const &d) {
 | 
			
		||||
			out() << "superblock is corrupt" << end_message();
 | 
			
		||||
			{
 | 
			
		||||
				nested_output::nest _ = push();
 | 
			
		||||
				out() << d.get_desc() << end_message();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			mplus_error(FATAL);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void visit(superblock_damage::superblock_invalid const &d) {
 | 
			
		||||
			out() << "superblock is invalid" << end_message();
 | 
			
		||||
			{
 | 
			
		||||
				nested_output::nest _ = push();
 | 
			
		||||
				out() << d.get_desc() << end_message();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			mplus_error(FATAL);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		using reporter_base::get_error;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class mapping_reporter : public mapping_array_damage::damage_visitor, reporter_base {
 | 
			
		||||
	public:
 | 
			
		||||
		mapping_reporter(nested_output &o)
 | 
			
		||||
		: reporter_base(o) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void visit(mapping_array_damage::missing_mappings const &d) {
 | 
			
		||||
			out() << "missing mappings " << d.keys_ << ":" << end_message();
 | 
			
		||||
			{
 | 
			
		||||
				nested_output::nest _ = push();
 | 
			
		||||
				out() << d.get_desc() << end_message();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			mplus_error(FATAL);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void visit(mapping_array_damage::invalid_mapping const &d) {
 | 
			
		||||
			out() << "invalid mapping:" << end_message();
 | 
			
		||||
			{
 | 
			
		||||
				nested_output::nest _ = push();
 | 
			
		||||
				out() << d.get_desc()
 | 
			
		||||
				      << " [cblock = " << d.cblock_
 | 
			
		||||
				      << ", oblock = " << d.m_.oblock_
 | 
			
		||||
				      << ", flags = " << d.m_.flags_
 | 
			
		||||
				      << "]" << end_message();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			mplus_error(FATAL);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		using reporter_base::get_error;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class hint_reporter : public hint_array_damage::damage_visitor, reporter_base {
 | 
			
		||||
	public:
 | 
			
		||||
		hint_reporter(nested_output &o)
 | 
			
		||||
		: reporter_base(o) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void visit(hint_array_damage::missing_hints const &d) {
 | 
			
		||||
			out() << "missing mappings " << d.keys_ << ":" << end_message();
 | 
			
		||||
			{
 | 
			
		||||
				nested_output::nest _ = push();
 | 
			
		||||
				out() << d.get_desc() << end_message();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			mplus_error(FATAL);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		using reporter_base::get_error;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class discard_reporter : public bitset_detail::bitset_visitor, reporter_base {
 | 
			
		||||
	public:
 | 
			
		||||
		discard_reporter(nested_output &o)
 | 
			
		||||
		: reporter_base(o) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void visit(uint32_t index, bool value) {
 | 
			
		||||
			// no op
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void visit(bitset_detail::missing_bits const &d) {
 | 
			
		||||
			out() << "missing discard bits "  << d.keys_ << end_message();
 | 
			
		||||
			mplus_error(FATAL);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		using reporter_base::get_error;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
 | 
			
		||||
	transaction_manager::ptr open_tm(block_manager<>::ptr bm) {
 | 
			
		||||
		space_map::ptr sm(new core_map(bm->get_nr_blocks()));
 | 
			
		||||
		sm->inc(SUPERBLOCK_LOCATION);
 | 
			
		||||
		transaction_manager::ptr tm(new transaction_manager(bm, sm));
 | 
			
		||||
		return tm;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
 | 
			
		||||
	struct flags {
 | 
			
		||||
		flags()
 | 
			
		||||
			: check_mappings_(true),
 | 
			
		||||
			  check_hints_(true),
 | 
			
		||||
			  check_discards_(true),
 | 
			
		||||
			  ignore_non_fatal_errors_(false),
 | 
			
		||||
			  quiet_(false) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		bool check_mappings_;
 | 
			
		||||
		bool check_hints_;
 | 
			
		||||
		bool check_discards_;
 | 
			
		||||
		bool ignore_non_fatal_errors_;
 | 
			
		||||
		bool quiet_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct stat guarded_stat(string const &path) {
 | 
			
		||||
		struct stat info;
 | 
			
		||||
 | 
			
		||||
		int r = ::stat(path.c_str(), &info);
 | 
			
		||||
		if (r) {
 | 
			
		||||
			ostringstream msg;
 | 
			
		||||
			char buffer[128], *ptr;
 | 
			
		||||
 | 
			
		||||
			ptr = ::strerror_r(errno, buffer, sizeof(buffer));
 | 
			
		||||
			msg << path << ": " << ptr;
 | 
			
		||||
			throw runtime_error(msg.str());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return info;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	error_state metadata_check(block_manager<>::ptr bm, flags const &fs) {
 | 
			
		||||
		nested_output out(cerr, 2);
 | 
			
		||||
		if (fs.quiet_)
 | 
			
		||||
			out.disable();
 | 
			
		||||
 | 
			
		||||
		superblock_reporter sb_rep(out);
 | 
			
		||||
		mapping_reporter mapping_rep(out);
 | 
			
		||||
		hint_reporter hint_rep(out);
 | 
			
		||||
		discard_reporter discard_rep(out);
 | 
			
		||||
 | 
			
		||||
		out << "examining superblock" << end_message();
 | 
			
		||||
		{
 | 
			
		||||
			nested_output::nest _ = out.push();
 | 
			
		||||
			check_superblock(bm, bm->get_nr_blocks(), sb_rep);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (sb_rep.get_error() == FATAL)
 | 
			
		||||
			return FATAL;
 | 
			
		||||
 | 
			
		||||
		superblock sb = read_superblock(bm);
 | 
			
		||||
		transaction_manager::ptr tm = open_tm(bm);
 | 
			
		||||
 | 
			
		||||
		if (fs.check_mappings_) {
 | 
			
		||||
			out << "examining mapping array" << end_message();
 | 
			
		||||
			{
 | 
			
		||||
				nested_output::nest _ = out.push();
 | 
			
		||||
				mapping_array ma(tm, mapping_array::ref_counter(), sb.mapping_root, sb.cache_blocks);
 | 
			
		||||
				check_mapping_array(ma, mapping_rep);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (fs.check_hints_) {
 | 
			
		||||
			if (!sb.hint_root)
 | 
			
		||||
				out << "no hint array present" << end_message();
 | 
			
		||||
 | 
			
		||||
			else {
 | 
			
		||||
				out << "examining hint array" << end_message();
 | 
			
		||||
				{
 | 
			
		||||
					nested_output::nest _ = out.push();
 | 
			
		||||
					hint_array ha(tm, sb.policy_hint_size, sb.hint_root, sb.cache_blocks);
 | 
			
		||||
					ha.check(hint_rep);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (fs.check_discards_) {
 | 
			
		||||
			if (!sb.discard_root)
 | 
			
		||||
				out << "no discard bitset present" << end_message();
 | 
			
		||||
 | 
			
		||||
			else {
 | 
			
		||||
				out << "examining discard bitset" << end_message();
 | 
			
		||||
				{
 | 
			
		||||
					nested_output::nest _ = out.push();
 | 
			
		||||
					bitset discards(tm, sb.discard_root, sb.discard_nr_blocks);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// FIXME: make an error class that's an instance of mplus
 | 
			
		||||
		return combine_errors(sb_rep.get_error(),
 | 
			
		||||
				      combine_errors(mapping_rep.get_error(),
 | 
			
		||||
						     combine_errors(hint_rep.get_error(),
 | 
			
		||||
								    discard_rep.get_error())));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int check(string const &path, flags const &fs) {
 | 
			
		||||
		error_state err;
 | 
			
		||||
		struct stat info = guarded_stat(path);
 | 
			
		||||
 | 
			
		||||
		if (!S_ISREG(info.st_mode) && !S_ISBLK(info.st_mode)) {
 | 
			
		||||
			ostringstream msg;
 | 
			
		||||
			msg << path << ": " << "Not a block device or regular file";
 | 
			
		||||
			throw runtime_error(msg.str());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		block_manager<>::ptr bm = open_bm(path, block_io<>::READ_ONLY);
 | 
			
		||||
		err = metadata_check(bm, fs);
 | 
			
		||||
 | 
			
		||||
		return err == NO_ERROR ? 0 : 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int check_with_exception_handling(string const &path, flags const &fs) {
 | 
			
		||||
		int r;
 | 
			
		||||
		try {
 | 
			
		||||
			r = check(path, fs);
 | 
			
		||||
 | 
			
		||||
		} catch (std::exception &e) {
 | 
			
		||||
			if (!fs.quiet_)
 | 
			
		||||
				cerr << e.what() << endl;
 | 
			
		||||
			r = 1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return r;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void usage(ostream &out, string const &cmd) {
 | 
			
		||||
		out << "Usage: " << cmd << " [options] {device|file}" << endl
 | 
			
		||||
		    << "Options:" << endl
 | 
			
		||||
		    << "  {-q|--quiet}" << endl
 | 
			
		||||
		    << "  {-h|--help}" << endl
 | 
			
		||||
		    << "  {-V|--version}" << endl
 | 
			
		||||
		    << "  {--super-block-only}" << endl
 | 
			
		||||
		    << "  {--skip-mappings}" << endl
 | 
			
		||||
		    << "  {--skip-hints}" << endl
 | 
			
		||||
		    << "  {--skip-discards}" << endl;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int c;
 | 
			
		||||
	flags fs;
 | 
			
		||||
	const char shortopts[] = "qhV";
 | 
			
		||||
	const struct option longopts[] = {
 | 
			
		||||
		{ "quiet", no_argument, NULL, 'q' },
 | 
			
		||||
		{ "superblock-only", no_argument, NULL, 1 },
 | 
			
		||||
		{ "skip-mappings", no_argument, NULL, 2 },
 | 
			
		||||
		{ "skip-hints", no_argument, NULL, 3 },
 | 
			
		||||
		{ "skip-discards", no_argument, NULL, 4 },
 | 
			
		||||
		{ "help", no_argument, NULL, 'h' },
 | 
			
		||||
		{ "version", no_argument, NULL, 'V' },
 | 
			
		||||
		{ NULL, no_argument, NULL, 0 }
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
 | 
			
		||||
		switch(c) {
 | 
			
		||||
		case 1:
 | 
			
		||||
			fs.check_mappings_ = false;
 | 
			
		||||
			fs.check_hints_ = false;
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case 2:
 | 
			
		||||
			fs.check_mappings_ = false;
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case 3:
 | 
			
		||||
			fs.check_hints_ = false;
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case 4:
 | 
			
		||||
			fs.check_discards_ = false;
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case 'h':
 | 
			
		||||
			usage(cout, basename(argv[0]));
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		case 'q':
 | 
			
		||||
			fs.quiet_ = true;
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case 'V':
 | 
			
		||||
			cout << THIN_PROVISIONING_TOOLS_VERSION << endl;
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			usage(cerr, basename(argv[0]));
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (argc == optind) {
 | 
			
		||||
		cerr << "No input file provided." << endl;
 | 
			
		||||
		usage(cerr, basename(argv[0]));
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return check_with_exception_handling(argv[optind], fs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
							
								
								
									
										117
									
								
								caching/cache_dump.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								caching/cache_dump.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
#include "version.h"
 | 
			
		||||
#include "caching/mapping_array.h"
 | 
			
		||||
#include "caching/metadata.h"
 | 
			
		||||
#include "caching/metadata_dump.h"
 | 
			
		||||
#include "caching/xml_format.h"
 | 
			
		||||
#include "persistent-data/file_utils.h"
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace caching;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	struct flags {
 | 
			
		||||
		flags()
 | 
			
		||||
			: repair_(false) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		bool repair_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
 | 
			
		||||
	string const STDOUT_PATH("-");
 | 
			
		||||
 | 
			
		||||
	bool want_stdout(string const &output) {
 | 
			
		||||
		return output == STDOUT_PATH;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int dump(string const &dev, string const &output, flags const &fs) {
 | 
			
		||||
		try {
 | 
			
		||||
			block_manager<>::ptr bm = open_bm(dev, block_io<>::READ_ONLY);
 | 
			
		||||
			metadata::ptr md(new metadata(bm, metadata::OPEN));
 | 
			
		||||
 | 
			
		||||
			if (want_stdout(output)) {
 | 
			
		||||
				emitter::ptr e = create_xml_emitter(cout);
 | 
			
		||||
				metadata_dump(md, e, fs.repair_);
 | 
			
		||||
			} else {
 | 
			
		||||
				ofstream out(output.c_str());
 | 
			
		||||
				emitter::ptr e = create_xml_emitter(out);
 | 
			
		||||
				metadata_dump(md, e, fs.repair_);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} catch (std::exception &e) {
 | 
			
		||||
			cerr << e.what() << endl;
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void usage(ostream &out, string const &cmd) {
 | 
			
		||||
		out << "Usage: " << cmd << " [options] {device|file}" << endl
 | 
			
		||||
		    << "Options:" << endl
 | 
			
		||||
		    << "  {-h|--help}" << endl
 | 
			
		||||
		    << "  {-o <xml file>}" << endl
 | 
			
		||||
		    << "  {-V|--version}" << endl
 | 
			
		||||
		    << "  {--repair}" << endl;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int c;
 | 
			
		||||
	flags fs;
 | 
			
		||||
	string output("-");
 | 
			
		||||
	char const shortopts[] = "ho:V";
 | 
			
		||||
 | 
			
		||||
	option const longopts[] = {
 | 
			
		||||
		{ "help", no_argument, NULL, 'h' },
 | 
			
		||||
		{ "output", required_argument, NULL, 'o' },
 | 
			
		||||
		{ "version", no_argument, NULL, 'V' },
 | 
			
		||||
		{ "repair", no_argument, NULL, 1 },
 | 
			
		||||
		{ NULL, no_argument, NULL, 0 }
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
 | 
			
		||||
		switch(c) {
 | 
			
		||||
		case 1:
 | 
			
		||||
			fs.repair_ = true;
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case 'h':
 | 
			
		||||
			usage(cout, basename(argv[0]));
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		case 'o':
 | 
			
		||||
			output = optarg;
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case 'V':
 | 
			
		||||
			cout << THIN_PROVISIONING_TOOLS_VERSION << endl;
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			usage(cerr, basename(argv[0]));
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (argc == optind) {
 | 
			
		||||
		cerr << "No input file provided." << endl;
 | 
			
		||||
		usage(cerr, basename(argv[0]));
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dump(argv[optind], output, fs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
							
								
								
									
										108
									
								
								caching/cache_repair.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								caching/cache_repair.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
 | 
			
		||||
#include "caching/metadata.h"
 | 
			
		||||
#include "caching/metadata_dump.h"
 | 
			
		||||
#include "caching/restore_emitter.h"
 | 
			
		||||
#include "persistent-data/file_utils.h"
 | 
			
		||||
#include "version.h"
 | 
			
		||||
 | 
			
		||||
using namespace persistent_data;
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace caching;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	metadata::ptr open_metadata_for_read(string const &path) {
 | 
			
		||||
		block_manager<>::ptr bm = open_bm(path, block_io<>::READ_ONLY);
 | 
			
		||||
		return metadata::ptr(new metadata(bm, metadata::OPEN));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	emitter::ptr output_emitter(string const &path) {
 | 
			
		||||
		block_manager<>::ptr bm = open_bm(path, block_io<>::READ_WRITE);
 | 
			
		||||
		metadata::ptr md(new metadata(bm, metadata::CREATE));
 | 
			
		||||
		return create_restore_emitter(md);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int repair(string const &old_path, string const &new_path) {
 | 
			
		||||
		try {
 | 
			
		||||
			metadata_dump(open_metadata_for_read(old_path),
 | 
			
		||||
				      output_emitter(new_path),
 | 
			
		||||
				      true);
 | 
			
		||||
 | 
			
		||||
		} catch (std::exception &e) {
 | 
			
		||||
			cerr << e.what() << endl;
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void usage(ostream &out, string const &cmd) {
 | 
			
		||||
		out << "Usage: " << cmd << " [options] {device|file}" << endl
 | 
			
		||||
		    << "Options:" << endl
 | 
			
		||||
		    << "  {-h|--help}" << endl
 | 
			
		||||
		    << "  {-i|--input} <input metadata (binary format)>" << endl
 | 
			
		||||
		    << "  {-o|--output} <output metadata (binary format)>" << endl
 | 
			
		||||
		    << "  {-V|--version}" << endl;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int c;
 | 
			
		||||
	boost::optional<string> input_path, output_path;
 | 
			
		||||
	const char shortopts[] = "hi:o:V";
 | 
			
		||||
 | 
			
		||||
	const struct option longopts[] = {
 | 
			
		||||
		{ "help", no_argument, NULL, 'h'},
 | 
			
		||||
		{ "input", required_argument, NULL, 'i'},
 | 
			
		||||
		{ "output", required_argument, NULL, 'o'},
 | 
			
		||||
		{ "version", no_argument, NULL, 'V'},
 | 
			
		||||
		{ NULL, no_argument, NULL, 0 }
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
 | 
			
		||||
		switch(c) {
 | 
			
		||||
		case 'h':
 | 
			
		||||
			usage(cout, basename(argv[0]));
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		case 'i':
 | 
			
		||||
			input_path = optarg;
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case 'o':
 | 
			
		||||
			output_path = optarg;
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case 'V':
 | 
			
		||||
			cout << THIN_PROVISIONING_TOOLS_VERSION << endl;
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			usage(cerr, basename(argv[0]));
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!input_path) {
 | 
			
		||||
		cerr << "no input file provided" << endl;
 | 
			
		||||
		usage(cerr, basename(argv[0]));
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!output_path) {
 | 
			
		||||
		cerr << "no output file provided" << endl;
 | 
			
		||||
		usage(cerr, basename(argv[0]));
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return repair(*input_path, *output_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
							
								
								
									
										108
									
								
								caching/cache_restore.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								caching/cache_restore.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
			
		||||
#include "version.h"
 | 
			
		||||
 | 
			
		||||
#include "caching/metadata.h"
 | 
			
		||||
#include "caching/restore_emitter.h"
 | 
			
		||||
#include "caching/xml_format.h"
 | 
			
		||||
#include "persistent-data/file_utils.h"
 | 
			
		||||
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
using namespace caching;
 | 
			
		||||
using namespace persistent_data;
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	int restore(string const &xml_file, string const &dev) {
 | 
			
		||||
		try {
 | 
			
		||||
			block_manager<>::ptr bm = open_bm(dev, block_io<>::READ_WRITE);
 | 
			
		||||
			metadata::ptr md(new metadata(bm, metadata::CREATE));
 | 
			
		||||
			emitter::ptr restorer = create_restore_emitter(md);
 | 
			
		||||
 | 
			
		||||
			check_file_exists(xml_file);
 | 
			
		||||
			ifstream in(xml_file.c_str(), ifstream::in);
 | 
			
		||||
			parse_xml(in, restorer);
 | 
			
		||||
 | 
			
		||||
		} catch (std::exception &e) {
 | 
			
		||||
			cerr << e.what() << endl;
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void usage(ostream &out, string const &cmd) {
 | 
			
		||||
		out << "Usage: " << cmd << " [options]" << endl
 | 
			
		||||
		    << "Options:" << endl
 | 
			
		||||
		    << "  {-h|--help}" << endl
 | 
			
		||||
		    << "  {-i|--input} <input xml file>" << endl
 | 
			
		||||
		    << "  {-o|--output} <output device or file>" << endl
 | 
			
		||||
		    << "  {-V|--version}" << endl;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int c;
 | 
			
		||||
	string input, output;
 | 
			
		||||
	char const *prog_name = basename(argv[0]);
 | 
			
		||||
	char const *short_opts = "hi:o:V";
 | 
			
		||||
	option const long_opts[] = {
 | 
			
		||||
		{ "help", no_argument, NULL, 'h'},
 | 
			
		||||
		{ "input", required_argument, NULL, 'i' },
 | 
			
		||||
		{ "output", required_argument, NULL, 'o'},
 | 
			
		||||
		{ "version", no_argument, NULL, 'V'},
 | 
			
		||||
		{ NULL, no_argument, NULL, 0 }
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
 | 
			
		||||
		switch(c) {
 | 
			
		||||
		case 'h':
 | 
			
		||||
			usage(cout, prog_name);
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		case 'i':
 | 
			
		||||
			input = optarg;
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case 'o':
 | 
			
		||||
			output = optarg;
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case 'V':
 | 
			
		||||
			cout << THIN_PROVISIONING_TOOLS_VERSION << endl;
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			usage(cerr, prog_name);
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (argc != optind) {
 | 
			
		||||
		usage(cerr, prog_name);
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
        if (input.empty()) {
 | 
			
		||||
		cerr << "No input file provided." << endl << endl;
 | 
			
		||||
		usage(cerr, prog_name);
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (output.empty()) {
 | 
			
		||||
		cerr << "No output file provided." << endl << endl;
 | 
			
		||||
		usage(cerr, prog_name);
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return restore(input, output);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										51
									
								
								caching/emitter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								caching/emitter.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
#ifndef CACHE_EMITTER_H
 | 
			
		||||
#define CACHE_EMITTER_H
 | 
			
		||||
 | 
			
		||||
#include "persistent-data/block.h"
 | 
			
		||||
 | 
			
		||||
#include <boost/shared_ptr.hpp>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace caching {
 | 
			
		||||
	namespace pd = persistent_data;
 | 
			
		||||
 | 
			
		||||
	class emitter {
 | 
			
		||||
	public:
 | 
			
		||||
		typedef boost::shared_ptr<emitter> ptr;
 | 
			
		||||
 | 
			
		||||
		virtual ~emitter() {}
 | 
			
		||||
 | 
			
		||||
		virtual void begin_superblock(std::string const &uuid,
 | 
			
		||||
					      pd::block_address block_size,
 | 
			
		||||
					      pd::block_address nr_cache_blocks,
 | 
			
		||||
					      std::string const &policy,
 | 
			
		||||
					      size_t hint_width) = 0;
 | 
			
		||||
 | 
			
		||||
		virtual void end_superblock() = 0;
 | 
			
		||||
 | 
			
		||||
		virtual void begin_mappings() = 0;
 | 
			
		||||
		virtual void end_mappings() = 0;
 | 
			
		||||
 | 
			
		||||
		virtual void mapping(pd::block_address cblock,
 | 
			
		||||
				     pd::block_address oblock,
 | 
			
		||||
				     bool dirty) = 0;
 | 
			
		||||
 | 
			
		||||
		virtual void begin_hints() = 0;
 | 
			
		||||
		virtual void end_hints() = 0;
 | 
			
		||||
 | 
			
		||||
		virtual void hint(pd::block_address cblock,
 | 
			
		||||
				  std::vector<unsigned char> const &data) = 0;
 | 
			
		||||
 | 
			
		||||
		virtual void begin_discards() = 0;
 | 
			
		||||
		virtual void end_discards() = 0;
 | 
			
		||||
 | 
			
		||||
		virtual void discard(pd::block_address dblock_begin,
 | 
			
		||||
				     pd::block_address dblock_end) = 0;
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										286
									
								
								caching/hint_array.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								caching/hint_array.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,286 @@
 | 
			
		||||
#include "caching/hint_array.h"
 | 
			
		||||
 | 
			
		||||
using namespace boost;
 | 
			
		||||
using namespace caching;
 | 
			
		||||
using namespace caching::hint_array_damage;
 | 
			
		||||
using namespace persistent_data;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	template <uint32_t WIDTH>
 | 
			
		||||
	struct hint_traits {
 | 
			
		||||
		typedef unsigned char byte;
 | 
			
		||||
		typedef byte disk_type[WIDTH];
 | 
			
		||||
		typedef vector<byte> value_type;
 | 
			
		||||
		typedef no_op_ref_counter<value_type> ref_counter;
 | 
			
		||||
 | 
			
		||||
		// FIXME: slow copying for now
 | 
			
		||||
		static void unpack(disk_type const &disk, value_type &value) {
 | 
			
		||||
			value.resize(WIDTH);
 | 
			
		||||
			for (unsigned byte = 0; byte < WIDTH; byte++)
 | 
			
		||||
				value.at(byte) = disk[byte];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		static void pack(value_type const &value, disk_type &disk) {
 | 
			
		||||
			for (unsigned byte = 0; byte < WIDTH; byte++)
 | 
			
		||||
				disk[byte] = value.at(byte);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	// We've got into a bit of a mess here.  Templates are compile
 | 
			
		||||
	// time, and we don't know the hint width until run time.  We're
 | 
			
		||||
	// going to have to provide specialisation for all legal widths and
 | 
			
		||||
	// use the appropriate one.
 | 
			
		||||
 | 
			
		||||
#define all_widths \
 | 
			
		||||
	xx(4); xx(8); xx(12); xx(16); xx(20); xx(24); xx(28); xx(32);\
 | 
			
		||||
	xx(36); xx(40); xx(44); xx(48); xx(52); xx(56); xx(60); xx(64); \
 | 
			
		||||
	xx(68); xx(72); xx(76); xx(80); xx(84); xx(88); xx(92); xx(96); \
 | 
			
		||||
	xx(100); xx(104); xx(108); xx(112); xx(116); xx(120); xx(124); xx(128);
 | 
			
		||||
 | 
			
		||||
	template <uint32_t WIDTH>
 | 
			
		||||
	shared_ptr<array_base> mk_array(transaction_manager::ptr tm) {
 | 
			
		||||
		typedef hint_traits<WIDTH> traits;
 | 
			
		||||
		typedef array<traits> ha;
 | 
			
		||||
 | 
			
		||||
		shared_ptr<array_base> r = typename ha::ptr(new ha(tm, typename traits::ref_counter()));
 | 
			
		||||
 | 
			
		||||
		return r;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	shared_ptr<array_base> mk_array(transaction_manager::ptr tm, uint32_t width) {
 | 
			
		||||
		switch (width) {
 | 
			
		||||
#define xx(n)	case n:	return mk_array<n>(tm)
 | 
			
		||||
 | 
			
		||||
			all_widths
 | 
			
		||||
#undef xx
 | 
			
		||||
		default:
 | 
			
		||||
			throw runtime_error("invalid hint width");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// never get here
 | 
			
		||||
		return shared_ptr<array_base>();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
 | 
			
		||||
	template <typename HA>
 | 
			
		||||
	shared_ptr<HA>
 | 
			
		||||
	downcast_array(shared_ptr<array_base> base) {
 | 
			
		||||
		shared_ptr<HA> a = dynamic_pointer_cast<HA>(base);
 | 
			
		||||
		if (!a)
 | 
			
		||||
			throw runtime_error("internal error: couldn't cast hint array");
 | 
			
		||||
 | 
			
		||||
		return a;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
 | 
			
		||||
	template <uint32_t WIDTH>
 | 
			
		||||
	shared_ptr<array_base> mk_array(transaction_manager::ptr tm, block_address root, unsigned nr_entries) {
 | 
			
		||||
		typedef hint_traits<WIDTH> traits;
 | 
			
		||||
		typedef array<traits> ha;
 | 
			
		||||
 | 
			
		||||
		shared_ptr<array_base> r = typename ha::ptr(new ha(tm, typename traits::ref_counter(), root, nr_entries));
 | 
			
		||||
 | 
			
		||||
		return r;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	shared_ptr<array_base> mk_array(transaction_manager::ptr tm, uint32_t width, block_address root, unsigned nr_entries) {
 | 
			
		||||
		switch (width) {
 | 
			
		||||
#define xx(n)	case n:	return mk_array<n>(tm, root, nr_entries)
 | 
			
		||||
			all_widths
 | 
			
		||||
#undef xx
 | 
			
		||||
		default:
 | 
			
		||||
			throw runtime_error("invalid hint width");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// never get here
 | 
			
		||||
		return shared_ptr<array_base>();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
 | 
			
		||||
	template <uint32_t WIDTH>
 | 
			
		||||
	void get_hint(shared_ptr<array_base> base, unsigned index, vector<unsigned char> &data) {
 | 
			
		||||
		typedef hint_traits<WIDTH> traits;
 | 
			
		||||
		typedef array<traits> ha;
 | 
			
		||||
 | 
			
		||||
		shared_ptr<ha> a = downcast_array<ha>(base);
 | 
			
		||||
		data = a->get(index);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void get_hint_(uint32_t width, shared_ptr<array_base> base, unsigned index, vector<unsigned char> &data) {
 | 
			
		||||
		switch (width) {
 | 
			
		||||
#define xx(n) case n: return get_hint<n>(base, index, data)
 | 
			
		||||
			all_widths
 | 
			
		||||
#undef xx
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
 | 
			
		||||
	template <uint32_t WIDTH>
 | 
			
		||||
	void set_hint(shared_ptr<array_base> base, unsigned index, vector<unsigned char> const &data) {
 | 
			
		||||
		typedef hint_traits<WIDTH> traits;
 | 
			
		||||
		typedef array<traits> ha;
 | 
			
		||||
 | 
			
		||||
		shared_ptr<ha> a = downcast_array<ha>(base);
 | 
			
		||||
		a->set(index, data);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
        void set_hint_(uint32_t width, shared_ptr<array_base> base,
 | 
			
		||||
		      unsigned index, vector<unsigned char> const &data) {
 | 
			
		||||
		switch (width) {
 | 
			
		||||
#define xx(n) case n: return set_hint<n>(base, index, data)
 | 
			
		||||
		all_widths
 | 
			
		||||
#undef xx
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
 | 
			
		||||
	template <uint32_t WIDTH>
 | 
			
		||||
	void grow(shared_ptr<array_base> base, unsigned new_nr_entries, vector<unsigned char> const &value) {
 | 
			
		||||
		typedef hint_traits<WIDTH> traits;
 | 
			
		||||
		typedef array<traits> ha;
 | 
			
		||||
 | 
			
		||||
		shared_ptr<ha> a = downcast_array<ha>(base);
 | 
			
		||||
		a->grow(new_nr_entries, value);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void grow_(uint32_t width, shared_ptr<array_base> base,
 | 
			
		||||
		   unsigned new_nr_entries, vector<unsigned char> const &value)
 | 
			
		||||
	{
 | 
			
		||||
		switch (width) {
 | 
			
		||||
#define xx(n) case n: return grow<n>(base, new_nr_entries, value)
 | 
			
		||||
		all_widths
 | 
			
		||||
#undef xx
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
 | 
			
		||||
	class value_adapter {
 | 
			
		||||
	public:
 | 
			
		||||
		value_adapter(hint_visitor &v)
 | 
			
		||||
		: v_(v) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void visit(uint32_t index, std::vector<unsigned char> const &v) {
 | 
			
		||||
			v_.visit(static_cast<block_address>(index), v);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		hint_visitor &v_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct no_op_visitor : public hint_visitor {
 | 
			
		||||
		virtual void visit(block_address cblock, std::vector<unsigned char> const &v) {
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class ll_damage_visitor {
 | 
			
		||||
	public:
 | 
			
		||||
		ll_damage_visitor(damage_visitor &v)
 | 
			
		||||
		: v_(v) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void visit(array_detail::damage const &d) {
 | 
			
		||||
			v_.visit(missing_hints(d.desc_, d.lost_keys_));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		damage_visitor &v_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	template <uint32_t WIDTH>
 | 
			
		||||
	void walk_hints(shared_ptr<array_base> base, hint_visitor &hv, damage_visitor &dv) {
 | 
			
		||||
		typedef hint_traits<WIDTH> traits;
 | 
			
		||||
		typedef array<traits> ha;
 | 
			
		||||
 | 
			
		||||
		shared_ptr<ha> a = downcast_array<ha>(base);
 | 
			
		||||
		value_adapter vv(hv);
 | 
			
		||||
		ll_damage_visitor ll(dv);
 | 
			
		||||
		a->visit_values(vv, ll);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void walk_hints_(uint32_t width, shared_ptr<array_base> base,
 | 
			
		||||
			 hint_visitor &hv, damage_visitor &dv) {
 | 
			
		||||
		switch (width) {
 | 
			
		||||
#define xx(n) case n: walk_hints<n>(base, hv, dv); break
 | 
			
		||||
			all_widths
 | 
			
		||||
#undef xx
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
missing_hints::missing_hints(std::string const desc, run<uint32_t> const &keys)
 | 
			
		||||
	: damage(desc),
 | 
			
		||||
	  keys_(keys)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
missing_hints::visit(damage_visitor &v) const
 | 
			
		||||
{
 | 
			
		||||
	v.visit(*this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
hint_array::hint_array(tm_ptr tm, unsigned width)
 | 
			
		||||
	: width_(width),
 | 
			
		||||
	  impl_(mk_array(tm, width))
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
hint_array::hint_array(typename hint_array::tm_ptr tm, unsigned width,
 | 
			
		||||
		       block_address root, unsigned nr_entries)
 | 
			
		||||
	: width_(width),
 | 
			
		||||
	  impl_(mk_array(tm, width, root, nr_entries))
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_address
 | 
			
		||||
hint_array::get_root() const
 | 
			
		||||
{
 | 
			
		||||
	return impl_->get_root();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
hint_array::get_hint(unsigned index, vector<unsigned char> &data) const
 | 
			
		||||
{
 | 
			
		||||
	get_hint_(width_, impl_, index, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
hint_array::set_hint(unsigned index, vector<unsigned char> const &data)
 | 
			
		||||
{
 | 
			
		||||
	set_hint_(width_, impl_, index, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
hint_array::grow(unsigned new_nr_entries, vector<unsigned char> const &value)
 | 
			
		||||
{
 | 
			
		||||
	grow_(width_, impl_, new_nr_entries, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
hint_array::walk(hint_visitor &hv, hint_array_damage::damage_visitor &dv)
 | 
			
		||||
{
 | 
			
		||||
	walk_hints_(width_, impl_, hv, dv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
hint_array::check(hint_array_damage::damage_visitor &visitor)
 | 
			
		||||
{
 | 
			
		||||
	no_op_visitor vv;
 | 
			
		||||
	walk(vv, visitor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
							
								
								
									
										85
									
								
								caching/hint_array.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								caching/hint_array.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
#ifndef CACHE_HINT_ARRAY_H
 | 
			
		||||
#define CACHE_HINT_ARRAY_H
 | 
			
		||||
 | 
			
		||||
#include "persistent-data/data-structures/array.h"
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace caching {
 | 
			
		||||
	namespace hint_array_damage {
 | 
			
		||||
		class damage_visitor;
 | 
			
		||||
 | 
			
		||||
		class damage {
 | 
			
		||||
		public:
 | 
			
		||||
			damage(std::string const &desc)
 | 
			
		||||
				: desc_(desc) {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			virtual ~damage() {}
 | 
			
		||||
			virtual void visit(damage_visitor &v) const = 0;
 | 
			
		||||
 | 
			
		||||
			std::string get_desc() const {
 | 
			
		||||
				return desc_;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		private:
 | 
			
		||||
			std::string desc_;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		struct missing_hints : public damage {
 | 
			
		||||
			missing_hints(std::string const desc, run<uint32_t> const &keys);
 | 
			
		||||
			virtual void visit(damage_visitor &v) const;
 | 
			
		||||
 | 
			
		||||
			run<uint32_t> keys_;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		class damage_visitor {
 | 
			
		||||
		public:
 | 
			
		||||
			virtual ~damage_visitor() {}
 | 
			
		||||
 | 
			
		||||
			void visit(damage const &d) {
 | 
			
		||||
				d.visit(*this);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			virtual void visit(missing_hints const &d) = 0;
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	class hint_visitor {
 | 
			
		||||
	public:
 | 
			
		||||
		virtual ~hint_visitor() {}
 | 
			
		||||
		virtual void visit(block_address cblock, std::vector<unsigned char> const &data) = 0;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class hint_array {
 | 
			
		||||
	public:
 | 
			
		||||
		typedef boost::shared_ptr<hint_array> ptr;
 | 
			
		||||
		typedef typename persistent_data::transaction_manager::ptr tm_ptr;
 | 
			
		||||
 | 
			
		||||
		hint_array(tm_ptr tm, unsigned width);
 | 
			
		||||
		hint_array(tm_ptr tm, unsigned width, block_address root, unsigned nr_entries);
 | 
			
		||||
 | 
			
		||||
		unsigned get_nr_entries() const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		void grow(unsigned new_nr_entries, void const *v);
 | 
			
		||||
 | 
			
		||||
		block_address get_root() const;
 | 
			
		||||
		void get_hint(unsigned index, vector<unsigned char> &data) const;
 | 
			
		||||
		void set_hint(unsigned index, vector<unsigned char> const &data);
 | 
			
		||||
 | 
			
		||||
		void grow(unsigned new_nr_entries, vector<unsigned char> const &value);
 | 
			
		||||
		void walk(hint_visitor &hv, hint_array_damage::damage_visitor &dv);
 | 
			
		||||
		void check(hint_array_damage::damage_visitor &visitor);
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		unsigned width_;
 | 
			
		||||
		boost::shared_ptr<persistent_data::array_base> impl_;
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										132
									
								
								caching/mapping_array.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								caching/mapping_array.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,132 @@
 | 
			
		||||
#include "caching/mapping_array.h"
 | 
			
		||||
#include "persistent-data/endian_utils.h"
 | 
			
		||||
 | 
			
		||||
#include <set>
 | 
			
		||||
 | 
			
		||||
using namespace caching;
 | 
			
		||||
using namespace caching::mapping_array_damage;
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	const uint64_t FLAGS_MASK = (1 << 16) - 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
mapping_traits::unpack(disk_type const &disk, value_type &value)
 | 
			
		||||
{
 | 
			
		||||
	uint64_t v = base::to_cpu<uint64_t>(disk);
 | 
			
		||||
	value.oblock_ = v >> 16;
 | 
			
		||||
	value.flags_ = v & FLAGS_MASK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
mapping_traits::pack(value_type const &value, disk_type &disk)
 | 
			
		||||
{
 | 
			
		||||
	uint64_t packed = value.oblock_ << 16;
 | 
			
		||||
	packed = packed | (value.flags_ & FLAGS_MASK);
 | 
			
		||||
	disk = base::to_disk<le64>(packed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
missing_mappings::missing_mappings(std::string const &desc, run<uint32_t> const &keys)
 | 
			
		||||
	: damage(desc),
 | 
			
		||||
	  keys_(keys)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
missing_mappings::visit(damage_visitor &v) const
 | 
			
		||||
{
 | 
			
		||||
	v.visit(*this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
invalid_mapping::invalid_mapping(std::string const &desc,
 | 
			
		||||
				 block_address cblock, mapping const &m)
 | 
			
		||||
	: damage(desc),
 | 
			
		||||
	  cblock_(cblock),
 | 
			
		||||
	  m_(m)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
invalid_mapping::visit(damage_visitor &v) const
 | 
			
		||||
{
 | 
			
		||||
	v.visit(*this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	class check_mapping_visitor : public mapping_visitor {
 | 
			
		||||
	public:
 | 
			
		||||
		check_mapping_visitor(damage_visitor &visitor)
 | 
			
		||||
		: visitor_(visitor) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void visit(block_address cblock, mapping const &m) {
 | 
			
		||||
			if (!valid_mapping(m))
 | 
			
		||||
				return;
 | 
			
		||||
 | 
			
		||||
			if (seen_oblock(m))
 | 
			
		||||
				visitor_.visit(invalid_mapping("origin block already mapped", cblock, m));
 | 
			
		||||
			else
 | 
			
		||||
				record_oblock(m);
 | 
			
		||||
 | 
			
		||||
			if (unknown_flags(m))
 | 
			
		||||
				visitor_.visit(invalid_mapping("unknown flags in mapping", cblock, m));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		static bool valid_mapping(mapping const &m) {
 | 
			
		||||
			return !!(m.flags_ & M_VALID);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		bool seen_oblock(mapping const &m) const {
 | 
			
		||||
			return seen_oblocks_.find(m.oblock_) != seen_oblocks_.end();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void record_oblock(mapping const &m) {
 | 
			
		||||
			seen_oblocks_.insert(m.oblock_);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		static bool unknown_flags(mapping const &m) {
 | 
			
		||||
			return (m.flags_ & ~(M_VALID | M_DIRTY));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		damage_visitor &visitor_;
 | 
			
		||||
		set<block_address> seen_oblocks_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class ll_damage_visitor {
 | 
			
		||||
	public:
 | 
			
		||||
		ll_damage_visitor(damage_visitor &v)
 | 
			
		||||
		: v_(v) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void visit(array_detail::damage const &d) {
 | 
			
		||||
			v_.visit(missing_mappings(d.desc_, d.lost_keys_));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		damage_visitor &v_;
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
caching::walk_mapping_array(mapping_array const &array,
 | 
			
		||||
			    mapping_visitor &mv,
 | 
			
		||||
			    damage_visitor &dv)
 | 
			
		||||
{
 | 
			
		||||
	ll_damage_visitor ll(dv);
 | 
			
		||||
	array.visit_values(mv, ll);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
caching::check_mapping_array(mapping_array const &array, damage_visitor &visitor)
 | 
			
		||||
{
 | 
			
		||||
	check_mapping_visitor mv(visitor);
 | 
			
		||||
	walk_mapping_array(array, mv, visitor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
							
								
								
									
										99
									
								
								caching/mapping_array.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								caching/mapping_array.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
			
		||||
#ifndef CACHE_MAPPING_ARRAY_H
 | 
			
		||||
#define CACHE_MAPPING_ARRAY_H
 | 
			
		||||
 | 
			
		||||
#include "persistent-data/data-structures/array.h"
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace caching {
 | 
			
		||||
	enum mapping_flags {
 | 
			
		||||
		M_VALID = 1,
 | 
			
		||||
		M_DIRTY = 2
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct mapping {
 | 
			
		||||
		uint64_t oblock_;
 | 
			
		||||
		uint32_t flags_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct mapping_traits {
 | 
			
		||||
		typedef base::le64 disk_type;
 | 
			
		||||
		typedef mapping value_type;
 | 
			
		||||
		typedef no_op_ref_counter<value_type> ref_counter;
 | 
			
		||||
 | 
			
		||||
		static void unpack(disk_type const &disk, value_type &value);
 | 
			
		||||
		static void pack(value_type const &value, disk_type &disk);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	namespace mapping_array_damage {
 | 
			
		||||
		class damage_visitor;
 | 
			
		||||
 | 
			
		||||
		class damage {
 | 
			
		||||
		public:
 | 
			
		||||
			damage(std::string const &desc)
 | 
			
		||||
				: desc_(desc) {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			virtual ~damage() {}
 | 
			
		||||
			virtual void visit(damage_visitor &v) const = 0;
 | 
			
		||||
 | 
			
		||||
			std::string get_desc() const {
 | 
			
		||||
				return desc_;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		private:
 | 
			
		||||
			std::string desc_;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		struct missing_mappings : public damage {
 | 
			
		||||
			missing_mappings(std::string const &desc, run<uint32_t> const &keys);
 | 
			
		||||
			virtual void visit(damage_visitor &v) const;
 | 
			
		||||
 | 
			
		||||
			run<uint32_t> keys_;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		struct invalid_mapping : public damage {
 | 
			
		||||
			invalid_mapping(std::string const &desc, block_address cblock, mapping const &m);
 | 
			
		||||
			virtual void visit(damage_visitor &v) const;
 | 
			
		||||
 | 
			
		||||
			block_address cblock_;
 | 
			
		||||
			mapping m_;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		class damage_visitor {
 | 
			
		||||
		public:
 | 
			
		||||
			virtual ~damage_visitor() {}
 | 
			
		||||
 | 
			
		||||
			void visit(mapping_array_damage::damage const &d) {
 | 
			
		||||
				d.visit(*this);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			virtual void visit(missing_mappings const &d) = 0;
 | 
			
		||||
			virtual void visit(invalid_mapping const &d) = 0;
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	typedef persistent_data::array<mapping_traits> mapping_array;
 | 
			
		||||
 | 
			
		||||
	class mapping_visitor {
 | 
			
		||||
	public:
 | 
			
		||||
		virtual ~mapping_visitor() {}
 | 
			
		||||
 | 
			
		||||
		void visit(uint32_t index, mapping const &m) {
 | 
			
		||||
			visit(static_cast<block_address>(index), m);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void visit(block_address cblock, mapping const &m) = 0;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	void walk_mapping_array(mapping_array const &array,
 | 
			
		||||
				mapping_visitor &mv,
 | 
			
		||||
				mapping_array_damage::damage_visitor &dv);
 | 
			
		||||
 | 
			
		||||
	void check_mapping_array(mapping_array const &array,
 | 
			
		||||
				 mapping_array_damage::damage_visitor &visitor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										138
									
								
								caching/metadata.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								caching/metadata.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,138 @@
 | 
			
		||||
#include "caching/metadata.h"
 | 
			
		||||
#include "caching/superblock.h"
 | 
			
		||||
#include "persistent-data/space-maps/core.h"
 | 
			
		||||
 | 
			
		||||
using namespace caching;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	unsigned const METADATA_CACHE_SIZE = 1024;
 | 
			
		||||
 | 
			
		||||
	// FIXME: duplication
 | 
			
		||||
	transaction_manager::ptr
 | 
			
		||||
	open_tm(block_manager<>::ptr bm) {
 | 
			
		||||
		space_map::ptr sm(new core_map(bm->get_nr_blocks()));
 | 
			
		||||
		sm->inc(SUPERBLOCK_LOCATION);
 | 
			
		||||
		transaction_manager::ptr tm(new transaction_manager(bm, sm));
 | 
			
		||||
		return tm;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void
 | 
			
		||||
	copy_space_maps(space_map::ptr lhs, space_map::ptr rhs) {
 | 
			
		||||
		for (block_address b = 0; b < rhs->get_nr_blocks(); b++) {
 | 
			
		||||
			uint32_t count = rhs->get_count(b);
 | 
			
		||||
			if (count > 0)
 | 
			
		||||
				lhs->set_count(b, rhs->get_count(b));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
metadata::metadata(block_manager<>::ptr bm, open_type ot)
 | 
			
		||||
{
 | 
			
		||||
	switch (ot) {
 | 
			
		||||
	case CREATE:
 | 
			
		||||
		create_metadata(bm);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case OPEN:
 | 
			
		||||
		open_metadata(bm);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		throw runtime_error("unhandled open_type");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
metadata::commit()
 | 
			
		||||
{
 | 
			
		||||
	commit_space_map();
 | 
			
		||||
	commit_mappings();
 | 
			
		||||
	commit_hints();
 | 
			
		||||
	commit_discard_bits();
 | 
			
		||||
	commit_superblock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
metadata::setup_hint_array(size_t width)
 | 
			
		||||
{
 | 
			
		||||
	if (width > 0)
 | 
			
		||||
		hints_ = hint_array::ptr(
 | 
			
		||||
			new hint_array(tm_, width));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
metadata::create_metadata(block_manager<>::ptr bm)
 | 
			
		||||
{
 | 
			
		||||
	tm_ = open_tm(bm);
 | 
			
		||||
 | 
			
		||||
	space_map::ptr core = tm_->get_sm();
 | 
			
		||||
	metadata_sm_ = create_metadata_sm(tm_, tm_->get_bm()->get_nr_blocks());
 | 
			
		||||
	copy_space_maps(metadata_sm_, core);
 | 
			
		||||
	tm_->set_sm(metadata_sm_);
 | 
			
		||||
 | 
			
		||||
	mappings_ = mapping_array::ptr(new mapping_array(tm_, mapping_array::ref_counter()));
 | 
			
		||||
 | 
			
		||||
	// We can't instantiate the hint array yet, since we don't know the
 | 
			
		||||
	// hint width.
 | 
			
		||||
 | 
			
		||||
	discard_bits_ = bitset::ptr(new bitset(tm_));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
metadata::open_metadata(block_manager<>::ptr bm)
 | 
			
		||||
{
 | 
			
		||||
	tm_ = open_tm(bm);
 | 
			
		||||
	sb_ = read_superblock(tm_->get_bm());
 | 
			
		||||
 | 
			
		||||
	mappings_ = mapping_array::ptr(
 | 
			
		||||
		new mapping_array(tm_,
 | 
			
		||||
				  mapping_array::ref_counter(),
 | 
			
		||||
				  sb_.mapping_root,
 | 
			
		||||
				  sb_.cache_blocks));
 | 
			
		||||
 | 
			
		||||
	if (sb_.hint_root)
 | 
			
		||||
		hints_ = hint_array::ptr(
 | 
			
		||||
			new hint_array(tm_, sb_.policy_hint_size,
 | 
			
		||||
				       sb_.hint_root, sb_.cache_blocks));
 | 
			
		||||
 | 
			
		||||
	if (sb_.discard_root)
 | 
			
		||||
		discard_bits_ = bitset::ptr(
 | 
			
		||||
			new bitset(tm_, sb_.discard_root, sb_.discard_nr_blocks));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
metadata::commit_space_map()
 | 
			
		||||
{
 | 
			
		||||
	metadata_sm_->commit();
 | 
			
		||||
	metadata_sm_->copy_root(&sb_.metadata_space_map_root, sizeof(sb_.metadata_space_map_root));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
metadata::commit_mappings()
 | 
			
		||||
{
 | 
			
		||||
	sb_.mapping_root = mappings_->get_root();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
metadata::commit_hints()
 | 
			
		||||
{
 | 
			
		||||
	sb_.hint_root = hints_->get_root();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
metadata::commit_discard_bits()
 | 
			
		||||
{
 | 
			
		||||
	sb_.discard_root = discard_bits_->get_root();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
metadata::commit_superblock()
 | 
			
		||||
{
 | 
			
		||||
	write_superblock(tm_->get_bm(), sb_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
							
								
								
									
										59
									
								
								caching/metadata.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								caching/metadata.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
#ifndef CACHE_METADATA_H
 | 
			
		||||
#define CACHE_METADATA_H
 | 
			
		||||
 | 
			
		||||
#include "persistent-data/block.h"
 | 
			
		||||
#include "persistent-data/data-structures/array.h"
 | 
			
		||||
#include "persistent-data/data-structures/bitset.h"
 | 
			
		||||
#include "persistent-data/endian_utils.h"
 | 
			
		||||
#include "persistent-data/space-maps/disk.h"
 | 
			
		||||
#include "persistent-data/transaction_manager.h"
 | 
			
		||||
 | 
			
		||||
#include "caching/superblock.h"
 | 
			
		||||
#include "caching/hint_array.h"
 | 
			
		||||
#include "caching/mapping_array.h"
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace caching {
 | 
			
		||||
	class metadata {
 | 
			
		||||
	public:
 | 
			
		||||
		enum open_type {
 | 
			
		||||
			CREATE,
 | 
			
		||||
			OPEN
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		typedef block_manager<>::read_ref read_ref;
 | 
			
		||||
		typedef block_manager<>::write_ref write_ref;
 | 
			
		||||
		typedef boost::shared_ptr<metadata> ptr;
 | 
			
		||||
 | 
			
		||||
		metadata(block_manager<>::ptr bm, open_type ot);
 | 
			
		||||
 | 
			
		||||
		void commit();
 | 
			
		||||
		void setup_hint_array(size_t width);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		typedef persistent_data::transaction_manager tm;
 | 
			
		||||
		tm::ptr tm_;
 | 
			
		||||
		superblock sb_;
 | 
			
		||||
		checked_space_map::ptr metadata_sm_;
 | 
			
		||||
		mapping_array::ptr mappings_;
 | 
			
		||||
		hint_array::ptr hints_;
 | 
			
		||||
		bitset::ptr discard_bits_;
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		void init_superblock();
 | 
			
		||||
 | 
			
		||||
		void create_metadata(block_manager<>::ptr bm);
 | 
			
		||||
		void open_metadata(block_manager<>::ptr bm);
 | 
			
		||||
 | 
			
		||||
		void commit_space_map();
 | 
			
		||||
		void commit_mappings();
 | 
			
		||||
		void commit_hints();
 | 
			
		||||
		void commit_discard_bits();
 | 
			
		||||
		void commit_superblock();
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										28
									
								
								caching/metadata_disk_structures.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								caching/metadata_disk_structures.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
// Copyright (C) 2012 Red Hat, Inc. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// This file is part of the thin-provisioning-tools source.
 | 
			
		||||
//
 | 
			
		||||
// thin-provisioning-tools is free software: you can redistribute it
 | 
			
		||||
// and/or modify it under the terms of the GNU General Public License
 | 
			
		||||
// as published by the Free Software Foundation, either version 3 of
 | 
			
		||||
// the License, or (at your option) any later version.
 | 
			
		||||
//
 | 
			
		||||
// thin-provisioning-tools is distributed in the hope that it will be
 | 
			
		||||
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 | 
			
		||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License along
 | 
			
		||||
// with thin-provisioning-tools.  If not, see
 | 
			
		||||
// <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
#include "metadata_disk_structures.h"
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
using namespace cache_tools;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
							
								
								
									
										35
									
								
								caching/metadata_disk_structures.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								caching/metadata_disk_structures.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
// Copyright (C) 2012 Red Hat, Inc. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// This file is part of the thin-provisioning-tools source.
 | 
			
		||||
//
 | 
			
		||||
// thin-provisioning-tools is free software: you can redistribute it
 | 
			
		||||
// and/or modify it under the terms of the GNU General Public License
 | 
			
		||||
// as published by the Free Software Foundation, either version 3 of
 | 
			
		||||
// the License, or (at your option) any later version.
 | 
			
		||||
//
 | 
			
		||||
// thin-provisioning-tools is distributed in the hope that it will be
 | 
			
		||||
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 | 
			
		||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License along
 | 
			
		||||
// with thin-provisioning-tools.  If not, see
 | 
			
		||||
// <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
#ifndef CACHE_METADATA_DISK_STRUCTURES_H
 | 
			
		||||
#define CACHE_METADATA_DISK_STRUCTURES_H
 | 
			
		||||
 | 
			
		||||
#include "persistent-data/endian_utils.h"
 | 
			
		||||
#include "persistent-data/data-structures/btree.h"
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// FIXME: rename to just METADATA_DISK_STRUCTURES
 | 
			
		||||
namespace cache_tools {
 | 
			
		||||
	using namespace base;	// FIXME: don't use namespaces in headers.
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										144
									
								
								caching/metadata_dump.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								caching/metadata_dump.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,144 @@
 | 
			
		||||
#include "caching/metadata_dump.h"
 | 
			
		||||
 | 
			
		||||
#include <set>
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace caching;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	string to_string(unsigned char const *data) {
 | 
			
		||||
		// FIXME: we're assuming the data is zero terminated here
 | 
			
		||||
		return std::string(reinterpret_cast<char const *>(data));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void raise_metadata_damage() {
 | 
			
		||||
		throw std::runtime_error("metadata contains errors (run cache_check for details).\n"
 | 
			
		||||
					 "perhaps you wanted to run with --repair");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
 | 
			
		||||
	class mapping_emitter : public mapping_visitor {
 | 
			
		||||
	public:
 | 
			
		||||
		mapping_emitter(emitter::ptr e, set<block_address> &valid_blocks)
 | 
			
		||||
			: e_(e),
 | 
			
		||||
			  valid_blocks_(valid_blocks) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void visit(block_address cblock, mapping const &m) {
 | 
			
		||||
			if (m.flags_ & M_VALID) {
 | 
			
		||||
				e_->mapping(cblock, m.oblock_, m.flags_ & M_DIRTY);
 | 
			
		||||
				mark_valid(cblock);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		void mark_valid(block_address cblock) {
 | 
			
		||||
			valid_blocks_.insert(cblock);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		emitter::ptr e_;
 | 
			
		||||
		set<block_address> &valid_blocks_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct ignore_mapping_damage : public mapping_array_damage::damage_visitor {
 | 
			
		||||
		virtual void visit(mapping_array_damage::missing_mappings const &d) {}
 | 
			
		||||
		virtual void visit(mapping_array_damage::invalid_mapping const &d) {}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class fatal_mapping_damage : public mapping_array_damage::damage_visitor {
 | 
			
		||||
	public:
 | 
			
		||||
		virtual void visit(mapping_array_damage::missing_mappings const &d) {
 | 
			
		||||
			raise_metadata_damage();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void visit(mapping_array_damage::invalid_mapping const &d) {
 | 
			
		||||
			raise_metadata_damage();
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
 | 
			
		||||
	class hint_emitter : public hint_visitor {
 | 
			
		||||
	public:
 | 
			
		||||
		hint_emitter(emitter::ptr e, set<block_address> &valid_blocks)
 | 
			
		||||
			: e_(e),
 | 
			
		||||
			  valid_blocks_(valid_blocks) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void visit(block_address cblock, std::vector<unsigned char> const &data) {
 | 
			
		||||
			if (valid(cblock))
 | 
			
		||||
				e_->hint(cblock, data);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		bool valid(block_address cblock) const {
 | 
			
		||||
			return valid_blocks_.find(cblock) != valid_blocks_.end();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		emitter::ptr e_;
 | 
			
		||||
		set<block_address> &valid_blocks_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct ignore_hint_damage : public hint_array_damage::damage_visitor {
 | 
			
		||||
		virtual void visit(hint_array_damage::missing_hints const &d) {}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class fatal_hint_damage : public hint_array_damage::damage_visitor {
 | 
			
		||||
		virtual void visit(hint_array_damage::missing_hints const &d) {
 | 
			
		||||
			raise_metadata_damage();
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
caching::metadata_dump(metadata::ptr md, emitter::ptr e, bool repair)
 | 
			
		||||
{
 | 
			
		||||
	set<block_address> valid_blocks;
 | 
			
		||||
 | 
			
		||||
	superblock const &sb = md->sb_;
 | 
			
		||||
	e->begin_superblock(to_string(sb.uuid), sb.data_block_size,
 | 
			
		||||
			    sb.cache_blocks, to_string(sb.policy_name),
 | 
			
		||||
			    sb.policy_hint_size);
 | 
			
		||||
 | 
			
		||||
	e->begin_mappings();
 | 
			
		||||
	{
 | 
			
		||||
		namespace mad = mapping_array_damage;
 | 
			
		||||
 | 
			
		||||
		mapping_emitter me(e, valid_blocks);
 | 
			
		||||
		ignore_mapping_damage ignore;
 | 
			
		||||
		fatal_mapping_damage fatal;
 | 
			
		||||
		mad::damage_visitor &dv = repair ?
 | 
			
		||||
			static_cast<mad::damage_visitor &>(ignore) :
 | 
			
		||||
			static_cast<mad::damage_visitor &>(fatal);
 | 
			
		||||
		walk_mapping_array(*md->mappings_, me, dv);
 | 
			
		||||
	}
 | 
			
		||||
	e->end_mappings();
 | 
			
		||||
 | 
			
		||||
	// walk hints
 | 
			
		||||
	e->begin_hints();
 | 
			
		||||
	{
 | 
			
		||||
		using namespace hint_array_damage;
 | 
			
		||||
 | 
			
		||||
		hint_emitter he(e, valid_blocks);
 | 
			
		||||
		ignore_hint_damage ignore;
 | 
			
		||||
		fatal_hint_damage fatal;
 | 
			
		||||
		damage_visitor &dv = repair ?
 | 
			
		||||
			static_cast<damage_visitor &>(ignore) :
 | 
			
		||||
			static_cast<damage_visitor &>(fatal);
 | 
			
		||||
		md->hints_->walk(he, dv);
 | 
			
		||||
	}
 | 
			
		||||
	e->end_hints();
 | 
			
		||||
 | 
			
		||||
	// FIXME: walk discards
 | 
			
		||||
 | 
			
		||||
	e->end_superblock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										17
									
								
								caching/metadata_dump.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								caching/metadata_dump.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
#ifndef CACHING_METADATA_DUMP_H
 | 
			
		||||
#define CACHING_METADATA_DUMP_H
 | 
			
		||||
 | 
			
		||||
#include "caching/metadata.h"
 | 
			
		||||
#include "caching/emitter.h"
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace caching {
 | 
			
		||||
	// If 'repair' is set then any metadata damage will be stepped
 | 
			
		||||
	// around.  Otherwise an exception will be thrown.
 | 
			
		||||
	void metadata_dump(metadata::ptr md, emitter::ptr out, bool repair);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										113
									
								
								caching/restore_emitter.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								caching/restore_emitter.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
			
		||||
#include "caching/restore_emitter.h"
 | 
			
		||||
#include "caching/superblock.h"
 | 
			
		||||
#include "caching/mapping_array.h"
 | 
			
		||||
 | 
			
		||||
using namespace caching;
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace superblock_damage;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	class restorer : public emitter {
 | 
			
		||||
	public:
 | 
			
		||||
		restorer(metadata::ptr md)
 | 
			
		||||
			: in_superblock_(false),
 | 
			
		||||
			  md_(md) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual ~restorer() {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void begin_superblock(std::string const &uuid,
 | 
			
		||||
					      pd::block_address block_size,
 | 
			
		||||
					      pd::block_address nr_cache_blocks,
 | 
			
		||||
					      std::string const &policy,
 | 
			
		||||
					      size_t hint_width) {
 | 
			
		||||
 | 
			
		||||
			superblock &sb = md_->sb_;
 | 
			
		||||
			strncpy((char *) sb.policy_name, policy.c_str(), sizeof(sb.policy_name));
 | 
			
		||||
			memset(sb.policy_version, 0, sizeof(sb.policy_version)); // FIXME: should come from xml
 | 
			
		||||
			sb.policy_hint_size = hint_width;
 | 
			
		||||
			md_->setup_hint_array(hint_width);
 | 
			
		||||
 | 
			
		||||
			sb.data_block_size = block_size;
 | 
			
		||||
			sb.cache_blocks = nr_cache_blocks;
 | 
			
		||||
 | 
			
		||||
			struct mapping unmapped_value;
 | 
			
		||||
			unmapped_value.oblock_ = 0;
 | 
			
		||||
			unmapped_value.flags_ = 0;
 | 
			
		||||
			md_->mappings_->grow(nr_cache_blocks, unmapped_value);
 | 
			
		||||
 | 
			
		||||
			vector<unsigned char> hint_value(hint_width, '\0');
 | 
			
		||||
			md_->hints_->grow(nr_cache_blocks, hint_value);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void end_superblock() {
 | 
			
		||||
			md_->commit();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void begin_mappings() {
 | 
			
		||||
			// noop
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void end_mappings() {
 | 
			
		||||
			// noop
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void mapping(pd::block_address cblock,
 | 
			
		||||
				     pd::block_address oblock,
 | 
			
		||||
				     bool dirty) {
 | 
			
		||||
			typename caching::mapping m;
 | 
			
		||||
			m.oblock_ = oblock;
 | 
			
		||||
			m.flags_ = M_VALID;
 | 
			
		||||
 | 
			
		||||
			if (dirty)
 | 
			
		||||
				m.flags_ = m.flags_ | M_DIRTY;
 | 
			
		||||
 | 
			
		||||
			md_->mappings_->set(cblock, m);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void begin_hints() {
 | 
			
		||||
			// noop
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void end_hints() {
 | 
			
		||||
			// noop
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void hint(pd::block_address cblock,
 | 
			
		||||
				  vector<unsigned char> const &data) {
 | 
			
		||||
			md_->hints_->set_hint(cblock, data);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void begin_discards() {
 | 
			
		||||
			// noop
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void end_discards() {
 | 
			
		||||
			// noop
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void discard(block_address dblock, block_address dblock_e) {
 | 
			
		||||
			while (dblock != dblock_e) {
 | 
			
		||||
				md_->discard_bits_->set(dblock, true);
 | 
			
		||||
				dblock++;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		bool in_superblock_;
 | 
			
		||||
		metadata::ptr md_;
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
emitter::ptr
 | 
			
		||||
caching::create_restore_emitter(metadata::ptr md)
 | 
			
		||||
{
 | 
			
		||||
	return emitter::ptr(new restorer(md));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
							
								
								
									
										15
									
								
								caching/restore_emitter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								caching/restore_emitter.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
#ifndef CACHE_RESTORE_EMITTER_H
 | 
			
		||||
#define CACHE_RESTORE_EMITTER_H
 | 
			
		||||
 | 
			
		||||
#include "emitter.h"
 | 
			
		||||
#include "metadata.h"
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace caching {
 | 
			
		||||
	emitter::ptr create_restore_emitter(metadata::ptr md);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										393
									
								
								caching/superblock.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										393
									
								
								caching/superblock.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,393 @@
 | 
			
		||||
#include "caching/superblock.h"
 | 
			
		||||
 | 
			
		||||
using namespace caching;
 | 
			
		||||
using namespace superblock_damage;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	using namespace base;
 | 
			
		||||
 | 
			
		||||
	struct superblock_disk {
 | 
			
		||||
		le32 csum;
 | 
			
		||||
		le32 flags;
 | 
			
		||||
		le64 blocknr;
 | 
			
		||||
 | 
			
		||||
		__u8 uuid[16];
 | 
			
		||||
		le64 magic;
 | 
			
		||||
		le32 version;
 | 
			
		||||
 | 
			
		||||
		__u8 policy_name[CACHE_POLICY_NAME_SIZE];
 | 
			
		||||
		le32 policy_hint_size;
 | 
			
		||||
 | 
			
		||||
		__u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
 | 
			
		||||
 | 
			
		||||
		le64 mapping_root;
 | 
			
		||||
		le64 hint_root;
 | 
			
		||||
 | 
			
		||||
		le64 discard_root;
 | 
			
		||||
		le64 discard_block_size;
 | 
			
		||||
		le64 discard_nr_blocks;
 | 
			
		||||
 | 
			
		||||
		le32 data_block_size; /* in 512-byte sectors */
 | 
			
		||||
		le32 metadata_block_size; /* in 512-byte sectors */
 | 
			
		||||
		le32 cache_blocks;
 | 
			
		||||
 | 
			
		||||
		le32 compat_flags;
 | 
			
		||||
		le32 compat_ro_flags;
 | 
			
		||||
		le32 incompat_flags;
 | 
			
		||||
 | 
			
		||||
		le32 read_hits;
 | 
			
		||||
		le32 read_misses;
 | 
			
		||||
		le32 write_hits;
 | 
			
		||||
		le32 write_misses;
 | 
			
		||||
 | 
			
		||||
		le32 policy_version[CACHE_POLICY_VERSION_SIZE];
 | 
			
		||||
	} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
	struct superblock_traits {
 | 
			
		||||
		typedef superblock_disk disk_type;
 | 
			
		||||
		typedef superblock value_type;
 | 
			
		||||
 | 
			
		||||
		static void unpack(superblock_disk const &disk, superblock &value);
 | 
			
		||||
		static void pack(superblock const &value, superblock_disk &disk);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	uint32_t const SUPERBLOCK_MAGIC = 06142003;
 | 
			
		||||
	uint32_t const VERSION_BEGIN = 1;
 | 
			
		||||
	uint32_t const VERSION_END = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
superblock_flags::superblock_flags()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
superblock_flags::superblock_flags(uint32_t bits)
 | 
			
		||||
{
 | 
			
		||||
	if (bits & (1 << CLEAN_SHUTDOWN_BIT)) {
 | 
			
		||||
		flags_.insert(CLEAN_SHUTDOWN);
 | 
			
		||||
		bits &= ~(1 << CLEAN_SHUTDOWN_BIT);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	unhandled_flags_ = bits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
superblock_flags::set_flag(superblock_flags::flag f)
 | 
			
		||||
{
 | 
			
		||||
	flags_.insert(f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
superblock_flags::get_flag(flag f) const
 | 
			
		||||
{
 | 
			
		||||
	return flags_.find(f) != flags_.end();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t
 | 
			
		||||
superblock_flags::encode() const
 | 
			
		||||
{
 | 
			
		||||
	uint32_t r = 0;
 | 
			
		||||
 | 
			
		||||
	if (get_flag(CLEAN_SHUTDOWN))
 | 
			
		||||
		r = r | (1 << CLEAN_SHUTDOWN_BIT);
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t
 | 
			
		||||
superblock_flags::get_unhandled_flags() const
 | 
			
		||||
{
 | 
			
		||||
	return unhandled_flags_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
superblock::superblock()
 | 
			
		||||
	: csum(0),
 | 
			
		||||
	  blocknr(SUPERBLOCK_LOCATION),
 | 
			
		||||
	  magic(SUPERBLOCK_MAGIC),
 | 
			
		||||
	  version(VERSION_BEGIN),
 | 
			
		||||
	  policy_hint_size(0),
 | 
			
		||||
	  mapping_root(0),
 | 
			
		||||
	  hint_root(0),
 | 
			
		||||
	  discard_root(0),
 | 
			
		||||
	  discard_block_size(0),
 | 
			
		||||
	  discard_nr_blocks(0),
 | 
			
		||||
	  data_block_size(0),
 | 
			
		||||
	  metadata_block_size(8),
 | 
			
		||||
	  cache_blocks(0),
 | 
			
		||||
	  compat_flags(0),
 | 
			
		||||
	  compat_ro_flags(0),
 | 
			
		||||
	  incompat_flags(0),
 | 
			
		||||
	  read_hits(0),
 | 
			
		||||
	  read_misses(0),
 | 
			
		||||
	  write_hits(0),
 | 
			
		||||
	  write_misses(0)
 | 
			
		||||
{
 | 
			
		||||
	::memset(uuid, 0, sizeof(uuid));
 | 
			
		||||
	::memset(policy_name, 0, sizeof(policy_name));
 | 
			
		||||
	::memset(policy_version, 0, sizeof(policy_version));
 | 
			
		||||
	::memset(metadata_space_map_root, 0, sizeof(metadata_space_map_root));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
superblock_traits::unpack(superblock_disk const &disk, superblock &core)
 | 
			
		||||
{
 | 
			
		||||
	core.csum = to_cpu<uint32_t>(disk.csum);
 | 
			
		||||
 | 
			
		||||
	core.flags = superblock_flags(to_cpu<uint32_t>(disk.flags));
 | 
			
		||||
	core.blocknr = to_cpu<uint64_t>(disk.blocknr);
 | 
			
		||||
 | 
			
		||||
	::memcpy(core.uuid, disk.uuid, sizeof(core.uuid));
 | 
			
		||||
	core.magic = to_cpu<uint64_t>(disk.magic);
 | 
			
		||||
	core.version = to_cpu<uint32_t>(disk.version);
 | 
			
		||||
 | 
			
		||||
	::memcpy(core.policy_name, disk.policy_name, sizeof(core.policy_name));
 | 
			
		||||
 | 
			
		||||
	for (unsigned i = 0; i < CACHE_POLICY_VERSION_SIZE; i++)
 | 
			
		||||
		core.policy_version[i] = to_cpu<uint32_t>(disk.policy_version[i]);
 | 
			
		||||
 | 
			
		||||
	core.policy_hint_size = to_cpu<uint32_t>(disk.policy_hint_size);
 | 
			
		||||
 | 
			
		||||
	::memcpy(core.metadata_space_map_root,
 | 
			
		||||
		 disk.metadata_space_map_root,
 | 
			
		||||
		 sizeof(core.metadata_space_map_root));
 | 
			
		||||
 | 
			
		||||
	core.mapping_root = to_cpu<uint64_t>(disk.mapping_root);
 | 
			
		||||
	core.hint_root = to_cpu<uint64_t>(disk.hint_root);
 | 
			
		||||
 | 
			
		||||
	core.discard_root = to_cpu<uint64_t>(disk.discard_root);
 | 
			
		||||
	core.discard_block_size = to_cpu<uint64_t>(disk.discard_block_size);
 | 
			
		||||
	core.discard_nr_blocks = to_cpu<uint64_t>(disk.discard_nr_blocks);
 | 
			
		||||
 | 
			
		||||
	core.data_block_size = to_cpu<uint32_t>(disk.data_block_size);
 | 
			
		||||
	core.metadata_block_size = to_cpu<uint32_t>(disk.metadata_block_size);
 | 
			
		||||
	core.cache_blocks = to_cpu<uint32_t>(disk.cache_blocks);
 | 
			
		||||
 | 
			
		||||
	core.compat_flags = to_cpu<uint32_t>(disk.compat_flags);
 | 
			
		||||
	core.compat_ro_flags = to_cpu<uint32_t>(disk.compat_ro_flags);
 | 
			
		||||
	core.incompat_flags = to_cpu<uint32_t>(disk.incompat_flags);
 | 
			
		||||
 | 
			
		||||
	core.read_hits = to_cpu<uint32_t>(disk.read_hits);
 | 
			
		||||
	core.read_misses = to_cpu<uint32_t>(disk.read_misses);
 | 
			
		||||
	core.write_hits = to_cpu<uint32_t>(disk.write_hits);
 | 
			
		||||
	core.write_misses = to_cpu<uint32_t>(disk.write_misses);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
superblock_traits::pack(superblock const &core, superblock_disk &disk)
 | 
			
		||||
{
 | 
			
		||||
	disk.csum = to_disk<le32>(core.csum);
 | 
			
		||||
	disk.flags = to_disk<le32>(core.flags.encode());
 | 
			
		||||
	disk.blocknr = to_disk<le64>(core.blocknr);
 | 
			
		||||
 | 
			
		||||
	::memcpy(disk.uuid, core.uuid, sizeof(disk.uuid));
 | 
			
		||||
	disk.magic = to_disk<le64>(core.magic);
 | 
			
		||||
	disk.version = to_disk<le32>(core.version);
 | 
			
		||||
 | 
			
		||||
	::memcpy(disk.policy_name, core.policy_name, sizeof(disk.policy_name));
 | 
			
		||||
 | 
			
		||||
	for (unsigned i = 0; i < CACHE_POLICY_VERSION_SIZE; i++)
 | 
			
		||||
		disk.policy_version[i] = to_disk<le32>(core.policy_version[i]);
 | 
			
		||||
 | 
			
		||||
	disk.policy_hint_size = to_disk<le32>(core.policy_hint_size);
 | 
			
		||||
 | 
			
		||||
	::memcpy(disk.metadata_space_map_root,
 | 
			
		||||
		 core.metadata_space_map_root,
 | 
			
		||||
		 sizeof(disk.metadata_space_map_root));
 | 
			
		||||
 | 
			
		||||
	disk.mapping_root = to_disk<le64>(core.mapping_root);
 | 
			
		||||
	disk.hint_root = to_disk<le64>(core.hint_root);
 | 
			
		||||
 | 
			
		||||
	disk.discard_root = to_disk<le64>(core.discard_root);
 | 
			
		||||
	disk.discard_block_size = to_disk<le64>(core.discard_block_size);
 | 
			
		||||
	disk.discard_nr_blocks = to_disk<le64>(core.discard_nr_blocks);
 | 
			
		||||
 | 
			
		||||
	disk.data_block_size = to_disk<le32>(core.data_block_size);
 | 
			
		||||
	disk.metadata_block_size = to_disk<le32>(core.metadata_block_size);
 | 
			
		||||
	disk.cache_blocks = to_disk<le32>(core.cache_blocks);
 | 
			
		||||
 | 
			
		||||
	disk.compat_flags = to_disk<le32>(core.compat_flags);
 | 
			
		||||
	disk.compat_ro_flags = to_disk<le32>(core.compat_ro_flags);
 | 
			
		||||
	disk.incompat_flags = to_disk<le32>(core.incompat_flags);
 | 
			
		||||
 | 
			
		||||
	disk.read_hits = to_disk<le32>(core.read_hits);
 | 
			
		||||
	disk.read_misses = to_disk<le32>(core.read_misses);
 | 
			
		||||
	disk.write_hits = to_disk<le32>(core.write_hits);
 | 
			
		||||
	disk.write_misses = to_disk<le32>(core.write_misses);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//--------------------------------
 | 
			
		||||
 | 
			
		||||
superblock_corrupt::superblock_corrupt(std::string const &desc)
 | 
			
		||||
	: damage(desc)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
superblock_corrupt::visit(damage_visitor &v) const
 | 
			
		||||
{
 | 
			
		||||
	v.visit(*this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
superblock_invalid::superblock_invalid(std::string const &desc)
 | 
			
		||||
	: damage(desc)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
superblock_invalid::visit(damage_visitor &v) const
 | 
			
		||||
{
 | 
			
		||||
	v.visit(*this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//--------------------------------
 | 
			
		||||
 | 
			
		||||
// anonymous namespace doesn't work for some reason
 | 
			
		||||
namespace validator {
 | 
			
		||||
	using namespace persistent_data;
 | 
			
		||||
 | 
			
		||||
        uint32_t const VERSION = 1;
 | 
			
		||||
        unsigned const SECTOR_TO_BLOCK_SHIFT = 3;
 | 
			
		||||
	uint32_t const SUPERBLOCK_CSUM_SEED = 9031977;
 | 
			
		||||
 | 
			
		||||
	struct sb_validator : public block_manager<>::validator {
 | 
			
		||||
		virtual void check(buffer<> const &b, block_address location) const {
 | 
			
		||||
			superblock_disk const *sbd = reinterpret_cast<superblock_disk const *>(&b);
 | 
			
		||||
			crc32c sum(SUPERBLOCK_CSUM_SEED);
 | 
			
		||||
			sum.append(&sbd->flags, MD_BLOCK_SIZE - sizeof(uint32_t));
 | 
			
		||||
			if (sum.get_sum() != to_cpu<uint32_t>(sbd->csum))
 | 
			
		||||
				throw checksum_error("bad checksum in superblock");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void prepare(buffer<> &b, block_address location) const {
 | 
			
		||||
			superblock_disk *sbd = reinterpret_cast<superblock_disk *>(&b);
 | 
			
		||||
			crc32c sum(SUPERBLOCK_CSUM_SEED);
 | 
			
		||||
			sum.append(&sbd->flags, MD_BLOCK_SIZE - sizeof(uint32_t));
 | 
			
		||||
			sbd->csum = to_disk<base::le32>(sum.get_sum());
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	block_manager<>::validator::ptr  mk_v() {
 | 
			
		||||
		return block_manager<>::validator::ptr(new sb_validator);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//--------------------------------
 | 
			
		||||
 | 
			
		||||
superblock
 | 
			
		||||
caching::read_superblock(block_manager<>::ptr bm, block_address location)
 | 
			
		||||
{
 | 
			
		||||
	superblock sb;
 | 
			
		||||
	block_manager<>::read_ref r = bm->read_lock(location, validator::mk_v());
 | 
			
		||||
	superblock_disk const *sbd = reinterpret_cast<superblock_disk const *>(&r.data());
 | 
			
		||||
	superblock_traits::unpack(*sbd, sb);
 | 
			
		||||
 | 
			
		||||
	return sb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
caching::write_superblock(block_manager<>::ptr bm, superblock const &sb, block_address location)
 | 
			
		||||
{
 | 
			
		||||
	block_manager<>::write_ref w = bm->superblock_zero(location, validator::mk_v());
 | 
			
		||||
	superblock_traits::pack(sb, *reinterpret_cast<superblock_disk *>(w.data().raw()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
caching::check_superblock(superblock const &sb,
 | 
			
		||||
			  block_address nr_metadata_blocks,
 | 
			
		||||
			  damage_visitor &visitor)
 | 
			
		||||
{
 | 
			
		||||
	if (sb.flags.get_unhandled_flags()) {
 | 
			
		||||
		ostringstream msg;
 | 
			
		||||
		msg << "invalid flags: " << sb.flags.get_unhandled_flags();
 | 
			
		||||
		visitor.visit(superblock_invalid(msg.str()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sb.blocknr >= nr_metadata_blocks) {
 | 
			
		||||
		ostringstream msg;
 | 
			
		||||
		msg << "blocknr out of bounds: " << sb.blocknr << " >= " << nr_metadata_blocks;
 | 
			
		||||
		visitor.visit(superblock_invalid(msg.str()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sb.magic != SUPERBLOCK_MAGIC) {
 | 
			
		||||
		ostringstream msg;
 | 
			
		||||
		msg << "magic in incorrect: " << sb.magic;
 | 
			
		||||
		visitor.visit(superblock_invalid(msg.str()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sb.version >= VERSION_END) {
 | 
			
		||||
		ostringstream msg;
 | 
			
		||||
		msg << "version incorrect: " << sb.version;
 | 
			
		||||
		visitor.visit(superblock_invalid(msg.str()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sb.version < VERSION_BEGIN) {
 | 
			
		||||
		ostringstream msg;
 | 
			
		||||
		msg << "version incorrect: " << sb.version;
 | 
			
		||||
		visitor.visit(superblock_invalid(msg.str()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (::strnlen((char const *) sb.policy_name, CACHE_POLICY_NAME_SIZE) == CACHE_POLICY_NAME_SIZE) {
 | 
			
		||||
		visitor.visit(superblock_invalid("policy name is not null terminated"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sb.policy_hint_size % 4 || sb.policy_hint_size > 128) {
 | 
			
		||||
		ostringstream msg;
 | 
			
		||||
		msg << "policy hint size invalid: " << sb.policy_hint_size;
 | 
			
		||||
		visitor.visit(superblock_invalid(msg.str()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sb.metadata_block_size != 8) {
 | 
			
		||||
		ostringstream msg;
 | 
			
		||||
		msg << "metadata block size incorrect: " << sb.metadata_block_size;
 | 
			
		||||
		visitor.visit(superblock_invalid(msg.str()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sb.compat_flags != 0) {
 | 
			
		||||
		ostringstream msg;
 | 
			
		||||
		msg << "compat_flags invalid (can only be 0): " << sb.compat_flags;
 | 
			
		||||
		visitor.visit(superblock_invalid(msg.str()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sb.compat_ro_flags != 0) {
 | 
			
		||||
		ostringstream msg;
 | 
			
		||||
		msg << "compat_ro_flags invalid (can only be 0): " << sb.compat_ro_flags;
 | 
			
		||||
		visitor.visit(superblock_invalid(msg.str()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sb.incompat_flags != 0) {
 | 
			
		||||
		ostringstream msg;
 | 
			
		||||
		msg << "incompat_flags invalid (can only be 0): " << sb.incompat_flags;
 | 
			
		||||
		visitor.visit(superblock_invalid(msg.str()));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
caching::check_superblock(persistent_data::block_manager<>::ptr bm,
 | 
			
		||||
			  block_address nr_metadata_blocks,
 | 
			
		||||
			  damage_visitor &visitor)
 | 
			
		||||
{
 | 
			
		||||
	superblock sb;
 | 
			
		||||
 | 
			
		||||
	try {
 | 
			
		||||
		sb = read_superblock(bm, SUPERBLOCK_LOCATION);
 | 
			
		||||
 | 
			
		||||
	} catch (std::exception const &e) {
 | 
			
		||||
 | 
			
		||||
		// FIXME: what if it fails due to a zero length file?  Not
 | 
			
		||||
		// really a corruption, so much as an io error.  Should we
 | 
			
		||||
		// separate these?
 | 
			
		||||
 | 
			
		||||
		visitor.visit(superblock_corrupt(e.what()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	check_superblock(sb, nr_metadata_blocks, visitor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
							
								
								
									
										146
									
								
								caching/superblock.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								caching/superblock.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,146 @@
 | 
			
		||||
#ifndef CACHE_SUPERBLOCK_H
 | 
			
		||||
#define CACHE_SUPERBLOCK_H
 | 
			
		||||
 | 
			
		||||
#include "persistent-data/endian_utils.h"
 | 
			
		||||
#include "persistent-data/data-structures/btree.h"
 | 
			
		||||
 | 
			
		||||
#include <set>
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace caching {
 | 
			
		||||
	typedef unsigned char __u8;
 | 
			
		||||
 | 
			
		||||
	unsigned const SPACE_MAP_ROOT_SIZE = 128;
 | 
			
		||||
	unsigned const CACHE_POLICY_NAME_SIZE = 16;
 | 
			
		||||
	unsigned const CACHE_POLICY_VERSION_SIZE = 3;
 | 
			
		||||
	block_address const SUPERBLOCK_LOCATION = 0;
 | 
			
		||||
 | 
			
		||||
	class superblock_flags {
 | 
			
		||||
	public:
 | 
			
		||||
		enum flag {
 | 
			
		||||
			CLEAN_SHUTDOWN
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		enum flag_bits {
 | 
			
		||||
			CLEAN_SHUTDOWN_BIT = 0
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		superblock_flags();
 | 
			
		||||
		superblock_flags(uint32_t bits);
 | 
			
		||||
 | 
			
		||||
		void set_flag(flag f);
 | 
			
		||||
		bool get_flag(flag f) const;
 | 
			
		||||
		uint32_t encode() const;
 | 
			
		||||
		uint32_t get_unhandled_flags() const;
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		uint32_t unhandled_flags_;
 | 
			
		||||
		std::set<flag> flags_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct superblock {
 | 
			
		||||
		superblock();
 | 
			
		||||
 | 
			
		||||
		uint32_t csum;
 | 
			
		||||
		superblock_flags flags;
 | 
			
		||||
		uint64_t blocknr;
 | 
			
		||||
 | 
			
		||||
		__u8 uuid[16];
 | 
			
		||||
		uint64_t magic;
 | 
			
		||||
		uint32_t version;
 | 
			
		||||
 | 
			
		||||
		__u8 policy_name[CACHE_POLICY_NAME_SIZE];
 | 
			
		||||
		uint32_t policy_version[CACHE_POLICY_VERSION_SIZE];
 | 
			
		||||
		uint32_t policy_hint_size;
 | 
			
		||||
 | 
			
		||||
		__u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
 | 
			
		||||
 | 
			
		||||
		uint64_t mapping_root;
 | 
			
		||||
		uint64_t hint_root;
 | 
			
		||||
 | 
			
		||||
		uint64_t discard_root;
 | 
			
		||||
		uint64_t discard_block_size;
 | 
			
		||||
		uint64_t discard_nr_blocks;
 | 
			
		||||
 | 
			
		||||
		uint32_t data_block_size; /* in 512-byte sectors */
 | 
			
		||||
		uint32_t metadata_block_size; /* in 512-byte sectors */
 | 
			
		||||
		uint32_t cache_blocks;
 | 
			
		||||
 | 
			
		||||
		uint32_t compat_flags;
 | 
			
		||||
		uint32_t compat_ro_flags;
 | 
			
		||||
		uint32_t incompat_flags;
 | 
			
		||||
 | 
			
		||||
		uint32_t read_hits;
 | 
			
		||||
		uint32_t read_misses;
 | 
			
		||||
		uint32_t write_hits;
 | 
			
		||||
		uint32_t write_misses;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
 | 
			
		||||
	namespace superblock_damage {
 | 
			
		||||
 | 
			
		||||
		class damage_visitor;
 | 
			
		||||
 | 
			
		||||
		class damage {
 | 
			
		||||
		public:
 | 
			
		||||
			damage(std::string const &desc)
 | 
			
		||||
				: desc_(desc) {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			virtual ~damage() {}
 | 
			
		||||
			virtual void visit(damage_visitor &v) const = 0;
 | 
			
		||||
 | 
			
		||||
			std::string const &get_desc() const {
 | 
			
		||||
				return desc_;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		private:
 | 
			
		||||
			std::string desc_;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		struct superblock_corrupt : public damage {
 | 
			
		||||
			superblock_corrupt(std::string const &desc);
 | 
			
		||||
			void visit(damage_visitor &v) const;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		struct superblock_invalid : public damage {
 | 
			
		||||
			superblock_invalid(std::string const &desc);
 | 
			
		||||
			void visit(damage_visitor &v) const;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		class damage_visitor {
 | 
			
		||||
		public:
 | 
			
		||||
			virtual ~damage_visitor() {}
 | 
			
		||||
 | 
			
		||||
			void visit(damage const &d);
 | 
			
		||||
 | 
			
		||||
			virtual void visit(superblock_corrupt const &d) = 0;
 | 
			
		||||
			virtual void visit(superblock_invalid const &d) = 0;
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
 | 
			
		||||
	persistent_data::block_manager<>::validator::ptr superblock_validator();
 | 
			
		||||
 | 
			
		||||
	superblock read_superblock(persistent_data::block_manager<>::ptr bm,
 | 
			
		||||
				   persistent_data::block_address location = SUPERBLOCK_LOCATION);
 | 
			
		||||
 | 
			
		||||
	void write_superblock(persistent_data::block_manager<>::ptr bm,
 | 
			
		||||
			      superblock const &sb,
 | 
			
		||||
			      persistent_data::block_address location = SUPERBLOCK_LOCATION);
 | 
			
		||||
 | 
			
		||||
	void check_superblock(superblock const &sb,
 | 
			
		||||
			      persistent_data::block_address nr_metadata_blocks,
 | 
			
		||||
			      superblock_damage::damage_visitor &visitor);
 | 
			
		||||
 | 
			
		||||
	void check_superblock(persistent_data::block_manager<>::ptr bm,
 | 
			
		||||
			      persistent_data::block_address nr_metadata_blocks,
 | 
			
		||||
			      superblock_damage::damage_visitor &visitor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										324
									
								
								caching/xml_format.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										324
									
								
								caching/xml_format.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,324 @@
 | 
			
		||||
#include "base/base64.h"
 | 
			
		||||
#include "caching/xml_format.h"
 | 
			
		||||
 | 
			
		||||
#include <boost/lexical_cast.hpp>
 | 
			
		||||
#include <expat.h>
 | 
			
		||||
 | 
			
		||||
using namespace boost;
 | 
			
		||||
using namespace caching;
 | 
			
		||||
using namespace persistent_data;
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
	// Emitter
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
	class xml_emitter : public emitter {
 | 
			
		||||
	public:
 | 
			
		||||
		xml_emitter(ostream &out)
 | 
			
		||||
		: out_(out),
 | 
			
		||||
		  indent_(0) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void begin_superblock(std::string const &uuid,
 | 
			
		||||
				      block_address block_size,
 | 
			
		||||
				      block_address nr_cache_blocks,
 | 
			
		||||
				      std::string const &policy,
 | 
			
		||||
				      size_t hint_width) {
 | 
			
		||||
			indent();
 | 
			
		||||
			out_ << "<superblock uuid=\"" << uuid << "\""
 | 
			
		||||
			     << " block_size=\"" << block_size << "\""
 | 
			
		||||
			     << " nr_cache_blocks=\"" << nr_cache_blocks << "\""
 | 
			
		||||
			     << " policy=\"" << policy << "\""
 | 
			
		||||
			     << " hint_width=\"" << hint_width << "\">" << endl;
 | 
			
		||||
			inc();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void end_superblock() {
 | 
			
		||||
			dec();
 | 
			
		||||
			indent();
 | 
			
		||||
			out_ << "</superblock>" << endl;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void begin_mappings() {
 | 
			
		||||
			indent();
 | 
			
		||||
			out_ << "<mappings>" << endl;
 | 
			
		||||
			inc();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void end_mappings() {
 | 
			
		||||
			dec();
 | 
			
		||||
			indent();
 | 
			
		||||
			out_ << "</mappings>" << endl;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void mapping(block_address cblock,
 | 
			
		||||
				     block_address oblock,
 | 
			
		||||
				     bool dirty) {
 | 
			
		||||
			indent();
 | 
			
		||||
			out_ << "<mapping"
 | 
			
		||||
			     << " cache_block=\"" << cblock << "\""
 | 
			
		||||
			     << " origin_block=\"" << oblock << "\""
 | 
			
		||||
			     << " dirty=\"" << as_truth(dirty) << "\""
 | 
			
		||||
			     << "/>" << endl;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void begin_hints() {
 | 
			
		||||
			indent();
 | 
			
		||||
			out_ << "<hints>" << endl;
 | 
			
		||||
			inc();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void end_hints() {
 | 
			
		||||
			dec();
 | 
			
		||||
			indent();
 | 
			
		||||
			out_ << "</hints>" << endl;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void hint(block_address cblock,
 | 
			
		||||
				  vector<unsigned char> const &data) {
 | 
			
		||||
			using namespace base;
 | 
			
		||||
 | 
			
		||||
			indent();
 | 
			
		||||
			out_ << "<hint"
 | 
			
		||||
			     << " cache_block=\"" << cblock << "\""
 | 
			
		||||
			     << " data=\"" << base64_encode(data) << "\""
 | 
			
		||||
			     << "/>" << endl;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void begin_discards() {
 | 
			
		||||
			indent();
 | 
			
		||||
			out_ << "<discards>" << endl;
 | 
			
		||||
			inc();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void end_discards() {
 | 
			
		||||
			dec();
 | 
			
		||||
			indent();
 | 
			
		||||
			out_ << "</discards>" << endl;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void discard(block_address dblock_b, block_address dblock_e) {
 | 
			
		||||
			indent();
 | 
			
		||||
			out_ << "<discard dbegin=\"" << dblock_b << "\""
 | 
			
		||||
			     << " dend=\"" << dblock_e << "\"/>" << endl;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		string as_truth(bool v) const {
 | 
			
		||||
			return v ? "true" : "false";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// FIXME: factor out a common class with the thin_provisioning emitter
 | 
			
		||||
		void indent() {
 | 
			
		||||
			for (unsigned i = 0; i < indent_ * 2; i++)
 | 
			
		||||
				out_ << ' ';
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void inc() {
 | 
			
		||||
			indent_++;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void dec() {
 | 
			
		||||
			indent_--;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ostream &out_;
 | 
			
		||||
		unsigned indent_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
	// Parser
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
 | 
			
		||||
	// FIXME: factor out common code with thinp one
 | 
			
		||||
	typedef std::map<string, string> attributes;
 | 
			
		||||
 | 
			
		||||
	void build_attributes(attributes &a, char const **attr) {
 | 
			
		||||
		while (*attr) {
 | 
			
		||||
			char const *key = *attr;
 | 
			
		||||
 | 
			
		||||
			attr++;
 | 
			
		||||
			if (!*attr) {
 | 
			
		||||
				ostringstream out;
 | 
			
		||||
				out << "No value given for xml attribute: " << key;
 | 
			
		||||
				throw runtime_error(out.str());
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			char const *value = *attr;
 | 
			
		||||
			a.insert(make_pair(string(key), string(value)));
 | 
			
		||||
			attr++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template <typename T>
 | 
			
		||||
	T get_attr(attributes const &attr, string const &key) {
 | 
			
		||||
		attributes::const_iterator it = attr.find(key);
 | 
			
		||||
		if (it == attr.end()) {
 | 
			
		||||
			ostringstream out;
 | 
			
		||||
			out << "could not find attribute: " << key;
 | 
			
		||||
			throw runtime_error(out.str());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return boost::lexical_cast<T>(it->second);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template <typename T>
 | 
			
		||||
	boost::optional<T> get_opt_attr(attributes const &attr, string const &key) {
 | 
			
		||||
		typedef boost::optional<T> rtype;
 | 
			
		||||
		attributes::const_iterator it = attr.find(key);
 | 
			
		||||
		if (it == attr.end())
 | 
			
		||||
			return rtype();
 | 
			
		||||
 | 
			
		||||
		return rtype(boost::lexical_cast<T>(it->second));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void parse_superblock(emitter *e, attributes const &attr) {
 | 
			
		||||
		e->begin_superblock(get_attr<string>(attr, "uuid"),
 | 
			
		||||
				    get_attr<uint64_t>(attr, "block_size"),
 | 
			
		||||
				    get_attr<uint64_t>(attr, "nr_cache_blocks"),
 | 
			
		||||
				    get_attr<string>(attr, "policy"),
 | 
			
		||||
				    get_attr<size_t>(attr, "hint_width"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool to_bool(string const &str) {
 | 
			
		||||
		if (str == "true")
 | 
			
		||||
			return true;
 | 
			
		||||
 | 
			
		||||
		else if (str == "false")
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		throw runtime_error("bad boolean value");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void parse_mapping(emitter *e, attributes const &attr) {
 | 
			
		||||
		e->mapping(get_attr<uint64_t>(attr, "cache_block"),
 | 
			
		||||
			   get_attr<uint64_t>(attr, "origin_block"),
 | 
			
		||||
			   to_bool(get_attr<string>(attr, "dirty")));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void parse_hint(emitter *e, attributes const &attr) {
 | 
			
		||||
		using namespace base;
 | 
			
		||||
 | 
			
		||||
		block_address cblock = get_attr<uint64_t>(attr, "cache_block");
 | 
			
		||||
		decoded_or_error doe = base64_decode(get_attr<string>(attr, "data"));
 | 
			
		||||
		if (!get<vector<unsigned char> >(&doe)) {
 | 
			
		||||
			ostringstream msg;
 | 
			
		||||
			msg << "invalid base64 encoding of hint for cache block "
 | 
			
		||||
			    << cblock << ": " << get<string>(doe);
 | 
			
		||||
			throw runtime_error(msg.str());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		e->hint(cblock, get<vector<unsigned char> >(doe));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// FIXME: why passing e by ptr?
 | 
			
		||||
	void parse_discard(emitter *e, attributes const &attr) {
 | 
			
		||||
		e->discard(get_attr<uint64_t>(attr, "dbegin"),
 | 
			
		||||
			   get_attr<uint64_t>(attr, "dend"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void start_tag(void *data, char const *el, char const **attr) {
 | 
			
		||||
		emitter *e = static_cast<emitter *>(data);
 | 
			
		||||
		attributes a;
 | 
			
		||||
 | 
			
		||||
		build_attributes(a, attr);
 | 
			
		||||
 | 
			
		||||
		if (!strcmp(el, "superblock"))
 | 
			
		||||
			parse_superblock(e, a);
 | 
			
		||||
 | 
			
		||||
		else if (!strcmp(el, "mappings"))
 | 
			
		||||
			e->begin_mappings();
 | 
			
		||||
 | 
			
		||||
		else if (!strcmp(el, "mapping"))
 | 
			
		||||
			parse_mapping(e, a);
 | 
			
		||||
 | 
			
		||||
		else if (!strcmp(el, "hints"))
 | 
			
		||||
			e->begin_hints();
 | 
			
		||||
 | 
			
		||||
		else if (!strcmp(el, "hint"))
 | 
			
		||||
			parse_hint(e, a);
 | 
			
		||||
 | 
			
		||||
		else if (!strcmp(el, "discards"))
 | 
			
		||||
			e->begin_discards();
 | 
			
		||||
 | 
			
		||||
		else if (!strcmp(el, "discard"))
 | 
			
		||||
			parse_discard(e, a);
 | 
			
		||||
 | 
			
		||||
		else
 | 
			
		||||
			throw runtime_error("unknown tag type");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void end_tag(void *data, const char *el) {
 | 
			
		||||
		emitter *e = static_cast<emitter *>(data);
 | 
			
		||||
 | 
			
		||||
		if (!strcmp(el, "superblock"))
 | 
			
		||||
			e->end_superblock();
 | 
			
		||||
 | 
			
		||||
		else if (!strcmp(el, "mappings"))
 | 
			
		||||
			e->end_mappings();
 | 
			
		||||
 | 
			
		||||
		else if (!strcmp(el, "mapping"))
 | 
			
		||||
			// do nothing
 | 
			
		||||
			;
 | 
			
		||||
 | 
			
		||||
		else if (!strcmp(el, "hints"))
 | 
			
		||||
			e->end_hints();
 | 
			
		||||
 | 
			
		||||
		else if (!strcmp(el, "hint"))
 | 
			
		||||
			// do nothing
 | 
			
		||||
			;
 | 
			
		||||
 | 
			
		||||
		else if (!strcmp(el, "discards"))
 | 
			
		||||
			e->end_discards();
 | 
			
		||||
 | 
			
		||||
		else if (!strcmp(el, "discard"))
 | 
			
		||||
			// do nothing
 | 
			
		||||
			;
 | 
			
		||||
 | 
			
		||||
		else
 | 
			
		||||
			throw runtime_error("unknown tag close");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
caching::emitter::ptr
 | 
			
		||||
caching::create_xml_emitter(ostream &out)
 | 
			
		||||
{
 | 
			
		||||
	return emitter::ptr(new xml_emitter(out));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
caching::parse_xml(istream &in, emitter::ptr e)
 | 
			
		||||
{
 | 
			
		||||
	XML_Parser parser = XML_ParserCreate(NULL);
 | 
			
		||||
	if (!parser)
 | 
			
		||||
		throw runtime_error("couldn't create xml parser");
 | 
			
		||||
 | 
			
		||||
	XML_SetUserData(parser, e.get());
 | 
			
		||||
	XML_SetElementHandler(parser, start_tag, end_tag);
 | 
			
		||||
 | 
			
		||||
	while (!in.eof()) {
 | 
			
		||||
		char buffer[4096];
 | 
			
		||||
		in.read(buffer, sizeof(buffer));
 | 
			
		||||
		size_t len = in.gcount();
 | 
			
		||||
		int done = in.eof();
 | 
			
		||||
 | 
			
		||||
		if (!XML_Parse(parser, buffer, len, done)) {
 | 
			
		||||
			ostringstream out;
 | 
			
		||||
			out << "Parse error at line "
 | 
			
		||||
			    << XML_GetCurrentLineNumber(parser)
 | 
			
		||||
			    << ":\n"
 | 
			
		||||
			    << XML_ErrorString(XML_GetErrorCode(parser))
 | 
			
		||||
			    << endl;
 | 
			
		||||
			throw runtime_error(out.str());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
							
								
								
									
										17
									
								
								caching/xml_format.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								caching/xml_format.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
#ifndef CACHE_XML_FORMAT_H
 | 
			
		||||
#define CACHE_XML_FORMAT_H
 | 
			
		||||
 | 
			
		||||
#include "emitter.h"
 | 
			
		||||
 | 
			
		||||
#include <iosfwd>
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace caching {
 | 
			
		||||
	emitter::ptr create_xml_emitter(std::ostream &out);
 | 
			
		||||
	void parse_xml(std::istream &in, emitter::ptr e);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -2,14 +2,12 @@ Feature: cache_check
 | 
			
		||||
  Scenario: print version (-V flag)
 | 
			
		||||
    When I run `cache_check -V`
 | 
			
		||||
    
 | 
			
		||||
    Then it should pass
 | 
			
		||||
    And version to stdout
 | 
			
		||||
    Then it should pass with version
 | 
			
		||||
 | 
			
		||||
  Scenario: print version (--version flag)
 | 
			
		||||
    When I run `cache_check --version`
 | 
			
		||||
 | 
			
		||||
    Then it should pass
 | 
			
		||||
    And version to stdout
 | 
			
		||||
    Then it should pass with version
 | 
			
		||||
 | 
			
		||||
  Scenario: print help
 | 
			
		||||
    When I run `cache_check --help`
 | 
			
		||||
@@ -56,31 +54,35 @@ Feature: cache_check
 | 
			
		||||
 | 
			
		||||
  Scenario: Metadata file exists, but can't be opened
 | 
			
		||||
    Given input without read permissions
 | 
			
		||||
 | 
			
		||||
    When I run `cache_check input`
 | 
			
		||||
 | 
			
		||||
    Then it should fail
 | 
			
		||||
    And the stderr should contain:
 | 
			
		||||
    """
 | 
			
		||||
    input: Permission denied
 | 
			
		||||
    Permission denied
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
  Scenario: Metadata file full of zeroes
 | 
			
		||||
    Given input file
 | 
			
		||||
    And block 1 is zeroed
 | 
			
		||||
 | 
			
		||||
    When I run `cache_check input`
 | 
			
		||||
    Then it should fail
 | 
			
		||||
 | 
			
		||||
    And the stderr should contain:
 | 
			
		||||
    """
 | 
			
		||||
    input: No superblock found
 | 
			
		||||
    """
 | 
			
		||||
  Scenario: --quiet is observed
 | 
			
		||||
    Given input file
 | 
			
		||||
    And block 1 is zeroed
 | 
			
		||||
    When I run `cache_check --quiet input`
 | 
			
		||||
    Then it should fail
 | 
			
		||||
    And it should give no output
 | 
			
		||||
 | 
			
		||||
  Scenario: -q is observed
 | 
			
		||||
    Given input file
 | 
			
		||||
    And block 1 is zeroed
 | 
			
		||||
    When I run `cache_check -q input`
 | 
			
		||||
    Then it should fail
 | 
			
		||||
    And it should give no output
 | 
			
		||||
 | 
			
		||||
  Scenario: A valid metadata area passes
 | 
			
		||||
    Given metadata containing:
 | 
			
		||||
    """
 | 
			
		||||
    """
 | 
			
		||||
    Given valid cache metadata
 | 
			
		||||
    When I run `cache_check metadata.bin`
 | 
			
		||||
    Then it should pass
 | 
			
		||||
 | 
			
		||||
    When I run cache_check
 | 
			
		||||
 | 
			
		||||
    Then it should pass
 | 
			
		||||
							
								
								
									
										50
									
								
								features/cache_dump.feature
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								features/cache_dump.feature
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
Feature: cache_dump
 | 
			
		||||
  Scenario: print version (-V flag)
 | 
			
		||||
    When I run cache_dump with -V
 | 
			
		||||
    Then it should pass with version
 | 
			
		||||
 | 
			
		||||
  Scenario: print version (--version flag)
 | 
			
		||||
    When I run cache_dump with --version
 | 
			
		||||
    Then it should pass with version
 | 
			
		||||
 | 
			
		||||
  @announce
 | 
			
		||||
  Scenario: print help (-h)
 | 
			
		||||
    When I run cache_dump with -h
 | 
			
		||||
    Then it should pass with:
 | 
			
		||||
    """
 | 
			
		||||
    Usage: cache_dump [options] {device|file}
 | 
			
		||||
    Options:
 | 
			
		||||
      {-h|--help}
 | 
			
		||||
      {-o <xml file>}
 | 
			
		||||
      {-V|--version}
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
  Scenario: print help (--help)
 | 
			
		||||
    When I run cache_dump with -h
 | 
			
		||||
    Then it should pass with:
 | 
			
		||||
    """
 | 
			
		||||
    Usage: cache_dump [options] {device|file}
 | 
			
		||||
    Options:
 | 
			
		||||
      {-h|--help}
 | 
			
		||||
      {-o <xml file>}
 | 
			
		||||
      {-V|--version}
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
  Scenario: accepts an output file
 | 
			
		||||
    Given valid cache metadata
 | 
			
		||||
    When I run cache_dump with -o metadata.xml metadata.bin
 | 
			
		||||
    Then it should pass
 | 
			
		||||
 | 
			
		||||
  Scenario: missing input file
 | 
			
		||||
    When I run cache_dump
 | 
			
		||||
    Then it should fail with:
 | 
			
		||||
    """
 | 
			
		||||
    No input file provided.
 | 
			
		||||
    """  
 | 
			
		||||
 | 
			
		||||
  Scenario: dump/restore is a noop
 | 
			
		||||
    Given valid cache metadata
 | 
			
		||||
    When I cache_dump
 | 
			
		||||
    And I cache_restore
 | 
			
		||||
    And I cache_dump
 | 
			
		||||
    Then cache dumps 1 and 2 should be identical
 | 
			
		||||
							
								
								
									
										61
									
								
								features/cache_restore.feature
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								features/cache_restore.feature
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
Feature: thin_restore
 | 
			
		||||
  Scenario: print version (-V flag)
 | 
			
		||||
    When I run cache_restore with -V
 | 
			
		||||
    Then it should pass with version
 | 
			
		||||
 | 
			
		||||
  Scenario: print version (--version flag)
 | 
			
		||||
    When I run cache_restore with --version
 | 
			
		||||
    Then it should pass with version
 | 
			
		||||
 | 
			
		||||
  Scenario: print help (-h)
 | 
			
		||||
    When I run cache_restore with -h
 | 
			
		||||
    Then it should pass with:
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    Usage: cache_restore [options]
 | 
			
		||||
    Options:
 | 
			
		||||
      {-h|--help}
 | 
			
		||||
      {-i|--input} <input xml file>
 | 
			
		||||
      {-o|--output} <output device or file>
 | 
			
		||||
      {-V|--version}
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
  Scenario: print help (--help)
 | 
			
		||||
    When I run cache_restore with -h
 | 
			
		||||
    Then it should pass with:
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    Usage: cache_restore [options]
 | 
			
		||||
    Options:
 | 
			
		||||
      {-h|--help}
 | 
			
		||||
      {-i|--input} <input xml file>
 | 
			
		||||
      {-o|--output} <output device or file>
 | 
			
		||||
      {-V|--version}
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
  Scenario: missing input file
 | 
			
		||||
    Given the dev file metadata.bin
 | 
			
		||||
    When I run cache_restore with -o metadata.bin
 | 
			
		||||
    Then it should fail with:
 | 
			
		||||
    """
 | 
			
		||||
    No input file provided.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
  Scenario: input file not found
 | 
			
		||||
    Given the dev file metadata.bin
 | 
			
		||||
    When I run cache_restore with -i foo.xml -o metadata.bin
 | 
			
		||||
    Then it should fail
 | 
			
		||||
 | 
			
		||||
  Scenario: missing output file
 | 
			
		||||
    When I run cache_restore with -i metadata.xml
 | 
			
		||||
    Then it should fail with:
 | 
			
		||||
    """
 | 
			
		||||
    No output file provided.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
  @announce
 | 
			
		||||
  Scenario: successfully restores a valid xml file
 | 
			
		||||
    Given a small xml file
 | 
			
		||||
    And an empty dev file
 | 
			
		||||
    When I run cache_restore with -i metadata.xml -o metadata.bin
 | 
			
		||||
    Then it should pass
 | 
			
		||||
@@ -1,57 +0,0 @@
 | 
			
		||||
DEFAULT_INPUT = 'input'
 | 
			
		||||
BLOCK_SIZE = 4096
 | 
			
		||||
 | 
			
		||||
Given /^a directory called (.*)\s*$/ do |dir|
 | 
			
		||||
  create_dir(dir)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Given /^input without read permissions$/ do
 | 
			
		||||
  write_file(DEFAULT_INPUT, "\0" * 4096)
 | 
			
		||||
  in_current_dir do
 | 
			
		||||
    f = File.new(DEFAULT_INPUT)
 | 
			
		||||
    f.chmod(0000)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Given(/^input file$/) do
 | 
			
		||||
  write_file(DEFAULT_INPUT, "\0" * BLOCK_SIZE * 1024)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Given(/^block (\d+) is zeroed$/) do |b|
 | 
			
		||||
  in_current_dir do
 | 
			
		||||
    File.open(DEFAULT_INPUT, 'w') do |f|
 | 
			
		||||
      f.seek(BLOCK_SIZE * b.to_i, IO::SEEK_SET)
 | 
			
		||||
      f.write("\0" * BLOCK_SIZE)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Then /^it should pass$/ do
 | 
			
		||||
  assert_success(true)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Then /^it should fail$/ do
 | 
			
		||||
  assert_success(false)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
VERSION="0.1.6\n"
 | 
			
		||||
 | 
			
		||||
Then /^version to stdout$/ do
 | 
			
		||||
  assert_exact_output(VERSION, all_stdout)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
USAGE =<<EOF
 | 
			
		||||
Usage: cache_check [options] {device|file}
 | 
			
		||||
Options:
 | 
			
		||||
  {-q|--quiet}
 | 
			
		||||
  {-h|--help}
 | 
			
		||||
  {-V|--version}
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
Then /^usage to stdout$/ do
 | 
			
		||||
  assert_partial_output(USAGE, all_stdout)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Then /^usage to stderr$/ do
 | 
			
		||||
  assert_partial_output(USAGE, all_stderr)
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										101
									
								
								features/step_definitions/cache_steps.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								features/step_definitions/cache_steps.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,101 @@
 | 
			
		||||
DEFAULT_INPUT = 'input'
 | 
			
		||||
BLOCK_SIZE = 4096
 | 
			
		||||
 | 
			
		||||
Given /^a directory called (.*)\s*$/ do |dir|
 | 
			
		||||
  create_dir(dir)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Given /^input without read permissions$/ do
 | 
			
		||||
  write_file(DEFAULT_INPUT, "\0" * 4096)
 | 
			
		||||
  in_current_dir do
 | 
			
		||||
    f = File.new(DEFAULT_INPUT)
 | 
			
		||||
    f.chmod(0000)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Given(/^input file$/) do
 | 
			
		||||
  write_file(DEFAULT_INPUT, "\0" * BLOCK_SIZE * 1024)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Given(/^block (\d+) is zeroed$/) do |b|
 | 
			
		||||
  in_current_dir do
 | 
			
		||||
    File.open(DEFAULT_INPUT, 'w') do |f|
 | 
			
		||||
      f.seek(BLOCK_SIZE * b.to_i, IO::SEEK_SET)
 | 
			
		||||
      f.write("\0" * BLOCK_SIZE)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Then /^it should pass$/ do
 | 
			
		||||
  assert_success(true)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Then /^it should fail$/ do
 | 
			
		||||
  assert_success(false)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
USAGE =<<EOF
 | 
			
		||||
Usage: cache_check [options] {device|file}
 | 
			
		||||
Options:
 | 
			
		||||
  {-q|--quiet}
 | 
			
		||||
  {-h|--help}
 | 
			
		||||
  {-V|--version}
 | 
			
		||||
  {--super-block-only}
 | 
			
		||||
  {--skip-mappings}
 | 
			
		||||
  {--skip-hints}
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
Then /^usage to stdout$/ do
 | 
			
		||||
  assert_partial_output(USAGE, all_stdout)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Then /^usage to stderr$/ do
 | 
			
		||||
  assert_partial_output(USAGE, all_stderr)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
When(/^I run cache_check with (.*?)$/) do |opts|
 | 
			
		||||
  run_simple("cache_check #{opts} #{dev_file}", false)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
When(/^I run cache_restore with (.*?)$/) do |opts|
 | 
			
		||||
  run_simple("cache_restore #{opts}", false)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
When(/^I run cache_dump$/) do
 | 
			
		||||
  run_simple("cache_dump", false)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
When(/^I run cache_dump with (.*?)$/) do |opts|
 | 
			
		||||
  run_simple("cache_dump #{opts}", false)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Given(/^valid cache metadata$/) do
 | 
			
		||||
  in_current_dir do
 | 
			
		||||
    system("cache_xml create --nr-cache-blocks uniform[1000..5000] --nr-mappings uniform[500..1000] > #{xml_file}")
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  run_simple("dd if=/dev/zero of=#{dev_file} bs=4k count=1024")
 | 
			
		||||
  run_simple("cache_restore -i #{xml_file} -o #{dev_file}")
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Then(/^cache dumps (\d+) and (\d+) should be identical$/) do |d1, d2|
 | 
			
		||||
  run_simple("diff -ub #{dump_files[d1.to_i]} #{dump_files[d2.to_i]}", true)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Given(/^a small xml file$/) do
 | 
			
		||||
  in_current_dir do
 | 
			
		||||
    system("cache_xml create --nr-cache-blocks 3 --nr-mappings 3 --layout linear --dirty-percent 100 > #{xml_file}")
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Given(/^an empty dev file$/) do
 | 
			
		||||
  run_simple("dd if=/dev/zero of=#{dev_file} bs=4k count=1024")
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
When(/^I cache_dump$/) do
 | 
			
		||||
  run_simple("cache_dump #{dev_file} -o #{new_dump_file}", true)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
When(/^I cache_restore$/) do
 | 
			
		||||
  run_simple("cache_restore -i #{dump_files[-1]} -o #{dev_file}", true)
 | 
			
		||||
end
 | 
			
		||||
@@ -7,6 +7,10 @@ Given(/^valid metadata$/) do
 | 
			
		||||
  run_simple("thin_restore -i #{xml_file} -o #{dev_file}")
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Given(/^the dev file metadata\.bin$/) do
 | 
			
		||||
  run_simple("dd if=/dev/zero of=#{dev_file} bs=4k count=1024")
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Given(/^a corrupt superblock$/) do
 | 
			
		||||
  in_current_dir do
 | 
			
		||||
    write_valid_xml(xml_file)
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ module ThinpWorld
 | 
			
		||||
  # FIXME: we should really break out the xml stuff from
 | 
			
		||||
  # thinp-test-suite and put in a gem in this repo.
 | 
			
		||||
  def write_valid_xml(path)
 | 
			
		||||
    `thinp_xml create --nr-thins uniform[4..9] --nr-mappings uniform[1000.5000] > #{path}`
 | 
			
		||||
    `thinp_xml create --nr-thins uniform[4..9] --nr-mappings uniform[1000..5000] > #{path}`
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 
 | 
			
		||||
@@ -34,12 +34,25 @@ Feature: thin_restore
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
  Scenario: missing input file
 | 
			
		||||
    Given the dev file metadata.bin
 | 
			
		||||
    When I run thin_restore with -o metadata.bin
 | 
			
		||||
    Then it should fail with:
 | 
			
		||||
    """
 | 
			
		||||
    No input file provided.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
  Scenario: input file not found
 | 
			
		||||
    Given the dev file metadata.bin
 | 
			
		||||
    When I run thin_restore with -i foo.xml -o metadata.bin
 | 
			
		||||
    Then it should fail
 | 
			
		||||
 | 
			
		||||
  Scenario: missing output file
 | 
			
		||||
    When I run thin_restore with -i metadata.xml
 | 
			
		||||
    Then it should fail with:
 | 
			
		||||
    """
 | 
			
		||||
    No output file provided.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
  Scenario: dump/restore is a noop
 | 
			
		||||
    Given valid metadata
 | 
			
		||||
    When I dump
 | 
			
		||||
 
 | 
			
		||||
@@ -65,7 +65,6 @@ namespace persistent_data {
 | 
			
		||||
		int fd_;
 | 
			
		||||
		block_address nr_blocks_;
 | 
			
		||||
		mode mode_;
 | 
			
		||||
		bool writeable_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	template <uint32_t BlockSize = MD_BLOCK_SIZE>
 | 
			
		||||
 
 | 
			
		||||
@@ -38,8 +38,8 @@ namespace {
 | 
			
		||||
 | 
			
		||||
	int const DEFAULT_MODE = 0666;
 | 
			
		||||
 | 
			
		||||
	 // FIXME: these will slow it down until we start doing asyn io.  O_DIRECT | O_SYNC;
 | 
			
		||||
	int const OPEN_FLAGS = 0;
 | 
			
		||||
	 // FIXME: these will slow it down until we start doing async io.
 | 
			
		||||
	int const OPEN_FLAGS = O_DIRECT | O_SYNC;
 | 
			
		||||
 | 
			
		||||
	// FIXME: introduce a new exception for this, or at least lift this
 | 
			
		||||
	// to exception.h
 | 
			
		||||
@@ -61,7 +61,7 @@ namespace {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool file_exists(string const &path) {
 | 
			
		||||
		typename ::stat info;
 | 
			
		||||
		struct ::stat info;
 | 
			
		||||
 | 
			
		||||
		int r = ::stat(path.c_str(), &info);
 | 
			
		||||
		if (r) {
 | 
			
		||||
 
 | 
			
		||||
@@ -68,15 +68,29 @@ namespace base {
 | 
			
		||||
				  v_(v) {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			struct {
 | 
			
		||||
				value_entry *next_, *prev_;
 | 
			
		||||
			} lru_;
 | 
			
		||||
			struct lru {
 | 
			
		||||
				lru()
 | 
			
		||||
					: next_(0),
 | 
			
		||||
					  prev_(0) {
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				value_entry *next_, *prev_;
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			struct lookup {
 | 
			
		||||
				lookup()
 | 
			
		||||
					: parent_(0),
 | 
			
		||||
					  left_(0),
 | 
			
		||||
					  right_(0),
 | 
			
		||||
					  color_() {
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			struct {
 | 
			
		||||
				value_entry *parent_, *left_, *right_;
 | 
			
		||||
				int color_;
 | 
			
		||||
			} lookup_;
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			lru lru_;
 | 
			
		||||
			lookup lookup_;
 | 
			
		||||
			unsigned ref_count_;
 | 
			
		||||
			value_type v_;
 | 
			
		||||
		};
 | 
			
		||||
 
 | 
			
		||||
@@ -21,17 +21,15 @@
 | 
			
		||||
 | 
			
		||||
#include "persistent-data/math_utils.h"
 | 
			
		||||
#include "persistent-data/data-structures/btree.h"
 | 
			
		||||
#include "persistent-data/data-structures/btree_damage_visitor.h"
 | 
			
		||||
#include "persistent-data/data-structures/array_block.h"
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// FIXME: we need an array checker
 | 
			
		||||
 | 
			
		||||
namespace persistent_data {
 | 
			
		||||
	namespace array_detail {
 | 
			
		||||
		uint32_t const ARRAY_CSUM_XOR = 595846735;
 | 
			
		||||
 | 
			
		||||
		// FIXME: this isn't used!
 | 
			
		||||
		struct array_block_validator : public block_manager<>::validator {
 | 
			
		||||
			virtual void check(buffer<> const &b, block_address location) const {
 | 
			
		||||
				array_block_disk const *data = reinterpret_cast<array_block_disk const *>(&b);
 | 
			
		||||
@@ -65,10 +63,36 @@ namespace persistent_data {
 | 
			
		||||
			unsigned nr_entries_in_last_block;
 | 
			
		||||
			unsigned nr_total_blocks;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		struct damage {
 | 
			
		||||
			typedef boost::shared_ptr<damage> ptr;
 | 
			
		||||
 | 
			
		||||
			damage(run<uint32_t> lost_keys,
 | 
			
		||||
			       std::string const &desc)
 | 
			
		||||
				: lost_keys_(lost_keys),
 | 
			
		||||
				  desc_(desc) {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			run<uint32_t> lost_keys_;
 | 
			
		||||
			std::string desc_;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		inline std::ostream &operator <<(std::ostream &out, damage const &d) {
 | 
			
		||||
			out << "array damage[lost_keys = " << d.lost_keys_
 | 
			
		||||
			    << ", \"" << d.desc_ << "\"]";
 | 
			
		||||
			return out;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	class array_base {
 | 
			
		||||
	public:
 | 
			
		||||
		virtual ~array_base() {}
 | 
			
		||||
		virtual void set_root(block_address root) = 0;
 | 
			
		||||
		virtual block_address get_root() const = 0;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	template <typename ValueTraits>
 | 
			
		||||
	class array {
 | 
			
		||||
	class array : public array_base {
 | 
			
		||||
	public:
 | 
			
		||||
		class block_ref_counter : public ref_counter<uint64_t> {
 | 
			
		||||
		public:
 | 
			
		||||
@@ -118,6 +142,60 @@ namespace persistent_data {
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		template <typename ValueVisitor>
 | 
			
		||||
		struct block_value_visitor {
 | 
			
		||||
			block_value_visitor(array<ValueTraits> const &a, ValueVisitor &vv)
 | 
			
		||||
				: a_(a),
 | 
			
		||||
				  vv_(vv) {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			void visit(btree_path const &p,
 | 
			
		||||
				   typename block_traits::value_type const &v) {
 | 
			
		||||
				a_.visit_value(vv_, p, v);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		private:
 | 
			
		||||
			array<ValueTraits> const &a_;
 | 
			
		||||
			ValueVisitor &vv_;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		template <typename ValueVisitor>
 | 
			
		||||
		void visit_value(ValueVisitor &vv,
 | 
			
		||||
				 btree_path const &p,
 | 
			
		||||
				 typename block_traits::value_type const &v) const {
 | 
			
		||||
			rblock rb(tm_->read_lock(v, validator_), rc_);
 | 
			
		||||
 | 
			
		||||
			for (uint32_t i = 0; i < rb.nr_entries(); i++)
 | 
			
		||||
				vv.visit(p[0] * rb.max_entries() + i, rb.get(i));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		template <typename DamageVisitor>
 | 
			
		||||
		struct block_damage_visitor {
 | 
			
		||||
			block_damage_visitor(DamageVisitor &dv, unsigned entries_per_block)
 | 
			
		||||
				: dv_(dv),
 | 
			
		||||
				  entries_per_block_(entries_per_block) {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			void visit(btree_path const &path, btree_detail::damage const &d) {
 | 
			
		||||
				dv_.visit(array_detail::damage(convert_run(d.lost_keys_), d.desc_));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		private:
 | 
			
		||||
			run<uint32_t>::maybe convert_maybe(run<uint64_t>::maybe const &v) const {
 | 
			
		||||
				if (v)
 | 
			
		||||
					return run<uint32_t>::maybe(*v * entries_per_block_);
 | 
			
		||||
 | 
			
		||||
				return run<uint32_t>::maybe();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			run<uint32_t> convert_run(run<uint64_t> const &v) const {
 | 
			
		||||
				return run<uint32_t>(convert_maybe(v.begin_), convert_maybe(v.end_));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			DamageVisitor &dv_;
 | 
			
		||||
			unsigned entries_per_block_;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		typedef typename persistent_data::transaction_manager::ptr tm_ptr;
 | 
			
		||||
 | 
			
		||||
		typedef block_manager<>::write_ref write_ref;
 | 
			
		||||
@@ -128,20 +206,19 @@ namespace persistent_data {
 | 
			
		||||
 | 
			
		||||
		typedef boost::shared_ptr<array<ValueTraits> > ptr;
 | 
			
		||||
		typedef typename ValueTraits::value_type value_type;
 | 
			
		||||
		typedef typename ValueTraits::ref_counter ref_counter;
 | 
			
		||||
 | 
			
		||||
		array(tm_ptr tm,
 | 
			
		||||
		      typename ValueTraits::ref_counter rc)
 | 
			
		||||
		array(tm_ptr tm, ref_counter rc)
 | 
			
		||||
			: tm_(tm),
 | 
			
		||||
			  entries_per_block_(rblock::calc_max_entries()),
 | 
			
		||||
			  nr_entries_(0),
 | 
			
		||||
			  block_rc_(tm->get_sm(), *this),
 | 
			
		||||
			  block_tree_(tm, block_rc_),
 | 
			
		||||
			  rc_(rc),
 | 
			
		||||
			  validator_(new block_manager<>::noop_validator()) {
 | 
			
		||||
			  validator_(new array_detail::array_block_validator) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		array(tm_ptr tm,
 | 
			
		||||
		      typename ValueTraits::ref_counter rc,
 | 
			
		||||
		array(tm_ptr tm, ref_counter rc,
 | 
			
		||||
		      block_address root,
 | 
			
		||||
		      unsigned nr_entries)
 | 
			
		||||
			: tm_(tm),
 | 
			
		||||
@@ -150,7 +227,7 @@ namespace persistent_data {
 | 
			
		||||
			  block_rc_(tm->get_sm(), *this),
 | 
			
		||||
			  block_tree_(tm, root, block_rc_),
 | 
			
		||||
			  rc_(rc),
 | 
			
		||||
			  validator_(new block_manager<>::noop_validator()) {
 | 
			
		||||
			  validator_(new array_detail::array_block_validator) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		unsigned get_nr_entries() const {
 | 
			
		||||
@@ -185,6 +262,15 @@ namespace persistent_data {
 | 
			
		||||
			b.set(index % entries_per_block_, value);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		template <typename ValueVisitor, typename DamageVisitor>
 | 
			
		||||
		void visit_values(ValueVisitor &value_visitor,
 | 
			
		||||
				  DamageVisitor &damage_visitor) const {
 | 
			
		||||
			block_counter counter;
 | 
			
		||||
			block_value_visitor<ValueVisitor> bvisitor(*this, value_visitor);
 | 
			
		||||
			block_damage_visitor<DamageVisitor> dvisitor(damage_visitor, entries_per_block_);
 | 
			
		||||
			btree_visit_values(block_tree_, counter, bvisitor, dvisitor);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
 | 
			
		||||
		struct resizer {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										254
									
								
								persistent-data/data-structures/bitset.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								persistent-data/data-structures/bitset.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,254 @@
 | 
			
		||||
#include "persistent-data/data-structures/array.h"
 | 
			
		||||
#include "persistent-data/data-structures/bitset.h"
 | 
			
		||||
#include "persistent-data/math_utils.h"
 | 
			
		||||
 | 
			
		||||
using namespace boost;
 | 
			
		||||
using namespace persistent_data;
 | 
			
		||||
using namespace persistent_data::bitset_detail;
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	struct bitset_traits {
 | 
			
		||||
		typedef base::le64 disk_type;
 | 
			
		||||
		typedef uint64_t value_type;
 | 
			
		||||
		typedef no_op_ref_counter<uint64_t> ref_counter;
 | 
			
		||||
 | 
			
		||||
		static void unpack(disk_type const &disk, value_type &value) {
 | 
			
		||||
			value = base::to_cpu<uint64_t>(disk);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		static void pack(value_type const &value, disk_type &disk) {
 | 
			
		||||
			disk = base::to_disk<base::le64>(value);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace persistent_data {
 | 
			
		||||
	namespace bitset_detail {
 | 
			
		||||
		class bitset_impl {
 | 
			
		||||
		public:
 | 
			
		||||
			typedef boost::shared_ptr<bitset_impl> ptr;
 | 
			
		||||
			typedef typename persistent_data::transaction_manager::ptr tm_ptr;
 | 
			
		||||
 | 
			
		||||
			bitset_impl(tm_ptr tm)
 | 
			
		||||
			: nr_bits_(0),
 | 
			
		||||
			  array_(tm, rc_) {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			bitset_impl(tm_ptr tm, block_address root, unsigned nr_bits)
 | 
			
		||||
				: nr_bits_(nr_bits),
 | 
			
		||||
				  array_(tm, rc_, root, nr_bits) {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			block_address get_root() const {
 | 
			
		||||
				return array_.get_root();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			void grow(unsigned new_nr_bits, bool default_value) {
 | 
			
		||||
				pad_last_block(default_value);
 | 
			
		||||
				resize_array(new_nr_bits, default_value);
 | 
			
		||||
				nr_bits_ = new_nr_bits;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			void destroy() {
 | 
			
		||||
				throw runtime_error("bitset.destroy() not implemented");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// May trigger a flush, so cannot be const
 | 
			
		||||
			bool get(unsigned n) {
 | 
			
		||||
				check_bounds(n);
 | 
			
		||||
				return get_bit(array_.get(word(n)), bit(n));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			void set(unsigned n, bool value) {
 | 
			
		||||
				check_bounds(n);
 | 
			
		||||
				unsigned w_index = word(n);
 | 
			
		||||
				uint64_t w = array_.get(w_index);
 | 
			
		||||
				if (value)
 | 
			
		||||
					w = set_bit(w, bit(n));
 | 
			
		||||
				else
 | 
			
		||||
					w = clear_bit(w, bit(n));
 | 
			
		||||
				array_.set(w_index, w);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			void flush() {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			void walk_bitset(bitset_visitor &v) const {
 | 
			
		||||
				bit_visitor vv(v);
 | 
			
		||||
				damage_visitor dv(v);
 | 
			
		||||
				array_.visit_values(vv, dv);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		private:
 | 
			
		||||
			class bit_visitor {
 | 
			
		||||
			public:
 | 
			
		||||
				bit_visitor(bitset_visitor &v)
 | 
			
		||||
				: v_(v) {
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				void visit(uint32_t word_index, uint64_t word) {
 | 
			
		||||
					uint32_t bit_index = word_index * 64;
 | 
			
		||||
					for (unsigned bit = 0; bit < 64; bit++, bit_index++)
 | 
			
		||||
						v_.visit(bit_index, !!(word & (1 << bit)));
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			private:
 | 
			
		||||
				bitset_visitor &v_;
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			class damage_visitor {
 | 
			
		||||
			public:
 | 
			
		||||
				damage_visitor(bitset_visitor &v)
 | 
			
		||||
					: v_(v) {
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				void visit(array_detail::damage const &d) {
 | 
			
		||||
					run<uint32_t> bits(lifted_mult64(d.lost_keys_.begin_),
 | 
			
		||||
							   lifted_mult64(d.lost_keys_.end_));
 | 
			
		||||
					v_.visit(bits);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			private:
 | 
			
		||||
				optional<uint32_t> lifted_mult64(optional<uint32_t> const &m) {
 | 
			
		||||
					if (!m)
 | 
			
		||||
						return m;
 | 
			
		||||
 | 
			
		||||
					return optional<uint32_t>(*m * 64);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				bitset_visitor &v_;
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			void pad_last_block(bool default_value) {
 | 
			
		||||
				// Set defaults in the final word
 | 
			
		||||
				if (bit(nr_bits_)) {
 | 
			
		||||
					unsigned w_index = word(nr_bits_);
 | 
			
		||||
					uint64_t w = array_.get(w_index);
 | 
			
		||||
 | 
			
		||||
					for (unsigned b = bit(nr_bits_); b < 64; b++)
 | 
			
		||||
						if (default_value)
 | 
			
		||||
							w = set_bit(w, b);
 | 
			
		||||
						else
 | 
			
		||||
							w = clear_bit(w, b);
 | 
			
		||||
 | 
			
		||||
					array_.set(w_index, w);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			void resize_array(unsigned new_nr_bits, bool default_value) {
 | 
			
		||||
				unsigned old_nr_words = words_needed(nr_bits_);
 | 
			
		||||
				unsigned new_nr_words = words_needed(new_nr_bits);
 | 
			
		||||
 | 
			
		||||
				if (new_nr_words < old_nr_words)
 | 
			
		||||
					throw runtime_error("bitset grow actually asked to shrink");
 | 
			
		||||
 | 
			
		||||
				if (new_nr_words > old_nr_words)
 | 
			
		||||
					array_.grow(new_nr_words, default_value ? ~0 : 0);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			unsigned words_needed(unsigned nr_bits) const {
 | 
			
		||||
				return base::div_up<unsigned>(nr_bits, 64u);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			unsigned word(unsigned bit) const {
 | 
			
		||||
				return bit / 64;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			uint64_t mask(unsigned bit) const {
 | 
			
		||||
				return 1ull << bit;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			bool get_bit(uint64_t w, unsigned bit) const {
 | 
			
		||||
				return w & mask(bit);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			uint64_t set_bit(uint64_t w, unsigned bit) const {
 | 
			
		||||
				return w | mask(bit);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			uint64_t clear_bit(uint64_t w, unsigned bit) const {
 | 
			
		||||
				return w & (~mask(bit));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			unsigned bit(unsigned bit) const {
 | 
			
		||||
				return bit % 64;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// The last word may be only partially full, so we have to
 | 
			
		||||
			// do our own bounds checking rather than relying on array
 | 
			
		||||
			// to do it.
 | 
			
		||||
			void check_bounds(unsigned n) const {
 | 
			
		||||
				if (n >= nr_bits_) {
 | 
			
		||||
					std::ostringstream str;
 | 
			
		||||
					str << "bitset index out of bounds ("
 | 
			
		||||
					    << n << " >= " << nr_bits_ << endl;
 | 
			
		||||
					throw runtime_error(str.str());
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			unsigned nr_bits_;
 | 
			
		||||
			no_op_ref_counter<uint64_t> rc_;
 | 
			
		||||
			array<bitset_traits> array_;
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
bitset::bitset(tm_ptr tm)
 | 
			
		||||
	: impl_(new bitset_impl(tm))
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bitset::bitset(tm_ptr tm, block_address root, unsigned nr_bits)
 | 
			
		||||
	: impl_(new bitset_impl(tm, root, nr_bits))
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
block_address
 | 
			
		||||
bitset::get_root() const
 | 
			
		||||
{
 | 
			
		||||
	return impl_->get_root();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
bitset::grow(unsigned new_nr_bits, bool default_value)
 | 
			
		||||
{
 | 
			
		||||
	impl_->grow(new_nr_bits, default_value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
bitset::destroy()
 | 
			
		||||
{
 | 
			
		||||
	impl_->destroy();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
bitset::get(unsigned n)
 | 
			
		||||
{
 | 
			
		||||
	return impl_->get(n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
bitset::set(unsigned n, bool value)
 | 
			
		||||
{
 | 
			
		||||
	impl_->set(n, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
bitset::flush()
 | 
			
		||||
{
 | 
			
		||||
	impl_->flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
bitset::walk_bitset(bitset_visitor &v) const
 | 
			
		||||
{
 | 
			
		||||
	impl_->walk_bitset(v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
@@ -19,25 +19,30 @@
 | 
			
		||||
#ifndef BITSET_H
 | 
			
		||||
#define BITSET_H
 | 
			
		||||
 | 
			
		||||
#include "persistent-data/math_utils.h"
 | 
			
		||||
#include "persistent-data/data-structures/array.h"
 | 
			
		||||
#include "persistent-data/run.h"
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace persistent_data {
 | 
			
		||||
	namespace bitset_detail {
 | 
			
		||||
		struct bitset_traits {
 | 
			
		||||
			typedef base::le64 disk_type;
 | 
			
		||||
			typedef uint64_t value_type;
 | 
			
		||||
			typedef no_op_ref_counter<uint64_t> ref_counter;
 | 
			
		||||
		class bitset_impl;
 | 
			
		||||
 | 
			
		||||
			static void unpack(disk_type const &disk, value_type &value) {
 | 
			
		||||
				value = base::to_cpu<uint64_t>(disk);
 | 
			
		||||
		class missing_bits {
 | 
			
		||||
		public:
 | 
			
		||||
			missing_bits(base::run<uint32_t> const &keys)
 | 
			
		||||
				: keys_(keys) {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			static void pack(value_type const &value, disk_type &disk) {
 | 
			
		||||
				disk = base::to_disk<base::le64>(value);
 | 
			
		||||
			}
 | 
			
		||||
			base::run<uint32_t> keys_;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		class bitset_visitor {
 | 
			
		||||
		public:
 | 
			
		||||
			typedef boost::shared_ptr<bitset_visitor> ptr;
 | 
			
		||||
 | 
			
		||||
			virtual ~bitset_visitor() {}
 | 
			
		||||
			virtual void visit(uint32_t index, bool value) = 0;
 | 
			
		||||
			virtual void visit(missing_bits const &d) = 0;
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -46,119 +51,21 @@ namespace persistent_data {
 | 
			
		||||
		typedef boost::shared_ptr<bitset> ptr;
 | 
			
		||||
		typedef typename persistent_data::transaction_manager::ptr tm_ptr;
 | 
			
		||||
 | 
			
		||||
		bitset(tm_ptr tm)
 | 
			
		||||
		: nr_bits_(0),
 | 
			
		||||
		  array_(tm, rc_) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		bitset(tm_ptr tm, block_address root, unsigned nr_bits)
 | 
			
		||||
			: nr_bits_(nr_bits),
 | 
			
		||||
			  array_(tm, rc_, root, nr_bits) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		block_address get_root() const {
 | 
			
		||||
			return array_.get_root();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void grow(unsigned new_nr_bits, bool default_value) {
 | 
			
		||||
			pad_last_block(default_value);
 | 
			
		||||
			resize_array(new_nr_bits, default_value);
 | 
			
		||||
			nr_bits_ = new_nr_bits;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		bitset(tm_ptr tm);
 | 
			
		||||
		bitset(tm_ptr tm, block_address root, unsigned nr_bits);
 | 
			
		||||
		block_address get_root() const;
 | 
			
		||||
		void grow(unsigned new_nr_bits, bool default_value);
 | 
			
		||||
		void destroy();
 | 
			
		||||
 | 
			
		||||
		// May trigger a flush, so cannot be const
 | 
			
		||||
		bool get(unsigned n) {
 | 
			
		||||
			check_bounds(n);
 | 
			
		||||
			return get_bit(array_.get(word(n)), bit(n));
 | 
			
		||||
		}
 | 
			
		||||
		bool get(unsigned n);
 | 
			
		||||
		void set(unsigned n, bool value);
 | 
			
		||||
		void flush();
 | 
			
		||||
 | 
			
		||||
		void set(unsigned n, bool value) {
 | 
			
		||||
			check_bounds(n);
 | 
			
		||||
			unsigned w_index = word(n);
 | 
			
		||||
			uint64_t w = array_.get(w_index);
 | 
			
		||||
			if (value)
 | 
			
		||||
				w = set_bit(w, bit(n));
 | 
			
		||||
			else
 | 
			
		||||
				w = clear_bit(w, bit(n));
 | 
			
		||||
			array_.set(w_index, w);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void flush() {
 | 
			
		||||
		}
 | 
			
		||||
		void walk_bitset(bitset_detail::bitset_visitor &v) const;
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		void pad_last_block(bool default_value) {
 | 
			
		||||
			// Set defaults in the final word
 | 
			
		||||
			if (bit(nr_bits_)) {
 | 
			
		||||
				unsigned w_index = word(nr_bits_);
 | 
			
		||||
				uint64_t w = array_.get(w_index);
 | 
			
		||||
 | 
			
		||||
				for (unsigned b = bit(nr_bits_); b < 64; b++)
 | 
			
		||||
					if (default_value)
 | 
			
		||||
						w = set_bit(w, b);
 | 
			
		||||
					else
 | 
			
		||||
						w = clear_bit(w, b);
 | 
			
		||||
 | 
			
		||||
				array_.set(w_index, w);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void resize_array(unsigned new_nr_bits, bool default_value) {
 | 
			
		||||
			unsigned old_nr_words = words_needed(nr_bits_);
 | 
			
		||||
			unsigned new_nr_words = words_needed(new_nr_bits);
 | 
			
		||||
 | 
			
		||||
			if (new_nr_words < old_nr_words)
 | 
			
		||||
				throw runtime_error("bitset grow actually asked to shrink");
 | 
			
		||||
 | 
			
		||||
			if (new_nr_words > old_nr_words)
 | 
			
		||||
				array_.grow(new_nr_words, default_value ? ~0 : 0);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		unsigned words_needed(unsigned nr_bits) const {
 | 
			
		||||
			return base::div_up<unsigned>(nr_bits, 64u);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		unsigned word(unsigned bit) const {
 | 
			
		||||
			return bit / 64;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		uint64_t mask(unsigned bit) const {
 | 
			
		||||
			return 1ull << bit;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		bool get_bit(uint64_t w, unsigned bit) const {
 | 
			
		||||
			return w & mask(bit);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		uint64_t set_bit(uint64_t w, unsigned bit) const {
 | 
			
		||||
			return w | mask(bit);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		uint64_t clear_bit(uint64_t w, unsigned bit) const {
 | 
			
		||||
			return w & (~mask(bit));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		unsigned bit(unsigned bit) const {
 | 
			
		||||
			return bit % 64;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// The last word may be only partially full, so we have to
 | 
			
		||||
		// do our own bounds checking rather than relying on array
 | 
			
		||||
		// to do it.
 | 
			
		||||
		void check_bounds(unsigned n) const {
 | 
			
		||||
			if (n >= nr_bits_) {
 | 
			
		||||
				std::ostringstream str;
 | 
			
		||||
				str << "bitset index out of bounds ("
 | 
			
		||||
				    << n << " >= " << nr_bits_ << endl;
 | 
			
		||||
				throw runtime_error(str.str());
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		unsigned nr_bits_;
 | 
			
		||||
		no_op_ref_counter<uint64_t> rc_;
 | 
			
		||||
		array<bitset_detail::bitset_traits> array_;
 | 
			
		||||
		boost::shared_ptr<bitset_detail::bitset_impl> impl_;
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -213,7 +213,7 @@ namespace persistent_data {
 | 
			
		||||
		class ro_spine : private boost::noncopyable {
 | 
			
		||||
		public:
 | 
			
		||||
			ro_spine(transaction_manager::ptr tm,
 | 
			
		||||
				 typename block_manager<>::validator::ptr v)
 | 
			
		||||
				 block_manager<>::validator::ptr v)
 | 
			
		||||
				: tm_(tm),
 | 
			
		||||
				  validator_(v) {
 | 
			
		||||
			}
 | 
			
		||||
@@ -227,7 +227,7 @@ namespace persistent_data {
 | 
			
		||||
 | 
			
		||||
		private:
 | 
			
		||||
			transaction_manager::ptr tm_;
 | 
			
		||||
			typename block_manager<>::validator::ptr validator_;
 | 
			
		||||
			block_manager<>::validator::ptr validator_;
 | 
			
		||||
			std::list<block_manager<>::read_ref> spine_;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
@@ -235,9 +235,10 @@ namespace persistent_data {
 | 
			
		||||
		public:
 | 
			
		||||
			typedef transaction_manager::read_ref read_ref;
 | 
			
		||||
			typedef transaction_manager::write_ref write_ref;
 | 
			
		||||
			typedef boost::optional<block_address> maybe_block;
 | 
			
		||||
 | 
			
		||||
			shadow_spine(transaction_manager::ptr tm,
 | 
			
		||||
				     typename block_manager<>::validator::ptr v)
 | 
			
		||||
				     block_manager<>::validator::ptr v)
 | 
			
		||||
 | 
			
		||||
				: tm_(tm),
 | 
			
		||||
				  validator_(v) {
 | 
			
		||||
@@ -282,14 +283,17 @@ namespace persistent_data {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			block_address get_root() const {
 | 
			
		||||
				return root_;
 | 
			
		||||
				if (root_)
 | 
			
		||||
					return *root_;
 | 
			
		||||
 | 
			
		||||
				throw std::runtime_error("shadow spine has no root");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		private:
 | 
			
		||||
			transaction_manager::ptr tm_;
 | 
			
		||||
			typename block_manager<>::validator::ptr validator_;
 | 
			
		||||
			block_manager<>::validator::ptr validator_;
 | 
			
		||||
			std::list<block_manager<>::write_ref> spine_;
 | 
			
		||||
			block_address root_;
 | 
			
		||||
		        maybe_block root_;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		// Used to keep a record of a nested btree's position.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
#include "persistent-data/math_utils.h"
 | 
			
		||||
#include "thin-provisioning/file_utils.h"
 | 
			
		||||
#include "persistent-data/file_utils.h"
 | 
			
		||||
 | 
			
		||||
#include <linux/fs.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
@@ -12,7 +12,7 @@ using namespace base;
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
persistent_data::block_address
 | 
			
		||||
thin_provisioning::get_nr_blocks(string const &path)
 | 
			
		||||
persistent_data::get_nr_blocks(string const &path)
 | 
			
		||||
{
 | 
			
		||||
	using namespace persistent_data;
 | 
			
		||||
 | 
			
		||||
@@ -47,4 +47,22 @@ thin_provisioning::get_nr_blocks(string const &path)
 | 
			
		||||
	return nr_blocks;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
persistent_data::block_manager<>::ptr
 | 
			
		||||
persistent_data::open_bm(std::string const &dev_path, block_io<>::mode m)
 | 
			
		||||
{
 | 
			
		||||
	block_address nr_blocks = get_nr_blocks(dev_path);
 | 
			
		||||
	return block_manager<>::ptr(new block_manager<>(dev_path, nr_blocks, 1, m));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
persistent_data::check_file_exists(string const &file) {
 | 
			
		||||
	struct stat info;
 | 
			
		||||
	int r = ::stat(file.c_str(), &info);
 | 
			
		||||
	if (r)
 | 
			
		||||
		throw runtime_error("Couldn't stat file");
 | 
			
		||||
 | 
			
		||||
	if (!S_ISREG(info.st_mode))
 | 
			
		||||
		throw runtime_error("Not a regular file");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
@@ -3,10 +3,16 @@
 | 
			
		||||
 | 
			
		||||
#include "persistent-data/block.h"
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace thin_provisioning {
 | 
			
		||||
// FIXME: move to a different unit
 | 
			
		||||
namespace persistent_data {
 | 
			
		||||
	persistent_data::block_address get_nr_blocks(string const &path);
 | 
			
		||||
	block_manager<>::ptr open_bm(std::string const &dev_path, block_io<>::mode m);
 | 
			
		||||
 | 
			
		||||
	void check_file_exists(std::string const &file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
@@ -29,6 +29,8 @@ void base::hex_dump(ostream &out, void const *data_, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char const *data = reinterpret_cast<unsigned char const *>(data_),
 | 
			
		||||
		*end = data + len;
 | 
			
		||||
 | 
			
		||||
	ios_base::fmtflags old_flags = out.flags();
 | 
			
		||||
	out << hex;
 | 
			
		||||
 | 
			
		||||
	while (data < end) {
 | 
			
		||||
@@ -36,7 +38,8 @@ void base::hex_dump(ostream &out, void const *data_, size_t len)
 | 
			
		||||
			out << setw(2) << setfill('0') << (unsigned) *data << " ";
 | 
			
		||||
		out << endl;
 | 
			
		||||
	}
 | 
			
		||||
	out << dec;
 | 
			
		||||
 | 
			
		||||
	out.setf(old_flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@
 | 
			
		||||
namespace persistent_data {
 | 
			
		||||
	class subtracting_span_iterator : public space_map::span_iterator {
 | 
			
		||||
	public:
 | 
			
		||||
		typedef typename base::run_set<block_address> block_set;
 | 
			
		||||
		typedef base::run_set<block_address> block_set;
 | 
			
		||||
		typedef space_map::span span;
 | 
			
		||||
 | 
			
		||||
		subtracting_span_iterator(block_address max,
 | 
			
		||||
 
 | 
			
		||||
@@ -17,9 +17,9 @@
 | 
			
		||||
// <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
#include "thin-provisioning/device_tree.h"
 | 
			
		||||
#include "thin-provisioning/file_utils.h"
 | 
			
		||||
#include "thin-provisioning/metadata.h"
 | 
			
		||||
 | 
			
		||||
#include "persistent-data/file_utils.h"
 | 
			
		||||
#include "persistent-data/math_utils.h"
 | 
			
		||||
#include "persistent-data/space-maps/core.h"
 | 
			
		||||
#include "persistent-data/space-maps/disk.h"
 | 
			
		||||
@@ -40,15 +40,6 @@ namespace {
 | 
			
		||||
 | 
			
		||||
	unsigned const METADATA_CACHE_SIZE = 1024;
 | 
			
		||||
 | 
			
		||||
	block_manager<>::ptr open_bm(string const &dev_path, bool writeable) {
 | 
			
		||||
		block_address nr_blocks = get_nr_blocks(dev_path);
 | 
			
		||||
		typename block_io<>::mode m = writeable ?
 | 
			
		||||
			block_io<>::READ_WRITE :
 | 
			
		||||
			block_io<>::READ_ONLY;
 | 
			
		||||
 | 
			
		||||
		return block_manager<>::ptr(new block_manager<>(dev_path, nr_blocks, 1, m));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	transaction_manager::ptr
 | 
			
		||||
	open_tm(block_manager<>::ptr bm) {
 | 
			
		||||
		space_map::ptr sm(new core_map(bm->get_nr_blocks()));
 | 
			
		||||
@@ -90,7 +81,7 @@ metadata::metadata(std::string const &dev_path, open_type ot,
 | 
			
		||||
{
 | 
			
		||||
	switch (ot) {
 | 
			
		||||
	case OPEN:
 | 
			
		||||
		tm_ = open_tm(open_bm(dev_path, false));
 | 
			
		||||
		tm_ = open_tm(open_bm(dev_path, block_io<>::READ_ONLY));
 | 
			
		||||
		sb_ = read_superblock(tm_->get_bm());
 | 
			
		||||
 | 
			
		||||
		if (sb_.version_ != 1)
 | 
			
		||||
@@ -115,7 +106,7 @@ metadata::metadata(std::string const &dev_path, open_type ot,
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case CREATE:
 | 
			
		||||
		tm_ = open_tm(open_bm(dev_path, true));
 | 
			
		||||
		tm_ = open_tm(open_bm(dev_path, block_io<>::READ_WRITE));
 | 
			
		||||
		space_map::ptr core = tm_->get_sm();
 | 
			
		||||
		metadata_sm_ = create_metadata_sm(tm_, tm_->get_bm()->get_nr_blocks());
 | 
			
		||||
		copy_space_maps(metadata_sm_, core);
 | 
			
		||||
@@ -143,7 +134,7 @@ metadata::metadata(std::string const &dev_path, open_type ot,
 | 
			
		||||
 | 
			
		||||
metadata::metadata(std::string const &dev_path, block_address metadata_snap)
 | 
			
		||||
{
 | 
			
		||||
	tm_ = open_tm(open_bm(dev_path, false));
 | 
			
		||||
	tm_ = open_tm(open_bm(dev_path, block_io<>::READ_ONLY));
 | 
			
		||||
	sb_ = read_superblock(tm_->get_bm(), metadata_snap);
 | 
			
		||||
 | 
			
		||||
	// We don't open the metadata sm for a held root
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@
 | 
			
		||||
// with thin-provisioning-tools.  If not, see
 | 
			
		||||
// <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
#include "thin-provisioning/file_utils.h"
 | 
			
		||||
#include "persistent-data/file_utils.h"
 | 
			
		||||
#include "thin-provisioning/metadata.h"
 | 
			
		||||
#include "thin-provisioning/metadata_checker.h"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,7 @@ namespace {
 | 
			
		||||
			  md_sm_(md_sm),
 | 
			
		||||
			  data_sm_(data_sm),
 | 
			
		||||
			  in_range_(false),
 | 
			
		||||
			  time_(),
 | 
			
		||||
			  found_errors_(false) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -139,7 +140,7 @@ namespace {
 | 
			
		||||
 | 
			
		||||
	class details_extractor : public btree<1, device_tree_detail::device_details_traits>::visitor {
 | 
			
		||||
	public:
 | 
			
		||||
		typedef typename btree<1, device_tree_detail::device_details_traits>::visitor::node_location node_location;
 | 
			
		||||
		typedef btree<1, device_tree_detail::device_details_traits>::visitor::node_location node_location;
 | 
			
		||||
		typedef boost::shared_ptr<details_extractor> ptr;
 | 
			
		||||
		typedef btree_checker<1, device_tree_detail::device_details_traits> checker;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,7 @@ namespace {
 | 
			
		||||
		restorer(metadata::ptr md)
 | 
			
		||||
			: md_(md),
 | 
			
		||||
			  in_superblock_(false),
 | 
			
		||||
			  nr_data_blocks_(),
 | 
			
		||||
			  empty_mapping_(new_mapping_tree()) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,7 @@ superblock_traits::unpack(superblock_disk const &disk, superblock &value)
 | 
			
		||||
	value.metadata_nr_blocks_ = to_cpu<uint64_t>(disk.metadata_nr_blocks_);
 | 
			
		||||
 | 
			
		||||
	value.compat_flags_ = to_cpu<uint32_t>(disk.compat_flags_);
 | 
			
		||||
	value.compat_ro_flags_ = to_cpu<uint32_t>(disk.compat_ro_flags_);
 | 
			
		||||
	value.incompat_flags_ = to_cpu<uint32_t>(disk.incompat_flags_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -70,6 +71,7 @@ superblock_traits::pack(superblock const &value, superblock_disk &disk)
 | 
			
		||||
	disk.metadata_nr_blocks_ = to_disk<le64>(value.metadata_nr_blocks_);
 | 
			
		||||
 | 
			
		||||
	disk.compat_flags_ = to_disk<le32>(value.compat_flags_);
 | 
			
		||||
	disk.compat_ro_flags_ = to_disk<le32>(value.compat_ro_flags_);
 | 
			
		||||
	disk.incompat_flags_ = to_disk<le32>(value.incompat_flags_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -122,6 +122,8 @@ namespace thin_provisioning {
 | 
			
		||||
 | 
			
		||||
	persistent_data::block_manager<>::validator::ptr superblock_validator();
 | 
			
		||||
 | 
			
		||||
	// FIXME: should we put init_superblock in here too?
 | 
			
		||||
 | 
			
		||||
	superblock_detail::superblock read_superblock(persistent_data::block_manager<>::ptr bm);
 | 
			
		||||
	superblock_detail::superblock read_superblock(persistent_data::block_manager<>::ptr bm, persistent_data::block_address location);
 | 
			
		||||
	void check_superblock(persistent_data::block_manager<>::ptr bm,
 | 
			
		||||
 
 | 
			
		||||
@@ -22,129 +22,27 @@
 | 
			
		||||
 | 
			
		||||
#include "version.h"
 | 
			
		||||
 | 
			
		||||
#include "base/error_state.h"
 | 
			
		||||
#include "base/nested_output.h"
 | 
			
		||||
#include "persistent-data/space-maps/core.h"
 | 
			
		||||
#include "persistent-data/file_utils.h"
 | 
			
		||||
#include "thin-provisioning/device_tree.h"
 | 
			
		||||
#include "thin-provisioning/file_utils.h"
 | 
			
		||||
#include "thin-provisioning/mapping_tree.h"
 | 
			
		||||
#include "thin-provisioning/superblock.h"
 | 
			
		||||
 | 
			
		||||
using namespace base;
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace thin_provisioning;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
	class end_message {};
 | 
			
		||||
 | 
			
		||||
	class nested_output {
 | 
			
		||||
	public:
 | 
			
		||||
		nested_output(ostream &out, unsigned step)
 | 
			
		||||
			: out_(out),
 | 
			
		||||
			  step_(step),
 | 
			
		||||
			  beginning_of_line_(true),
 | 
			
		||||
			  enabled_(true),
 | 
			
		||||
			  indent_(0) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		template <typename T>
 | 
			
		||||
		nested_output &operator <<(T const &t) {
 | 
			
		||||
			if (beginning_of_line_) {
 | 
			
		||||
				beginning_of_line_ = false;
 | 
			
		||||
				indent();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (enabled_)
 | 
			
		||||
				out_ << t;
 | 
			
		||||
 | 
			
		||||
			return *this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		nested_output &operator <<(end_message const &m) {
 | 
			
		||||
			beginning_of_line_ = true;
 | 
			
		||||
 | 
			
		||||
			if (enabled_)
 | 
			
		||||
				out_ << endl;
 | 
			
		||||
 | 
			
		||||
			return *this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void inc_indent() {
 | 
			
		||||
			indent_ += step_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void dec_indent() {
 | 
			
		||||
			indent_ -= step_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		struct nest {
 | 
			
		||||
			nest(nested_output &out)
 | 
			
		||||
			: out_(out) {
 | 
			
		||||
				out_.inc_indent();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			~nest() {
 | 
			
		||||
				out_.dec_indent();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			nested_output &out_;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		nest push() {
 | 
			
		||||
			return nest(*this);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void enable() {
 | 
			
		||||
			enabled_ = true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void disable() {
 | 
			
		||||
			enabled_ = false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		void indent() {
 | 
			
		||||
			if (enabled_)
 | 
			
		||||
				for (unsigned i = 0; i < indent_; i++)
 | 
			
		||||
					out_ << ' ';
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ostream &out_;
 | 
			
		||||
		unsigned step_;
 | 
			
		||||
 | 
			
		||||
		bool beginning_of_line_;
 | 
			
		||||
		bool enabled_;
 | 
			
		||||
		unsigned indent_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
 | 
			
		||||
	enum error_state {
 | 
			
		||||
		NO_ERROR,
 | 
			
		||||
		NON_FATAL,	// eg, lost blocks
 | 
			
		||||
		FATAL		// needs fixing before pool can be activated
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	error_state
 | 
			
		||||
	combine_errors(error_state lhs, error_state rhs) {
 | 
			
		||||
		switch (lhs) {
 | 
			
		||||
		case NO_ERROR:
 | 
			
		||||
			return rhs;
 | 
			
		||||
 | 
			
		||||
		case NON_FATAL:
 | 
			
		||||
			return (rhs == FATAL) ? FATAL : lhs;
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			return lhs;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//--------------------------------
 | 
			
		||||
 | 
			
		||||
	block_manager<>::ptr
 | 
			
		||||
	open_bm(string const &path) {
 | 
			
		||||
		block_address nr_blocks = get_nr_blocks(path);
 | 
			
		||||
		typename block_io<>::mode m = block_io<>::READ_ONLY;
 | 
			
		||||
		block_io<>::mode m = block_io<>::READ_ONLY;
 | 
			
		||||
		return block_manager<>::ptr(new block_manager<>(path, nr_blocks, 1, m));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -174,7 +72,7 @@ namespace {
 | 
			
		||||
			err_ = combine_errors(err_, FATAL);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		error_state get_error() const {
 | 
			
		||||
		base::error_state get_error() const {
 | 
			
		||||
			return err_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -216,7 +114,8 @@ namespace {
 | 
			
		||||
	class mapping_reporter : public mapping_tree_detail::damage_visitor {
 | 
			
		||||
	public:
 | 
			
		||||
		mapping_reporter(nested_output &out)
 | 
			
		||||
		: out_(out) {
 | 
			
		||||
		: out_(out),
 | 
			
		||||
		  err_(NO_ERROR) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		virtual void visit(mapping_tree_detail::missing_devices const &d) {
 | 
			
		||||
@@ -344,13 +243,12 @@ namespace {
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int c;
 | 
			
		||||
	flags fs = {
 | 
			
		||||
		.check_device_tree = true,
 | 
			
		||||
		.check_mapping_tree_level1 = true,
 | 
			
		||||
		.check_mapping_tree_level2 = true,
 | 
			
		||||
		.ignore_non_fatal_errors = false,
 | 
			
		||||
		.quiet = false
 | 
			
		||||
	};
 | 
			
		||||
	flags fs;
 | 
			
		||||
	fs.check_device_tree = true;
 | 
			
		||||
	fs.check_mapping_tree_level1 = true,
 | 
			
		||||
	fs.check_mapping_tree_level2 = true,
 | 
			
		||||
	fs.ignore_non_fatal_errors = false,
 | 
			
		||||
	fs.quiet = false;
 | 
			
		||||
 | 
			
		||||
	char const shortopts[] = "qhV";
 | 
			
		||||
	option const longopts[] = {
 | 
			
		||||
 
 | 
			
		||||
@@ -37,8 +37,8 @@ struct flags {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	int dump(string const &path, ostream *out, string const &format, struct flags &flags,
 | 
			
		||||
		 block_address metadata_snap = 0) {
 | 
			
		||||
	int dump_(string const &path, ostream &out, string const &format, struct flags &flags,
 | 
			
		||||
		  block_address metadata_snap) {
 | 
			
		||||
		try {
 | 
			
		||||
			metadata::ptr md(new metadata(path, metadata_snap));
 | 
			
		||||
			emitter::ptr e;
 | 
			
		||||
@@ -56,9 +56,9 @@ namespace {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (format == "xml")
 | 
			
		||||
				e = create_xml_emitter(*out);
 | 
			
		||||
				e = create_xml_emitter(out);
 | 
			
		||||
			else if (format == "human_readable")
 | 
			
		||||
				e = create_human_readable_emitter(*out);
 | 
			
		||||
				e = create_human_readable_emitter(out);
 | 
			
		||||
			else {
 | 
			
		||||
				cerr << "unknown format '" << format << "'" << endl;
 | 
			
		||||
				exit(1);
 | 
			
		||||
@@ -74,6 +74,15 @@ namespace {
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int dump(string const &path, char const *output, string const &format, struct flags &flags,
 | 
			
		||||
		 block_address metadata_snap = 0) {
 | 
			
		||||
		if (output) {
 | 
			
		||||
			ofstream out(output);
 | 
			
		||||
			return dump_(path, out, format, flags, metadata_snap);
 | 
			
		||||
		} else
 | 
			
		||||
			return dump_(path, cout, format, flags, metadata_snap);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void usage(ostream &out, string const &cmd) {
 | 
			
		||||
		out << "Usage: " << cmd << " [options] {device|file}" << endl
 | 
			
		||||
		    << "Options:" << endl
 | 
			
		||||
@@ -94,7 +103,8 @@ int main(int argc, char **argv)
 | 
			
		||||
	char *end_ptr;
 | 
			
		||||
	string format = "xml";
 | 
			
		||||
	block_address metadata_snap = 0;
 | 
			
		||||
	struct flags flags = { .find_metadata_snap = false, .repair = false };
 | 
			
		||||
	struct flags flags;
 | 
			
		||||
	flags.find_metadata_snap = flags.repair = false;
 | 
			
		||||
 | 
			
		||||
	const struct option longopts[] = {
 | 
			
		||||
		{ "help", no_argument, NULL, 'h'},
 | 
			
		||||
@@ -153,5 +163,5 @@ int main(int argc, char **argv)
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dump(argv[optind], output ? new ofstream(output) : &cout, format, flags, metadata_snap);
 | 
			
		||||
	return dump(argv[optind], output, format, flags, metadata_snap);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,11 +16,12 @@
 | 
			
		||||
// with thin-provisioning-tools.  If not, see
 | 
			
		||||
// <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
#include "emitter.h"
 | 
			
		||||
#include "human_readable_format.h"
 | 
			
		||||
#include "metadata.h"
 | 
			
		||||
#include "restore_emitter.h"
 | 
			
		||||
#include "xml_format.h"
 | 
			
		||||
#include "persistent-data/file_utils.h"
 | 
			
		||||
#include "thin-provisioning/emitter.h"
 | 
			
		||||
#include "thin-provisioning/human_readable_format.h"
 | 
			
		||||
#include "thin-provisioning/metadata.h"
 | 
			
		||||
#include "thin-provisioning/restore_emitter.h"
 | 
			
		||||
#include "thin-provisioning/xml_format.h"
 | 
			
		||||
#include "version.h"
 | 
			
		||||
 | 
			
		||||
#include <fstream>
 | 
			
		||||
@@ -37,12 +38,16 @@ using namespace persistent_data;
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace thin_provisioning;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	int restore(string const &backup_file, string const &dev) {
 | 
			
		||||
		try {
 | 
			
		||||
			// The block size gets updated by the restorer.
 | 
			
		||||
			metadata::ptr md(new metadata(dev, metadata::CREATE, 128, 0));
 | 
			
		||||
			emitter::ptr restorer = create_restore_emitter(md);
 | 
			
		||||
 | 
			
		||||
			check_file_exists(backup_file);
 | 
			
		||||
			ifstream in(backup_file.c_str(), ifstream::in);
 | 
			
		||||
			parse_xml(in, restorer);
 | 
			
		||||
 | 
			
		||||
@@ -121,3 +126,5 @@ int main(int argc, char **argv)
 | 
			
		||||
 | 
			
		||||
	return restore(input, output);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
#include "persistent-data/data-structures/btree_damage_visitor.h"
 | 
			
		||||
#include "persistent-data/run.h"
 | 
			
		||||
#include "persistent-data/space-maps/core.h"
 | 
			
		||||
#include "thin-provisioning/file_utils.h"
 | 
			
		||||
#include "persistent-data/file_utils.h"
 | 
			
		||||
#include "thin-provisioning/superblock.h"
 | 
			
		||||
#include "thin-provisioning/mapping_tree.h"
 | 
			
		||||
#include "thin-provisioning/rmap_visitor.h"
 | 
			
		||||
@@ -23,7 +23,7 @@ namespace {
 | 
			
		||||
	block_manager<>::ptr
 | 
			
		||||
	open_bm(string const &path) {
 | 
			
		||||
		block_address nr_blocks = get_nr_blocks(path);
 | 
			
		||||
		typename block_io<>::mode m = block_io<>::READ_ONLY;
 | 
			
		||||
		block_io<>::mode m = block_io<>::READ_ONLY;
 | 
			
		||||
		return block_manager<>::ptr(new block_manager<>(path, nr_blocks, 1, m));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -150,7 +150,14 @@ int main(int argc, char **argv)
 | 
			
		||||
 | 
			
		||||
		case 1:
 | 
			
		||||
			// region
 | 
			
		||||
			regions.push_back(parse_region(optarg));
 | 
			
		||||
			try {
 | 
			
		||||
				regions.push_back(parse_region(optarg));
 | 
			
		||||
 | 
			
		||||
			} catch (std::exception const &e) {
 | 
			
		||||
				cerr << e.what();
 | 
			
		||||
				return 1;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
 
 | 
			
		||||
@@ -47,12 +47,14 @@ TEST_SOURCE=\
 | 
			
		||||
	\
 | 
			
		||||
	unit-tests/array_block_t.cc \
 | 
			
		||||
	unit-tests/array_t.cc \
 | 
			
		||||
	unit-tests/base64_t.cc \
 | 
			
		||||
	unit-tests/bitset_t.cc \
 | 
			
		||||
	unit-tests/block_t.cc \
 | 
			
		||||
	unit-tests/btree_t.cc \
 | 
			
		||||
	unit-tests/btree_damage_visitor_t.cc \
 | 
			
		||||
	unit-tests/buffer_t.cc \
 | 
			
		||||
	unit-tests/cache_t.cc \
 | 
			
		||||
	unit-tests/cache_superblock_t.cc \
 | 
			
		||||
	unit-tests/damage_tracker_t.cc \
 | 
			
		||||
	unit-tests/endian_t.cc \
 | 
			
		||||
	unit-tests/rmap_visitor_t.cc \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										121
									
								
								unit-tests/base64_t.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								unit-tests/base64_t.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,121 @@
 | 
			
		||||
#include "gmock/gmock.h"
 | 
			
		||||
#include "base/base64.h"
 | 
			
		||||
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
using namespace base;
 | 
			
		||||
using namespace boost;
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace testing;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	typedef vector<unsigned char> bytes;
 | 
			
		||||
 | 
			
		||||
	char const *wikipedia_examples[] = {
 | 
			
		||||
		"any carnal pleasure.", "YW55IGNhcm5hbCBwbGVhc3VyZS4=",
 | 
			
		||||
		"any carnal pleasure", "YW55IGNhcm5hbCBwbGVhc3VyZQ==",
 | 
			
		||||
		"any carnal pleasur", "YW55IGNhcm5hbCBwbGVhc3Vy",
 | 
			
		||||
		"any carnal pleasu", "YW55IGNhcm5hbCBwbGVhc3U=",
 | 
			
		||||
		"any carnal pleas", "YW55IGNhcm5hbCBwbGVhcw==",
 | 
			
		||||
		"pleasure.", "cGxlYXN1cmUu",
 | 
			
		||||
		"leasure.", "bGVhc3VyZS4=",
 | 
			
		||||
		"easure.", "ZWFzdXJlLg==",
 | 
			
		||||
		"asure.", "YXN1cmUu",
 | 
			
		||||
		"sure.", "c3VyZS4="
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	void assert_fails(decoded_or_error const &eoe, string const &msg) {
 | 
			
		||||
		ASSERT_THAT(get<string>(eoe), Eq(msg));
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
TEST(Base64Tests, encoding_an_empty_string)
 | 
			
		||||
{
 | 
			
		||||
	bytes bs;
 | 
			
		||||
	ASSERT_THAT(base64_encode(bs), Eq(string()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(Base64Tests, decoding_an_empty_string)
 | 
			
		||||
{
 | 
			
		||||
	bytes bs;
 | 
			
		||||
	ASSERT_THAT(get<vector<unsigned char> >(base64_decode("")), Eq(bs));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(Base64Tests, encode_single_byte)
 | 
			
		||||
{
 | 
			
		||||
	bytes bs(1);
 | 
			
		||||
	bs[0] = 0;
 | 
			
		||||
 | 
			
		||||
	ASSERT_THAT(base64_encode(bs), Eq(string("AA==")));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(Base64Tests, encode_double_byte)
 | 
			
		||||
{
 | 
			
		||||
	bytes bs(2, 0);
 | 
			
		||||
	ASSERT_THAT(base64_encode(bs), Eq(string("AAA=")));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(Base64Tests, encode_triple_byte)
 | 
			
		||||
{
 | 
			
		||||
	bytes bs(3, 0);
 | 
			
		||||
	ASSERT_THAT(base64_encode(bs), Eq(string("AAAA")));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(Base64Tests, longer_encodings)
 | 
			
		||||
{
 | 
			
		||||
	for (unsigned example = 0; example < 5; example++) {
 | 
			
		||||
		char const *in = wikipedia_examples[example * 2];
 | 
			
		||||
		char const *out = wikipedia_examples[example * 2 + 1];
 | 
			
		||||
		unsigned len = strlen(in);
 | 
			
		||||
		bytes bs(len);
 | 
			
		||||
		for (unsigned b = 0; b < len; b++)
 | 
			
		||||
			bs.at(b) = in[b];
 | 
			
		||||
 | 
			
		||||
		ASSERT_THAT(base64_encode(bs), Eq(string(out)));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(Base64Tests, decoding_fails_with_bad_size_input)
 | 
			
		||||
{
 | 
			
		||||
	char const *err = "bad input length";
 | 
			
		||||
 | 
			
		||||
	assert_fails(base64_decode("AAA"), err);
 | 
			
		||||
	assert_fails(base64_decode("AA"), err);
 | 
			
		||||
	assert_fails(base64_decode("A"), err);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(Base64Tests, encode_decode_cycle)
 | 
			
		||||
{
 | 
			
		||||
	for (unsigned example = 0; example < 5; example++) {
 | 
			
		||||
		char const *in = wikipedia_examples[example * 2];
 | 
			
		||||
		unsigned len = strlen(in);
 | 
			
		||||
		bytes bs(len);
 | 
			
		||||
		for (unsigned b = 0; b < len; b++)
 | 
			
		||||
			bs.at(b) = in[b];
 | 
			
		||||
 | 
			
		||||
		decoded_or_error doe = base64_decode(base64_encode(bs));
 | 
			
		||||
		ASSERT_THAT(get<vector<unsigned char> >(doe), Eq(bs));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(Base64Tests, random_data)
 | 
			
		||||
{
 | 
			
		||||
	for (unsigned len = 1; len < 17; len++) {
 | 
			
		||||
		for (unsigned example = 0; example < 10000; example++) {
 | 
			
		||||
			vector<unsigned char> raw(len);
 | 
			
		||||
 | 
			
		||||
			for (unsigned i = 0; i < len; i++)
 | 
			
		||||
				raw.at(i) = ::rand() % 256;
 | 
			
		||||
 | 
			
		||||
			decoded_or_error doe = base64_decode(base64_encode(raw));
 | 
			
		||||
			ASSERT_THAT(get<vector<unsigned char> >(doe), Eq(raw));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
							
								
								
									
										140
									
								
								unit-tests/cache_superblock_t.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								unit-tests/cache_superblock_t.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,140 @@
 | 
			
		||||
#include "gmock/gmock.h"
 | 
			
		||||
#include "caching/superblock.h"
 | 
			
		||||
 | 
			
		||||
using namespace caching;
 | 
			
		||||
using namespace superblock_damage;
 | 
			
		||||
using namespace testing;
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	class damage_visitor_mock : public damage_visitor {
 | 
			
		||||
	public:
 | 
			
		||||
		MOCK_METHOD1(visit, void(superblock_corrupt const &));
 | 
			
		||||
		MOCK_METHOD1(visit, void(superblock_invalid const &));
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class CacheSuperblockTests : public Test {
 | 
			
		||||
	public:
 | 
			
		||||
		CacheSuperblockTests() {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void check() {
 | 
			
		||||
			check_superblock(sb_, 100, visitor_);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void expect_invalid() {
 | 
			
		||||
			EXPECT_CALL(visitor_, visit(Matcher<superblock_invalid const &>(_))).Times(1);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void check_invalid() {
 | 
			
		||||
			expect_invalid();
 | 
			
		||||
			check();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		damage_visitor_mock visitor_;
 | 
			
		||||
		superblock sb_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	bool operator ==(superblock_corrupt const &lhs, superblock_corrupt const &rhs) {
 | 
			
		||||
		return lhs.get_desc() == rhs.get_desc();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool operator ==(superblock_invalid const &lhs, superblock_invalid const &rhs) {
 | 
			
		||||
		return lhs.get_desc() == rhs.get_desc();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ostream &operator <<(ostream &out, damage const &d) {
 | 
			
		||||
		out << d.get_desc();
 | 
			
		||||
		return out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ostream &operator <<(ostream &out, superblock_invalid const &d) {
 | 
			
		||||
		out << "superblock_invalid: " << d.get_desc();
 | 
			
		||||
		return out;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
TEST_F(CacheSuperblockTests, default_constructed_superblock_is_valid)
 | 
			
		||||
{
 | 
			
		||||
	sb_.flags = 0;
 | 
			
		||||
	check();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(CacheSuperblockTests, non_zero_flags_are_invalid)
 | 
			
		||||
{
 | 
			
		||||
	sb_.flags = 1;
 | 
			
		||||
	check_invalid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(CacheSuperblockTests, blocknr_is_in_range)
 | 
			
		||||
{
 | 
			
		||||
	sb_.blocknr = 101;
 | 
			
		||||
	check_invalid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(CacheSuperblockTests, magic_is_checked)
 | 
			
		||||
{
 | 
			
		||||
	sb_.magic = 12345;
 | 
			
		||||
	check_invalid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(CacheSuperblockTests, version_gt_1_is_checked)
 | 
			
		||||
{
 | 
			
		||||
	sb_.version = 2;
 | 
			
		||||
	check_invalid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(CacheSuperblockTests, version_lt_1_is_checked)
 | 
			
		||||
{
 | 
			
		||||
	sb_.version = 0;
 | 
			
		||||
	check_invalid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(CacheSuperblockTests, policy_name_must_be_null_terminated)
 | 
			
		||||
{
 | 
			
		||||
	for (unsigned i = 0; i < CACHE_POLICY_NAME_SIZE; i++)
 | 
			
		||||
		sb_.policy_name[i] = 'a';
 | 
			
		||||
 | 
			
		||||
	check_invalid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(CacheSuperblockTests, policy_hint_size_checked)
 | 
			
		||||
{
 | 
			
		||||
	sb_.policy_hint_size = 3;
 | 
			
		||||
	check_invalid();
 | 
			
		||||
 | 
			
		||||
	sb_.policy_hint_size = 129;
 | 
			
		||||
	check_invalid();
 | 
			
		||||
 | 
			
		||||
	sb_.policy_hint_size = 132;
 | 
			
		||||
	check_invalid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(CacheSuperblockTests, metadata_block_size_checked)
 | 
			
		||||
{
 | 
			
		||||
	sb_.metadata_block_size = 16;
 | 
			
		||||
	check_invalid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(CacheSuperblockTests, compat_flags_checked)
 | 
			
		||||
{
 | 
			
		||||
	sb_.compat_flags = 1;
 | 
			
		||||
	check_invalid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(CacheSuperblockTests, compat_ro_flags_checked)
 | 
			
		||||
{
 | 
			
		||||
	sb_.compat_ro_flags = 1;
 | 
			
		||||
	check_invalid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(CacheSuperblockTests, incompat_flags_checked)
 | 
			
		||||
{
 | 
			
		||||
	sb_.incompat_flags = 1;
 | 
			
		||||
	check_invalid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//----------------------------------------------------------------
 | 
			
		||||
		Reference in New Issue
	
	Block a user