Merge remote-tracking branch 'ejt/master'
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,19 +1,25 @@
|
|||||||
*~
|
*~
|
||||||
*.o
|
*.o
|
||||||
|
*.a
|
||||||
*.gmo
|
*.gmo
|
||||||
*_t
|
*_t
|
||||||
*.d
|
*.d
|
||||||
*.d.[0-9]*
|
*.d.[0-9]*
|
||||||
test.data
|
test.data
|
||||||
cachegrind.*
|
cachegrind.*
|
||||||
|
\#*\#
|
||||||
|
|
||||||
thin_check
|
thin_check
|
||||||
thin_dump
|
thin_dump
|
||||||
thin_restore
|
thin_restore
|
||||||
thin_repair
|
thin_repair
|
||||||
thin_rmap
|
thin_rmap
|
||||||
|
thin_metadata_size
|
||||||
|
|
||||||
cache_check
|
cache_check
|
||||||
|
cache_dump
|
||||||
|
cache_restore
|
||||||
|
cache_repair
|
||||||
|
|
||||||
*.metadata
|
*.metadata
|
||||||
bad-metadata
|
bad-metadata
|
||||||
|
14
Gemfile.lock
14
Gemfile.lock
@ -8,22 +8,22 @@ GEM
|
|||||||
builder (3.2.2)
|
builder (3.2.2)
|
||||||
childprocess (0.3.9)
|
childprocess (0.3.9)
|
||||||
ffi (~> 1.0, >= 1.0.11)
|
ffi (~> 1.0, >= 1.0.11)
|
||||||
cucumber (1.3.5)
|
cucumber (1.3.8)
|
||||||
builder (>= 2.1.2)
|
builder (>= 2.1.2)
|
||||||
diff-lcs (>= 1.1.3)
|
diff-lcs (>= 1.1.3)
|
||||||
gherkin (~> 2.12.0)
|
gherkin (~> 2.12.1)
|
||||||
multi_json (~> 1.7.5)
|
multi_json (>= 1.7.5, < 2.0)
|
||||||
multi_test (>= 0.0.2)
|
multi_test (>= 0.0.2)
|
||||||
diff-lcs (1.2.4)
|
diff-lcs (1.2.4)
|
||||||
ejt_command_line (0.0.2)
|
ejt_command_line (0.0.2)
|
||||||
ffi (1.9.0)
|
ffi (1.9.0)
|
||||||
gherkin (2.12.0)
|
gherkin (2.12.1)
|
||||||
multi_json (~> 1.3)
|
multi_json (~> 1.3)
|
||||||
multi_json (1.7.7)
|
multi_json (1.8.0)
|
||||||
multi_test (0.0.2)
|
multi_test (0.0.2)
|
||||||
rspec-expectations (2.14.0)
|
rspec-expectations (2.14.3)
|
||||||
diff-lcs (>= 1.1.3, < 2.0)
|
diff-lcs (>= 1.1.3, < 2.0)
|
||||||
thinp_xml (0.0.6)
|
thinp_xml (0.0.12)
|
||||||
ejt_command_line (= 0.0.2)
|
ejt_command_line (= 0.0.2)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
|
78
Makefile.in
78
Makefile.in
@ -20,6 +20,11 @@
|
|||||||
|
|
||||||
V=@
|
V=@
|
||||||
PROGRAMS=\
|
PROGRAMS=\
|
||||||
|
cache_check \
|
||||||
|
cache_dump \
|
||||||
|
cache_restore \
|
||||||
|
cache_repair \
|
||||||
|
\
|
||||||
thin_check \
|
thin_check \
|
||||||
thin_dump \
|
thin_dump \
|
||||||
thin_restore \
|
thin_restore \
|
||||||
@ -29,29 +34,35 @@ PROGRAMS=\
|
|||||||
|
|
||||||
all: $(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/checksum.cc \
|
||||||
persistent-data/endian_utils.cc \
|
persistent-data/endian_utils.cc \
|
||||||
persistent-data/error_set.cc \
|
persistent-data/error_set.cc \
|
||||||
|
persistent-data/file_utils.cc \
|
||||||
persistent-data/hex_dump.cc \
|
persistent-data/hex_dump.cc \
|
||||||
persistent-data/lock_tracker.cc \
|
persistent-data/lock_tracker.cc \
|
||||||
persistent-data/transaction_manager.cc \
|
persistent-data/transaction_manager.cc \
|
||||||
\
|
\
|
||||||
|
persistent-data/data-structures/bitset.cc \
|
||||||
persistent-data/data-structures/btree.cc \
|
persistent-data/data-structures/btree.cc \
|
||||||
\
|
\
|
||||||
persistent-data/space_map.cc \
|
persistent-data/space_map.cc \
|
||||||
persistent-data/space-maps/disk.cc \
|
persistent-data/space-maps/disk.cc \
|
||||||
persistent-data/space-maps/recursive.cc \
|
persistent-data/space-maps/recursive.cc \
|
||||||
persistent-data/space-maps/careful_alloc.cc
|
persistent-data/space-maps/careful_alloc.cc \
|
||||||
#PDATA_OBJECTS=$(subst .cc,.o,$(PDATA_SOURCE))
|
|
||||||
|
|
||||||
SOURCE=\
|
|
||||||
$(PDATA_SOURCE) \
|
|
||||||
\
|
|
||||||
cache/metadata_disk_structures.cc \
|
|
||||||
\
|
\
|
||||||
thin-provisioning/device_tree.cc \
|
thin-provisioning/device_tree.cc \
|
||||||
thin-provisioning/file_utils.cc \
|
|
||||||
thin-provisioning/human_readable_format.cc \
|
thin-provisioning/human_readable_format.cc \
|
||||||
thin-provisioning/mapping_tree.cc \
|
thin-provisioning/mapping_tree.cc \
|
||||||
thin-provisioning/metadata.cc \
|
thin-provisioning/metadata.cc \
|
||||||
@ -66,7 +77,8 @@ SOURCE=\
|
|||||||
PDATA_OBJECTS=$(subst .cc,.o,$(SOURCE))
|
PDATA_OBJECTS=$(subst .cc,.o,$(SOURCE))
|
||||||
|
|
||||||
CXX_PROGRAM_SOURCE=\
|
CXX_PROGRAM_SOURCE=\
|
||||||
cache/check.cc \
|
caching/cache_check.cc \
|
||||||
|
caching/cache_restore.cc \
|
||||||
\
|
\
|
||||||
thin-provisioning/thin_check.cc \
|
thin-provisioning/thin_check.cc \
|
||||||
thin-provisioning/thin_dump.cc \
|
thin-provisioning/thin_dump.cc \
|
||||||
@ -140,9 +152,11 @@ THIN_DUMP_SOURCE=$(SOURCE)
|
|||||||
THIN_REPAIR_SOURCE=$(SOURCE)
|
THIN_REPAIR_SOURCE=$(SOURCE)
|
||||||
THIN_RESTORE_SOURCE=$(SOURCE)
|
THIN_RESTORE_SOURCE=$(SOURCE)
|
||||||
THIN_CHECK_SOURCE=\
|
THIN_CHECK_SOURCE=\
|
||||||
|
base/error_state.cc \
|
||||||
persistent-data/checksum.cc \
|
persistent-data/checksum.cc \
|
||||||
persistent-data/endian_utils.cc \
|
persistent-data/endian_utils.cc \
|
||||||
persistent-data/error_set.cc \
|
persistent-data/error_set.cc \
|
||||||
|
persistent-data/file_utils.cc \
|
||||||
persistent-data/hex_dump.cc \
|
persistent-data/hex_dump.cc \
|
||||||
persistent-data/lock_tracker.cc \
|
persistent-data/lock_tracker.cc \
|
||||||
persistent-data/data-structures/btree.cc \
|
persistent-data/data-structures/btree.cc \
|
||||||
@ -151,7 +165,6 @@ THIN_CHECK_SOURCE=\
|
|||||||
persistent-data/space-maps/recursive.cc \
|
persistent-data/space-maps/recursive.cc \
|
||||||
persistent-data/space-maps/careful_alloc.cc \
|
persistent-data/space-maps/careful_alloc.cc \
|
||||||
persistent-data/transaction_manager.cc \
|
persistent-data/transaction_manager.cc \
|
||||||
thin-provisioning/file_utils.cc \
|
|
||||||
thin-provisioning/device_tree.cc \
|
thin-provisioning/device_tree.cc \
|
||||||
thin-provisioning/mapping_tree.cc \
|
thin-provisioning/mapping_tree.cc \
|
||||||
thin-provisioning/metadata.cc \
|
thin-provisioning/metadata.cc \
|
||||||
@ -162,6 +175,7 @@ THIN_RMAP_SOURCE=\
|
|||||||
persistent-data/checksum.cc \
|
persistent-data/checksum.cc \
|
||||||
persistent-data/endian_utils.cc \
|
persistent-data/endian_utils.cc \
|
||||||
persistent-data/error_set.cc \
|
persistent-data/error_set.cc \
|
||||||
|
persistent-data/file_utils.cc \
|
||||||
persistent-data/hex_dump.cc \
|
persistent-data/hex_dump.cc \
|
||||||
persistent-data/lock_tracker.cc \
|
persistent-data/lock_tracker.cc \
|
||||||
persistent-data/data-structures/btree.cc \
|
persistent-data/data-structures/btree.cc \
|
||||||
@ -170,7 +184,6 @@ THIN_RMAP_SOURCE=\
|
|||||||
persistent-data/space-maps/recursive.cc \
|
persistent-data/space-maps/recursive.cc \
|
||||||
persistent-data/space-maps/careful_alloc.cc \
|
persistent-data/space-maps/careful_alloc.cc \
|
||||||
persistent-data/transaction_manager.cc \
|
persistent-data/transaction_manager.cc \
|
||||||
thin-provisioning/file_utils.cc \
|
|
||||||
thin-provisioning/device_tree.cc \
|
thin-provisioning/device_tree.cc \
|
||||||
thin-provisioning/mapping_tree.cc \
|
thin-provisioning/mapping_tree.cc \
|
||||||
thin-provisioning/metadata.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
|
thin_metadata_size: thin-provisioning/thin_metadata_size.o
|
||||||
@echo " [LD] $@"
|
@echo " [LD] $@"
|
||||||
$(V) $(CC) $(CFLAGS) -o $@ $+ -lm
|
$(V) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $+ -lm
|
||||||
|
|
||||||
#----------------------------------------------------------------
|
#----------------------------------------------------------------
|
||||||
# Cache tools
|
# Cache tools
|
||||||
|
|
||||||
CACHE_CHECK_SOURCE=\
|
CACHE_CHECK_SOURCE=\
|
||||||
|
base/base64.cc \
|
||||||
|
base/error_state.cc \
|
||||||
persistent-data/checksum.cc \
|
persistent-data/checksum.cc \
|
||||||
persistent-data/endian_utils.cc \
|
persistent-data/endian_utils.cc \
|
||||||
persistent-data/error_set.cc \
|
persistent-data/error_set.cc \
|
||||||
|
persistent-data/file_utils.cc \
|
||||||
persistent-data/hex_dump.cc \
|
persistent-data/hex_dump.cc \
|
||||||
persistent-data/lock_tracker.cc \
|
persistent-data/lock_tracker.cc \
|
||||||
persistent-data/data-structures/btree.cc \
|
persistent-data/data-structures/btree.cc \
|
||||||
|
persistent-data/data-structures/bitset.cc \
|
||||||
persistent-data/space_map.cc \
|
persistent-data/space_map.cc \
|
||||||
persistent-data/space-maps/disk.cc \
|
persistent-data/space-maps/disk.cc \
|
||||||
persistent-data/space-maps/recursive.cc \
|
persistent-data/space-maps/recursive.cc \
|
||||||
persistent-data/space-maps/careful_alloc.cc \
|
persistent-data/space-maps/careful_alloc.cc \
|
||||||
persistent-data/transaction_manager.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_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] $@"
|
@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=\
|
DEPEND_FILES=\
|
||||||
$(subst .cc,.d,$(SOURCE)) \
|
$(subst .cc,.d,$(SOURCE)) \
|
||||||
@ -276,7 +320,7 @@ include unit-tests/Makefile
|
|||||||
|
|
||||||
.PHONEY: features
|
.PHONEY: features
|
||||||
|
|
||||||
features: thin_check cache_check
|
features: $(PROGRAMS)
|
||||||
cucumber --no-color --format progress
|
cucumber --no-color --format progress
|
||||||
|
|
||||||
test: features unit-test
|
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)
|
Scenario: print version (-V flag)
|
||||||
When I run `cache_check -V`
|
When I run `cache_check -V`
|
||||||
|
|
||||||
Then it should pass
|
Then it should pass with version
|
||||||
And version to stdout
|
|
||||||
|
|
||||||
Scenario: print version (--version flag)
|
Scenario: print version (--version flag)
|
||||||
When I run `cache_check --version`
|
When I run `cache_check --version`
|
||||||
|
|
||||||
Then it should pass
|
Then it should pass with version
|
||||||
And version to stdout
|
|
||||||
|
|
||||||
Scenario: print help
|
Scenario: print help
|
||||||
When I run `cache_check --help`
|
When I run `cache_check --help`
|
||||||
@ -56,31 +54,35 @@ Feature: cache_check
|
|||||||
|
|
||||||
Scenario: Metadata file exists, but can't be opened
|
Scenario: Metadata file exists, but can't be opened
|
||||||
Given input without read permissions
|
Given input without read permissions
|
||||||
|
|
||||||
When I run `cache_check input`
|
When I run `cache_check input`
|
||||||
|
|
||||||
Then it should fail
|
Then it should fail
|
||||||
And the stderr should contain:
|
And the stderr should contain:
|
||||||
"""
|
"""
|
||||||
input: Permission denied
|
Permission denied
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Scenario: Metadata file full of zeroes
|
Scenario: Metadata file full of zeroes
|
||||||
Given input file
|
Given input file
|
||||||
And block 1 is zeroed
|
And block 1 is zeroed
|
||||||
|
|
||||||
When I run `cache_check input`
|
When I run `cache_check input`
|
||||||
|
Then it should fail
|
||||||
|
|
||||||
And the stderr should contain:
|
Scenario: --quiet is observed
|
||||||
"""
|
Given input file
|
||||||
input: No superblock found
|
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
|
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}")
|
run_simple("thin_restore -i #{xml_file} -o #{dev_file}")
|
||||||
end
|
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
|
Given(/^a corrupt superblock$/) do
|
||||||
in_current_dir do
|
in_current_dir do
|
||||||
write_valid_xml(xml_file)
|
write_valid_xml(xml_file)
|
||||||
|
@ -39,7 +39,7 @@ module ThinpWorld
|
|||||||
# FIXME: we should really break out the xml stuff from
|
# FIXME: we should really break out the xml stuff from
|
||||||
# thinp-test-suite and put in a gem in this repo.
|
# thinp-test-suite and put in a gem in this repo.
|
||||||
def write_valid_xml(path)
|
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -34,12 +34,25 @@ Feature: thin_restore
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
Scenario: missing input file
|
Scenario: missing input file
|
||||||
|
Given the dev file metadata.bin
|
||||||
When I run thin_restore with -o metadata.bin
|
When I run thin_restore with -o metadata.bin
|
||||||
Then it should fail with:
|
Then it should fail with:
|
||||||
"""
|
"""
|
||||||
No input file provided.
|
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
|
Scenario: dump/restore is a noop
|
||||||
Given valid metadata
|
Given valid metadata
|
||||||
When I dump
|
When I dump
|
||||||
|
@ -65,7 +65,6 @@ namespace persistent_data {
|
|||||||
int fd_;
|
int fd_;
|
||||||
block_address nr_blocks_;
|
block_address nr_blocks_;
|
||||||
mode mode_;
|
mode mode_;
|
||||||
bool writeable_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <uint32_t BlockSize = MD_BLOCK_SIZE>
|
template <uint32_t BlockSize = MD_BLOCK_SIZE>
|
||||||
|
@ -38,8 +38,8 @@ namespace {
|
|||||||
|
|
||||||
int const DEFAULT_MODE = 0666;
|
int const DEFAULT_MODE = 0666;
|
||||||
|
|
||||||
// FIXME: these will slow it down until we start doing asyn io. O_DIRECT | O_SYNC;
|
// FIXME: these will slow it down until we start doing async io.
|
||||||
int const OPEN_FLAGS = 0;
|
int const OPEN_FLAGS = O_DIRECT | O_SYNC;
|
||||||
|
|
||||||
// FIXME: introduce a new exception for this, or at least lift this
|
// FIXME: introduce a new exception for this, or at least lift this
|
||||||
// to exception.h
|
// to exception.h
|
||||||
@ -61,7 +61,7 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool file_exists(string const &path) {
|
bool file_exists(string const &path) {
|
||||||
typename ::stat info;
|
struct ::stat info;
|
||||||
|
|
||||||
int r = ::stat(path.c_str(), &info);
|
int r = ::stat(path.c_str(), &info);
|
||||||
if (r) {
|
if (r) {
|
||||||
|
@ -68,15 +68,29 @@ namespace base {
|
|||||||
v_(v) {
|
v_(v) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct {
|
struct lru {
|
||||||
value_entry *next_, *prev_;
|
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_;
|
value_entry *parent_, *left_, *right_;
|
||||||
int color_;
|
int color_;
|
||||||
} lookup_;
|
};
|
||||||
|
|
||||||
|
lru lru_;
|
||||||
|
lookup lookup_;
|
||||||
unsigned ref_count_;
|
unsigned ref_count_;
|
||||||
value_type v_;
|
value_type v_;
|
||||||
};
|
};
|
||||||
|
@ -21,17 +21,15 @@
|
|||||||
|
|
||||||
#include "persistent-data/math_utils.h"
|
#include "persistent-data/math_utils.h"
|
||||||
#include "persistent-data/data-structures/btree.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"
|
#include "persistent-data/data-structures/array_block.h"
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
// FIXME: we need an array checker
|
|
||||||
|
|
||||||
namespace persistent_data {
|
namespace persistent_data {
|
||||||
namespace array_detail {
|
namespace array_detail {
|
||||||
uint32_t const ARRAY_CSUM_XOR = 595846735;
|
uint32_t const ARRAY_CSUM_XOR = 595846735;
|
||||||
|
|
||||||
// FIXME: this isn't used!
|
|
||||||
struct array_block_validator : public block_manager<>::validator {
|
struct array_block_validator : public block_manager<>::validator {
|
||||||
virtual void check(buffer<> const &b, block_address location) const {
|
virtual void check(buffer<> const &b, block_address location) const {
|
||||||
array_block_disk const *data = reinterpret_cast<array_block_disk const *>(&b);
|
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_entries_in_last_block;
|
||||||
unsigned nr_total_blocks;
|
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>
|
template <typename ValueTraits>
|
||||||
class array {
|
class array : public array_base {
|
||||||
public:
|
public:
|
||||||
class block_ref_counter : public ref_counter<uint64_t> {
|
class block_ref_counter : public ref_counter<uint64_t> {
|
||||||
public:
|
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 typename persistent_data::transaction_manager::ptr tm_ptr;
|
||||||
|
|
||||||
typedef block_manager<>::write_ref write_ref;
|
typedef block_manager<>::write_ref write_ref;
|
||||||
@ -128,20 +206,19 @@ namespace persistent_data {
|
|||||||
|
|
||||||
typedef boost::shared_ptr<array<ValueTraits> > ptr;
|
typedef boost::shared_ptr<array<ValueTraits> > ptr;
|
||||||
typedef typename ValueTraits::value_type value_type;
|
typedef typename ValueTraits::value_type value_type;
|
||||||
|
typedef typename ValueTraits::ref_counter ref_counter;
|
||||||
|
|
||||||
array(tm_ptr tm,
|
array(tm_ptr tm, ref_counter rc)
|
||||||
typename ValueTraits::ref_counter rc)
|
|
||||||
: tm_(tm),
|
: tm_(tm),
|
||||||
entries_per_block_(rblock::calc_max_entries()),
|
entries_per_block_(rblock::calc_max_entries()),
|
||||||
nr_entries_(0),
|
nr_entries_(0),
|
||||||
block_rc_(tm->get_sm(), *this),
|
block_rc_(tm->get_sm(), *this),
|
||||||
block_tree_(tm, block_rc_),
|
block_tree_(tm, block_rc_),
|
||||||
rc_(rc),
|
rc_(rc),
|
||||||
validator_(new block_manager<>::noop_validator()) {
|
validator_(new array_detail::array_block_validator) {
|
||||||
}
|
}
|
||||||
|
|
||||||
array(tm_ptr tm,
|
array(tm_ptr tm, ref_counter rc,
|
||||||
typename ValueTraits::ref_counter rc,
|
|
||||||
block_address root,
|
block_address root,
|
||||||
unsigned nr_entries)
|
unsigned nr_entries)
|
||||||
: tm_(tm),
|
: tm_(tm),
|
||||||
@ -150,7 +227,7 @@ namespace persistent_data {
|
|||||||
block_rc_(tm->get_sm(), *this),
|
block_rc_(tm->get_sm(), *this),
|
||||||
block_tree_(tm, root, block_rc_),
|
block_tree_(tm, root, block_rc_),
|
||||||
rc_(rc),
|
rc_(rc),
|
||||||
validator_(new block_manager<>::noop_validator()) {
|
validator_(new array_detail::array_block_validator) {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned get_nr_entries() const {
|
unsigned get_nr_entries() const {
|
||||||
@ -185,6 +262,15 @@ namespace persistent_data {
|
|||||||
b.set(index % entries_per_block_, value);
|
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:
|
private:
|
||||||
|
|
||||||
struct resizer {
|
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
|
#ifndef BITSET_H
|
||||||
#define BITSET_H
|
#define BITSET_H
|
||||||
|
|
||||||
#include "persistent-data/math_utils.h"
|
#include "persistent-data/run.h"
|
||||||
#include "persistent-data/data-structures/array.h"
|
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
namespace persistent_data {
|
namespace persistent_data {
|
||||||
namespace bitset_detail {
|
namespace bitset_detail {
|
||||||
struct bitset_traits {
|
class bitset_impl;
|
||||||
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) {
|
class missing_bits {
|
||||||
value = base::to_cpu<uint64_t>(disk);
|
public:
|
||||||
|
missing_bits(base::run<uint32_t> const &keys)
|
||||||
|
: keys_(keys) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pack(value_type const &value, disk_type &disk) {
|
base::run<uint32_t> keys_;
|
||||||
disk = base::to_disk<base::le64>(value);
|
};
|
||||||
}
|
|
||||||
|
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 boost::shared_ptr<bitset> ptr;
|
||||||
typedef typename persistent_data::transaction_manager::ptr tm_ptr;
|
typedef typename persistent_data::transaction_manager::ptr tm_ptr;
|
||||||
|
|
||||||
bitset(tm_ptr tm)
|
bitset(tm_ptr tm);
|
||||||
: nr_bits_(0),
|
bitset(tm_ptr tm, block_address root, unsigned nr_bits);
|
||||||
array_(tm, rc_) {
|
block_address get_root() const;
|
||||||
}
|
void grow(unsigned new_nr_bits, bool default_value);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy();
|
void destroy();
|
||||||
|
|
||||||
// May trigger a flush, so cannot be const
|
// May trigger a flush, so cannot be const
|
||||||
bool get(unsigned n) {
|
bool get(unsigned n);
|
||||||
check_bounds(n);
|
void set(unsigned n, bool value);
|
||||||
return get_bit(array_.get(word(n)), bit(n));
|
void flush();
|
||||||
}
|
|
||||||
|
|
||||||
void set(unsigned n, bool value) {
|
void walk_bitset(bitset_detail::bitset_visitor &v) const;
|
||||||
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() {
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void pad_last_block(bool default_value) {
|
boost::shared_ptr<bitset_detail::bitset_impl> impl_;
|
||||||
// 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_;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ namespace persistent_data {
|
|||||||
class ro_spine : private boost::noncopyable {
|
class ro_spine : private boost::noncopyable {
|
||||||
public:
|
public:
|
||||||
ro_spine(transaction_manager::ptr tm,
|
ro_spine(transaction_manager::ptr tm,
|
||||||
typename block_manager<>::validator::ptr v)
|
block_manager<>::validator::ptr v)
|
||||||
: tm_(tm),
|
: tm_(tm),
|
||||||
validator_(v) {
|
validator_(v) {
|
||||||
}
|
}
|
||||||
@ -227,7 +227,7 @@ namespace persistent_data {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
transaction_manager::ptr tm_;
|
transaction_manager::ptr tm_;
|
||||||
typename block_manager<>::validator::ptr validator_;
|
block_manager<>::validator::ptr validator_;
|
||||||
std::list<block_manager<>::read_ref> spine_;
|
std::list<block_manager<>::read_ref> spine_;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -235,9 +235,10 @@ namespace persistent_data {
|
|||||||
public:
|
public:
|
||||||
typedef transaction_manager::read_ref read_ref;
|
typedef transaction_manager::read_ref read_ref;
|
||||||
typedef transaction_manager::write_ref write_ref;
|
typedef transaction_manager::write_ref write_ref;
|
||||||
|
typedef boost::optional<block_address> maybe_block;
|
||||||
|
|
||||||
shadow_spine(transaction_manager::ptr tm,
|
shadow_spine(transaction_manager::ptr tm,
|
||||||
typename block_manager<>::validator::ptr v)
|
block_manager<>::validator::ptr v)
|
||||||
|
|
||||||
: tm_(tm),
|
: tm_(tm),
|
||||||
validator_(v) {
|
validator_(v) {
|
||||||
@ -282,14 +283,17 @@ namespace persistent_data {
|
|||||||
}
|
}
|
||||||
|
|
||||||
block_address get_root() const {
|
block_address get_root() const {
|
||||||
return root_;
|
if (root_)
|
||||||
|
return *root_;
|
||||||
|
|
||||||
|
throw std::runtime_error("shadow spine has no root");
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
transaction_manager::ptr tm_;
|
transaction_manager::ptr tm_;
|
||||||
typename block_manager<>::validator::ptr validator_;
|
block_manager<>::validator::ptr validator_;
|
||||||
std::list<block_manager<>::write_ref> spine_;
|
std::list<block_manager<>::write_ref> spine_;
|
||||||
block_address root_;
|
maybe_block root_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Used to keep a record of a nested btree's position.
|
// Used to keep a record of a nested btree's position.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include "persistent-data/math_utils.h"
|
#include "persistent-data/math_utils.h"
|
||||||
#include "thin-provisioning/file_utils.h"
|
#include "persistent-data/file_utils.h"
|
||||||
|
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
@ -12,7 +12,7 @@ using namespace base;
|
|||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
persistent_data::block_address
|
persistent_data::block_address
|
||||||
thin_provisioning::get_nr_blocks(string const &path)
|
persistent_data::get_nr_blocks(string const &path)
|
||||||
{
|
{
|
||||||
using namespace persistent_data;
|
using namespace persistent_data;
|
||||||
|
|
||||||
@ -47,4 +47,22 @@ thin_provisioning::get_nr_blocks(string const &path)
|
|||||||
return nr_blocks;
|
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 "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);
|
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_),
|
unsigned char const *data = reinterpret_cast<unsigned char const *>(data_),
|
||||||
*end = data + len;
|
*end = data + len;
|
||||||
|
|
||||||
|
ios_base::fmtflags old_flags = out.flags();
|
||||||
out << hex;
|
out << hex;
|
||||||
|
|
||||||
while (data < end) {
|
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 << setw(2) << setfill('0') << (unsigned) *data << " ";
|
||||||
out << endl;
|
out << endl;
|
||||||
}
|
}
|
||||||
out << dec;
|
|
||||||
|
out.setf(old_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
namespace persistent_data {
|
namespace persistent_data {
|
||||||
class subtracting_span_iterator : public space_map::span_iterator {
|
class subtracting_span_iterator : public space_map::span_iterator {
|
||||||
public:
|
public:
|
||||||
typedef typename base::run_set<block_address> block_set;
|
typedef base::run_set<block_address> block_set;
|
||||||
typedef space_map::span span;
|
typedef space_map::span span;
|
||||||
|
|
||||||
subtracting_span_iterator(block_address max,
|
subtracting_span_iterator(block_address max,
|
||||||
|
@ -17,9 +17,9 @@
|
|||||||
// <http://www.gnu.org/licenses/>.
|
// <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include "thin-provisioning/device_tree.h"
|
#include "thin-provisioning/device_tree.h"
|
||||||
#include "thin-provisioning/file_utils.h"
|
|
||||||
#include "thin-provisioning/metadata.h"
|
#include "thin-provisioning/metadata.h"
|
||||||
|
|
||||||
|
#include "persistent-data/file_utils.h"
|
||||||
#include "persistent-data/math_utils.h"
|
#include "persistent-data/math_utils.h"
|
||||||
#include "persistent-data/space-maps/core.h"
|
#include "persistent-data/space-maps/core.h"
|
||||||
#include "persistent-data/space-maps/disk.h"
|
#include "persistent-data/space-maps/disk.h"
|
||||||
@ -40,15 +40,6 @@ namespace {
|
|||||||
|
|
||||||
unsigned const METADATA_CACHE_SIZE = 1024;
|
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
|
transaction_manager::ptr
|
||||||
open_tm(block_manager<>::ptr bm) {
|
open_tm(block_manager<>::ptr bm) {
|
||||||
space_map::ptr sm(new core_map(bm->get_nr_blocks()));
|
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) {
|
switch (ot) {
|
||||||
case OPEN:
|
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());
|
sb_ = read_superblock(tm_->get_bm());
|
||||||
|
|
||||||
if (sb_.version_ != 1)
|
if (sb_.version_ != 1)
|
||||||
@ -115,7 +106,7 @@ metadata::metadata(std::string const &dev_path, open_type ot,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case CREATE:
|
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();
|
space_map::ptr core = tm_->get_sm();
|
||||||
metadata_sm_ = create_metadata_sm(tm_, tm_->get_bm()->get_nr_blocks());
|
metadata_sm_ = create_metadata_sm(tm_, tm_->get_bm()->get_nr_blocks());
|
||||||
copy_space_maps(metadata_sm_, core);
|
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)
|
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);
|
sb_ = read_superblock(tm_->get_bm(), metadata_snap);
|
||||||
|
|
||||||
// We don't open the metadata sm for a held root
|
// We don't open the metadata sm for a held root
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
// with thin-provisioning-tools. If not, see
|
// with thin-provisioning-tools. If not, see
|
||||||
// <http://www.gnu.org/licenses/>.
|
// <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.h"
|
||||||
#include "thin-provisioning/metadata_checker.h"
|
#include "thin-provisioning/metadata_checker.h"
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ namespace {
|
|||||||
md_sm_(md_sm),
|
md_sm_(md_sm),
|
||||||
data_sm_(data_sm),
|
data_sm_(data_sm),
|
||||||
in_range_(false),
|
in_range_(false),
|
||||||
|
time_(),
|
||||||
found_errors_(false) {
|
found_errors_(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,7 +140,7 @@ namespace {
|
|||||||
|
|
||||||
class details_extractor : public btree<1, device_tree_detail::device_details_traits>::visitor {
|
class details_extractor : public btree<1, device_tree_detail::device_details_traits>::visitor {
|
||||||
public:
|
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 boost::shared_ptr<details_extractor> ptr;
|
||||||
typedef btree_checker<1, device_tree_detail::device_details_traits> checker;
|
typedef btree_checker<1, device_tree_detail::device_details_traits> checker;
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ namespace {
|
|||||||
restorer(metadata::ptr md)
|
restorer(metadata::ptr md)
|
||||||
: md_(md),
|
: md_(md),
|
||||||
in_superblock_(false),
|
in_superblock_(false),
|
||||||
|
nr_data_blocks_(),
|
||||||
empty_mapping_(new_mapping_tree()) {
|
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.metadata_nr_blocks_ = to_cpu<uint64_t>(disk.metadata_nr_blocks_);
|
||||||
|
|
||||||
value.compat_flags_ = to_cpu<uint32_t>(disk.compat_flags_);
|
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_);
|
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.metadata_nr_blocks_ = to_disk<le64>(value.metadata_nr_blocks_);
|
||||||
|
|
||||||
disk.compat_flags_ = to_disk<le32>(value.compat_flags_);
|
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_);
|
disk.incompat_flags_ = to_disk<le32>(value.incompat_flags_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +122,8 @@ namespace thin_provisioning {
|
|||||||
|
|
||||||
persistent_data::block_manager<>::validator::ptr superblock_validator();
|
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);
|
||||||
superblock_detail::superblock read_superblock(persistent_data::block_manager<>::ptr bm, persistent_data::block_address location);
|
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,
|
void check_superblock(persistent_data::block_manager<>::ptr bm,
|
||||||
|
@ -22,129 +22,27 @@
|
|||||||
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
|
#include "base/error_state.h"
|
||||||
|
#include "base/nested_output.h"
|
||||||
#include "persistent-data/space-maps/core.h"
|
#include "persistent-data/space-maps/core.h"
|
||||||
|
#include "persistent-data/file_utils.h"
|
||||||
#include "thin-provisioning/device_tree.h"
|
#include "thin-provisioning/device_tree.h"
|
||||||
#include "thin-provisioning/file_utils.h"
|
|
||||||
#include "thin-provisioning/mapping_tree.h"
|
#include "thin-provisioning/mapping_tree.h"
|
||||||
#include "thin-provisioning/superblock.h"
|
#include "thin-provisioning/superblock.h"
|
||||||
|
|
||||||
|
using namespace base;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace thin_provisioning;
|
using namespace thin_provisioning;
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
namespace {
|
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
|
block_manager<>::ptr
|
||||||
open_bm(string const &path) {
|
open_bm(string const &path) {
|
||||||
block_address nr_blocks = get_nr_blocks(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));
|
return block_manager<>::ptr(new block_manager<>(path, nr_blocks, 1, m));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +72,7 @@ namespace {
|
|||||||
err_ = combine_errors(err_, FATAL);
|
err_ = combine_errors(err_, FATAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
error_state get_error() const {
|
base::error_state get_error() const {
|
||||||
return err_;
|
return err_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +114,8 @@ namespace {
|
|||||||
class mapping_reporter : public mapping_tree_detail::damage_visitor {
|
class mapping_reporter : public mapping_tree_detail::damage_visitor {
|
||||||
public:
|
public:
|
||||||
mapping_reporter(nested_output &out)
|
mapping_reporter(nested_output &out)
|
||||||
: out_(out) {
|
: out_(out),
|
||||||
|
err_(NO_ERROR) {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(mapping_tree_detail::missing_devices const &d) {
|
virtual void visit(mapping_tree_detail::missing_devices const &d) {
|
||||||
@ -344,13 +243,12 @@ namespace {
|
|||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
flags fs = {
|
flags fs;
|
||||||
.check_device_tree = true,
|
fs.check_device_tree = true;
|
||||||
.check_mapping_tree_level1 = true,
|
fs.check_mapping_tree_level1 = true,
|
||||||
.check_mapping_tree_level2 = true,
|
fs.check_mapping_tree_level2 = true,
|
||||||
.ignore_non_fatal_errors = false,
|
fs.ignore_non_fatal_errors = false,
|
||||||
.quiet = false
|
fs.quiet = false;
|
||||||
};
|
|
||||||
|
|
||||||
char const shortopts[] = "qhV";
|
char const shortopts[] = "qhV";
|
||||||
option const longopts[] = {
|
option const longopts[] = {
|
||||||
|
@ -37,8 +37,8 @@ struct flags {
|
|||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int dump(string const &path, ostream *out, string const &format, struct flags &flags,
|
int dump_(string const &path, ostream &out, string const &format, struct flags &flags,
|
||||||
block_address metadata_snap = 0) {
|
block_address metadata_snap) {
|
||||||
try {
|
try {
|
||||||
metadata::ptr md(new metadata(path, metadata_snap));
|
metadata::ptr md(new metadata(path, metadata_snap));
|
||||||
emitter::ptr e;
|
emitter::ptr e;
|
||||||
@ -56,9 +56,9 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (format == "xml")
|
if (format == "xml")
|
||||||
e = create_xml_emitter(*out);
|
e = create_xml_emitter(out);
|
||||||
else if (format == "human_readable")
|
else if (format == "human_readable")
|
||||||
e = create_human_readable_emitter(*out);
|
e = create_human_readable_emitter(out);
|
||||||
else {
|
else {
|
||||||
cerr << "unknown format '" << format << "'" << endl;
|
cerr << "unknown format '" << format << "'" << endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -74,6 +74,15 @@ namespace {
|
|||||||
return 0;
|
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) {
|
void usage(ostream &out, string const &cmd) {
|
||||||
out << "Usage: " << cmd << " [options] {device|file}" << endl
|
out << "Usage: " << cmd << " [options] {device|file}" << endl
|
||||||
<< "Options:" << endl
|
<< "Options:" << endl
|
||||||
@ -94,7 +103,8 @@ int main(int argc, char **argv)
|
|||||||
char *end_ptr;
|
char *end_ptr;
|
||||||
string format = "xml";
|
string format = "xml";
|
||||||
block_address metadata_snap = 0;
|
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[] = {
|
const struct option longopts[] = {
|
||||||
{ "help", no_argument, NULL, 'h'},
|
{ "help", no_argument, NULL, 'h'},
|
||||||
@ -153,5 +163,5 @@ int main(int argc, char **argv)
|
|||||||
return 1;
|
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
|
// with thin-provisioning-tools. If not, see
|
||||||
// <http://www.gnu.org/licenses/>.
|
// <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include "emitter.h"
|
#include "persistent-data/file_utils.h"
|
||||||
#include "human_readable_format.h"
|
#include "thin-provisioning/emitter.h"
|
||||||
#include "metadata.h"
|
#include "thin-provisioning/human_readable_format.h"
|
||||||
#include "restore_emitter.h"
|
#include "thin-provisioning/metadata.h"
|
||||||
#include "xml_format.h"
|
#include "thin-provisioning/restore_emitter.h"
|
||||||
|
#include "thin-provisioning/xml_format.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@ -37,12 +38,16 @@ using namespace persistent_data;
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace thin_provisioning;
|
using namespace thin_provisioning;
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int restore(string const &backup_file, string const &dev) {
|
int restore(string const &backup_file, string const &dev) {
|
||||||
try {
|
try {
|
||||||
// The block size gets updated by the restorer.
|
// The block size gets updated by the restorer.
|
||||||
metadata::ptr md(new metadata(dev, metadata::CREATE, 128, 0));
|
metadata::ptr md(new metadata(dev, metadata::CREATE, 128, 0));
|
||||||
emitter::ptr restorer = create_restore_emitter(md);
|
emitter::ptr restorer = create_restore_emitter(md);
|
||||||
|
|
||||||
|
check_file_exists(backup_file);
|
||||||
ifstream in(backup_file.c_str(), ifstream::in);
|
ifstream in(backup_file.c_str(), ifstream::in);
|
||||||
parse_xml(in, restorer);
|
parse_xml(in, restorer);
|
||||||
|
|
||||||
@ -121,3 +126,5 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
return restore(input, output);
|
return restore(input, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
#include "persistent-data/data-structures/btree_damage_visitor.h"
|
#include "persistent-data/data-structures/btree_damage_visitor.h"
|
||||||
#include "persistent-data/run.h"
|
#include "persistent-data/run.h"
|
||||||
#include "persistent-data/space-maps/core.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/superblock.h"
|
||||||
#include "thin-provisioning/mapping_tree.h"
|
#include "thin-provisioning/mapping_tree.h"
|
||||||
#include "thin-provisioning/rmap_visitor.h"
|
#include "thin-provisioning/rmap_visitor.h"
|
||||||
@ -23,7 +23,7 @@ namespace {
|
|||||||
block_manager<>::ptr
|
block_manager<>::ptr
|
||||||
open_bm(string const &path) {
|
open_bm(string const &path) {
|
||||||
block_address nr_blocks = get_nr_blocks(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));
|
return block_manager<>::ptr(new block_manager<>(path, nr_blocks, 1, m));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +150,14 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
// region
|
// 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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -47,12 +47,14 @@ TEST_SOURCE=\
|
|||||||
\
|
\
|
||||||
unit-tests/array_block_t.cc \
|
unit-tests/array_block_t.cc \
|
||||||
unit-tests/array_t.cc \
|
unit-tests/array_t.cc \
|
||||||
|
unit-tests/base64_t.cc \
|
||||||
unit-tests/bitset_t.cc \
|
unit-tests/bitset_t.cc \
|
||||||
unit-tests/block_t.cc \
|
unit-tests/block_t.cc \
|
||||||
unit-tests/btree_t.cc \
|
unit-tests/btree_t.cc \
|
||||||
unit-tests/btree_damage_visitor_t.cc \
|
unit-tests/btree_damage_visitor_t.cc \
|
||||||
unit-tests/buffer_t.cc \
|
unit-tests/buffer_t.cc \
|
||||||
unit-tests/cache_t.cc \
|
unit-tests/cache_t.cc \
|
||||||
|
unit-tests/cache_superblock_t.cc \
|
||||||
unit-tests/damage_tracker_t.cc \
|
unit-tests/damage_tracker_t.cc \
|
||||||
unit-tests/endian_t.cc \
|
unit-tests/endian_t.cc \
|
||||||
unit-tests/rmap_visitor_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