Merge branch 'master' into held-root
This commit is contained in:
commit
ae220c977d
26
Makefile.in
26
Makefile.in
@ -19,7 +19,8 @@
|
|||||||
.PHONY: all
|
.PHONY: all
|
||||||
|
|
||||||
PROGRAMS=\
|
PROGRAMS=\
|
||||||
thin_repair \
|
thin_debug \
|
||||||
|
thin_check \
|
||||||
thin_dump \
|
thin_dump \
|
||||||
thin_restore
|
thin_restore
|
||||||
|
|
||||||
@ -45,15 +46,15 @@ SOURCE=\
|
|||||||
xml_format.cc
|
xml_format.cc
|
||||||
|
|
||||||
PROGRAM_SOURCE=\
|
PROGRAM_SOURCE=\
|
||||||
|
thin_check.cc \
|
||||||
thin_dump.cc \
|
thin_dump.cc \
|
||||||
thin_repair.cc \
|
|
||||||
thin_restore.cc
|
thin_restore.cc
|
||||||
|
|
||||||
CXX:=@CXX@
|
CXX:=@CXX@
|
||||||
OBJECTS:=$(subst .cc,.o,$(SOURCE))
|
OBJECTS:=$(subst .cc,.o,$(SOURCE))
|
||||||
TOP_DIR:=@top_srcdir@
|
TOP_DIR:=@top_srcdir@
|
||||||
TOP_BUILDDIR:=@top_builddir@
|
TOP_BUILDDIR:=@top_builddir@
|
||||||
CXXFLAGS+=-Wall
|
CXXFLAGS+=-Wall -fno-strict-aliasing
|
||||||
CXXFLAGS+=@CXXOPTIMISE_FLAG@
|
CXXFLAGS+=@CXXOPTIMISE_FLAG@
|
||||||
CXXFLAGS+=@CXXDEBUG_FLAG@
|
CXXFLAGS+=@CXXDEBUG_FLAG@
|
||||||
INCLUDES+=-I$(TOP_BUILDDIR) -I$(TOP_DIR)
|
INCLUDES+=-I$(TOP_BUILDDIR) -I$(TOP_DIR)
|
||||||
@ -61,8 +62,8 @@ LIBS:=-lstdc++
|
|||||||
LIBEXPAT:=-lexpat
|
LIBEXPAT:=-lexpat
|
||||||
INSTALL:=@INSTALL@
|
INSTALL:=@INSTALL@
|
||||||
STRIP=
|
STRIP=
|
||||||
DESTDIR:=@prefix@
|
PREFIX:=@prefix@
|
||||||
BINDIR:=$(DESTDIR)/sbin
|
BINDIR:=$(DESTDIR)$(PREFIX)/sbin
|
||||||
MANPATH:=$(DESTDIR)$(MANDIR)
|
MANPATH:=$(DESTDIR)$(MANDIR)
|
||||||
|
|
||||||
vpath %.cc $(TOP_DIR)
|
vpath %.cc $(TOP_DIR)
|
||||||
@ -85,9 +86,10 @@ test-programs: $(TEST_PROGRAMS)
|
|||||||
%.o: %.cc
|
%.o: %.cc
|
||||||
$(CXX) -c $(INCLUDES) $(CXXFLAGS) -o $@ $<
|
$(CXX) -c $(INCLUDES) $(CXXFLAGS) -o $@ $<
|
||||||
|
|
||||||
|
THIN_DEBUG_SOURCE=$(SOURCE)
|
||||||
THIN_DUMP_SOURCE=$(SOURCE)
|
THIN_DUMP_SOURCE=$(SOURCE)
|
||||||
THIN_RESTORE_SOURCE=$(SOURCE)
|
THIN_RESTORE_SOURCE=$(SOURCE)
|
||||||
THIN_REPAIR_SOURCE=\
|
THIN_CHECK_SOURCE=\
|
||||||
checksum.cc \
|
checksum.cc \
|
||||||
endian_utils.cc \
|
endian_utils.cc \
|
||||||
error_set.cc \
|
error_set.cc \
|
||||||
@ -101,9 +103,13 @@ THIN_REPAIR_SOURCE=\
|
|||||||
space_map_transactional.cc \
|
space_map_transactional.cc \
|
||||||
transaction_manager.cc
|
transaction_manager.cc
|
||||||
|
|
||||||
|
THIN_DEBUG_OBJECTS=$(subst .cc,.o,$(THIN_DEBUG_SOURCE))
|
||||||
THIN_DUMP_OBJECTS=$(subst .cc,.o,$(THIN_DUMP_SOURCE))
|
THIN_DUMP_OBJECTS=$(subst .cc,.o,$(THIN_DUMP_SOURCE))
|
||||||
THIN_RESTORE_OBJECTS=$(subst .cc,.o,$(THIN_RESTORE_SOURCE))
|
THIN_RESTORE_OBJECTS=$(subst .cc,.o,$(THIN_RESTORE_SOURCE))
|
||||||
THIN_REPAIR_OBJECTS=$(subst .cc,.o,$(THIN_REPAIR_SOURCE))
|
THIN_CHECK_OBJECTS=$(subst .cc,.o,$(THIN_CHECK_SOURCE))
|
||||||
|
|
||||||
|
thin_debug: $(THIN_DEBUG_OBJECTS) thin_debug.o
|
||||||
|
$(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT)
|
||||||
|
|
||||||
thin_dump: $(THIN_DUMP_OBJECTS) thin_dump.o
|
thin_dump: $(THIN_DUMP_OBJECTS) thin_dump.o
|
||||||
$(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT)
|
$(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT)
|
||||||
@ -111,7 +117,7 @@ thin_dump: $(THIN_DUMP_OBJECTS) thin_dump.o
|
|||||||
thin_restore: $(THIN_RESTORE_OBJECTS) thin_restore.o
|
thin_restore: $(THIN_RESTORE_OBJECTS) thin_restore.o
|
||||||
$(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT)
|
$(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT)
|
||||||
|
|
||||||
thin_repair: $(THIN_REPAIR_OBJECTS) thin_repair.o
|
thin_check: $(THIN_CHECK_OBJECTS) thin_check.o
|
||||||
$(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS)
|
$(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@ -122,11 +128,11 @@ distclean: clean
|
|||||||
|
|
||||||
install: $(PROGRAMS)
|
install: $(PROGRAMS)
|
||||||
$(INSTALL_DIR) $(BINDIR)
|
$(INSTALL_DIR) $(BINDIR)
|
||||||
$(INSTALL_PROGRAM) thin_repair $(BINDIR)/thin_repair
|
$(INSTALL_PROGRAM) thin_check $(BINDIR)/thin_check
|
||||||
$(INSTALL_PROGRAM) thin_dump $(BINDIR)/thin_dump
|
$(INSTALL_PROGRAM) thin_dump $(BINDIR)/thin_dump
|
||||||
$(INSTALL_PROGRAM) thin_restore $(BINDIR)/thin_restore
|
$(INSTALL_PROGRAM) thin_restore $(BINDIR)/thin_restore
|
||||||
$(INSTALL_DIR) $(MANPATH)/man8
|
$(INSTALL_DIR) $(MANPATH)/man8
|
||||||
$(INSTALL_DATA) man8/thin_repair.8 $(MANPATH)/man8/thin_repair.8
|
$(INSTALL_DATA) man8/thin_check.8 $(MANPATH)/man8/thin_check.8
|
||||||
$(INSTALL_DATA) man8/thin_dump.8 $(MANPATH)/man8/thin_dump.8
|
$(INSTALL_DATA) man8/thin_dump.8 $(MANPATH)/man8/thin_dump.8
|
||||||
$(INSTALL_DATA) man8/thin_restore.8 $(MANPATH)/man8/thin_restore.8
|
$(INSTALL_DATA) man8/thin_restore.8 $(MANPATH)/man8/thin_restore.8
|
||||||
.PHONY: install
|
.PHONY: install
|
||||||
|
@ -18,11 +18,13 @@
|
|||||||
|
|
||||||
#include "btree.h"
|
#include "btree.h"
|
||||||
|
|
||||||
|
#include "errors.h"
|
||||||
#include "checksum.h"
|
#include "checksum.h"
|
||||||
#include "transaction_manager.h"
|
#include "transaction_manager.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace base;
|
||||||
using namespace btree_detail;
|
using namespace btree_detail;
|
||||||
using namespace persistent_data;
|
using namespace persistent_data;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -37,10 +39,10 @@ namespace {
|
|||||||
crc32c sum(BTREE_CSUM_XOR);
|
crc32c sum(BTREE_CSUM_XOR);
|
||||||
sum.append(&n->flags, MD_BLOCK_SIZE - sizeof(uint32_t));
|
sum.append(&n->flags, MD_BLOCK_SIZE - sizeof(uint32_t));
|
||||||
if (sum.get_sum() != to_cpu<uint32_t>(n->csum))
|
if (sum.get_sum() != to_cpu<uint32_t>(n->csum))
|
||||||
throw runtime_error("bad checksum in btree node");
|
throw checksum_error("bad checksum in btree node");
|
||||||
|
|
||||||
if (to_cpu<uint64_t>(n->blocknr) != location)
|
if (to_cpu<uint64_t>(n->blocknr) != location)
|
||||||
throw runtime_error("bad block nr in btree node");
|
throw checksum_error("bad block nr in btree node");
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void prepare(block_manager<>::buffer &b, block_address location) const {
|
virtual void prepare(block_manager<>::buffer &b, block_address location) const {
|
||||||
|
167
btree_checker.h
167
btree_checker.h
@ -51,81 +51,39 @@ namespace persistent_data {
|
|||||||
// Not implemented
|
// Not implemented
|
||||||
// ---------------
|
// ---------------
|
||||||
//
|
//
|
||||||
// - checksum
|
|
||||||
// - leaf | internal flags (this can be inferred from siblings)
|
// - leaf | internal flags (this can be inferred from siblings)
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
template <uint32_t Levels, typename ValueTraits>
|
template <uint32_t Levels, typename ValueTraits>
|
||||||
class btree_checker : public btree<Levels, ValueTraits>::visitor {
|
class btree_checker : public btree<Levels, ValueTraits>::visitor {
|
||||||
public:
|
public:
|
||||||
btree_checker(block_counter &counter)
|
btree_checker(block_counter &counter, bool avoid_repeated_visits = true)
|
||||||
: counter_(counter),
|
: counter_(counter),
|
||||||
errs_(new error_set("btree errors")) {
|
errs_(new error_set("btree errors")),
|
||||||
|
avoid_repeated_visits_(avoid_repeated_visits) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit_internal(unsigned level,
|
bool visit_internal(unsigned level,
|
||||||
bool sub_root,
|
bool sub_root,
|
||||||
optional<uint64_t> key,
|
optional<uint64_t> key,
|
||||||
btree_detail::node_ref<uint64_traits> const &n) {
|
btree_detail::node_ref<uint64_traits> const &n) {
|
||||||
if (already_visited(n))
|
return check_internal(level, sub_root, key, n);
|
||||||
return false;
|
|
||||||
|
|
||||||
check_sum(n);
|
|
||||||
|
|
||||||
if (sub_root)
|
|
||||||
new_root(level);
|
|
||||||
|
|
||||||
check_block_nr(n);
|
|
||||||
check_max_entries(n);
|
|
||||||
check_nr_entries(n, sub_root);
|
|
||||||
check_ordered_keys(n);
|
|
||||||
check_parent_key(sub_root ? optional<uint64_t>() : key, n);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit_internal_leaf(unsigned level,
|
bool visit_internal_leaf(unsigned level,
|
||||||
bool sub_root,
|
bool sub_root,
|
||||||
optional<uint64_t> key,
|
optional<uint64_t> key,
|
||||||
btree_detail::node_ref<uint64_traits> const &n) {
|
btree_detail::node_ref<uint64_traits> const &n) {
|
||||||
if (already_visited(n))
|
return check_leaf(level, sub_root, key, n);
|
||||||
return false;
|
|
||||||
|
|
||||||
check_sum(n);
|
|
||||||
|
|
||||||
if (sub_root)
|
|
||||||
new_root(level);
|
|
||||||
|
|
||||||
check_block_nr(n);
|
|
||||||
check_max_entries(n);
|
|
||||||
check_nr_entries(n, sub_root);
|
|
||||||
check_ordered_keys(n);
|
|
||||||
check_parent_key(sub_root ? optional<uint64_t>() : key, n);
|
|
||||||
check_leaf_key(level, n);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit_leaf(unsigned level,
|
bool visit_leaf(unsigned level,
|
||||||
bool sub_root,
|
bool sub_root,
|
||||||
optional<uint64_t> key,
|
optional<uint64_t> key,
|
||||||
btree_detail::node_ref<ValueTraits> const &n) {
|
btree_detail::node_ref<ValueTraits> const &n) {
|
||||||
if (already_visited(n))
|
return check_leaf(level, sub_root, key, n);
|
||||||
return false;
|
|
||||||
|
|
||||||
check_sum(n);
|
|
||||||
|
|
||||||
if (sub_root)
|
|
||||||
new_root(level);
|
|
||||||
|
|
||||||
check_block_nr(n);
|
|
||||||
check_max_entries(n);
|
|
||||||
check_nr_entries(n, sub_root);
|
|
||||||
check_ordered_keys(n);
|
|
||||||
check_parent_key(sub_root ? optional<uint64_t>() : key, n);
|
|
||||||
check_leaf_key(level, n);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<error_set::ptr> get_errors() const {
|
error_set::ptr get_errors() const {
|
||||||
return errs_;
|
return errs_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,21 +93,66 @@ namespace persistent_data {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool check_internal(unsigned level,
|
||||||
|
bool sub_root,
|
||||||
|
optional<uint64_t> key,
|
||||||
|
btree_detail::node_ref<uint64_traits> const &n) {
|
||||||
|
if (!already_visited(n) &&
|
||||||
|
check_sum(n) &&
|
||||||
|
check_block_nr(n) &&
|
||||||
|
check_max_entries(n) &&
|
||||||
|
check_nr_entries(n, sub_root) &&
|
||||||
|
check_ordered_keys(n) &&
|
||||||
|
check_parent_key(sub_root ? optional<uint64_t>() : key, n)) {
|
||||||
|
if (sub_root)
|
||||||
|
new_root(level);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ValueTraits2>
|
||||||
|
bool check_leaf(unsigned level,
|
||||||
|
bool sub_root,
|
||||||
|
optional<uint64_t> key,
|
||||||
|
btree_detail::node_ref<ValueTraits2> const &n) {
|
||||||
|
if (!already_visited(n) &&
|
||||||
|
check_sum(n) &&
|
||||||
|
check_block_nr(n) &&
|
||||||
|
check_max_entries(n) &&
|
||||||
|
check_nr_entries(n, sub_root) &&
|
||||||
|
check_ordered_keys(n) &&
|
||||||
|
check_parent_key(sub_root ? optional<uint64_t>() : key, n)) {
|
||||||
|
if (sub_root)
|
||||||
|
new_root(level);
|
||||||
|
|
||||||
|
return check_leaf_key(level, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename node>
|
template <typename node>
|
||||||
bool already_visited(node const &n) {
|
bool already_visited(node const &n) {
|
||||||
block_address b = n.get_location();
|
block_address b = n.get_location();
|
||||||
|
|
||||||
counter_.inc(b);
|
counter_.inc(b);
|
||||||
|
|
||||||
if (seen_.count(b) > 0)
|
if (avoid_repeated_visits_) {
|
||||||
return true;
|
if (seen_.count(b) > 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
seen_.insert(b);
|
||||||
|
}
|
||||||
|
|
||||||
seen_.insert(b);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename node>
|
template <typename node>
|
||||||
void check_sum(node const &n) const {
|
bool check_sum(node const &n) const {
|
||||||
crc32c sum(BTREE_CSUM_XOR);
|
crc32c sum(BTREE_CSUM_XOR);
|
||||||
|
|
||||||
disk_node const *data = n.raw();
|
disk_node const *data = n.raw();
|
||||||
@ -160,48 +163,55 @@ namespace persistent_data {
|
|||||||
<< ", sum was " << sum.get_sum()
|
<< ", sum was " << sum.get_sum()
|
||||||
<< ", on disk " << n.get_checksum();
|
<< ", on disk " << n.get_checksum();
|
||||||
errs_->add_child(out.str());
|
errs_->add_child(out.str());
|
||||||
throw runtime_error(out.str());
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename node>
|
template <typename node>
|
||||||
void check_block_nr(node const &n) const {
|
bool check_block_nr(node const &n) const {
|
||||||
if (n.get_location() != n.get_block_nr()) {
|
if (n.get_location() != n.get_block_nr()) {
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
out << "block number mismatch: actually "
|
out << "block number mismatch: actually "
|
||||||
<< n.get_location()
|
<< n.get_location()
|
||||||
<< ", claims " << n.get_block_nr();
|
<< ", claims " << n.get_block_nr();
|
||||||
errs_->add_child(out.str());
|
errs_->add_child(out.str());
|
||||||
throw runtime_error(out.str());
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename node>
|
template <typename node>
|
||||||
void check_max_entries(node const &n) const {
|
bool check_max_entries(node const &n) const {
|
||||||
size_t elt_size = sizeof(uint64_t) + n.get_value_size();
|
size_t elt_size = sizeof(uint64_t) + n.get_value_size();
|
||||||
if (elt_size * n.get_max_entries() + sizeof(node_header) > MD_BLOCK_SIZE) {
|
if (elt_size * n.get_max_entries() + sizeof(node_header) > MD_BLOCK_SIZE) {
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
out << "max entries too large: " << n.get_max_entries();
|
out << "max entries too large: " << n.get_max_entries();
|
||||||
errs_->add_child(out.str());
|
errs_->add_child(out.str());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n.get_max_entries() % 3) {
|
if (n.get_max_entries() % 3) {
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
out << "max entries is not divisible by 3: " << n.get_max_entries();
|
out << "max entries is not divisible by 3: " << n.get_max_entries();
|
||||||
errs_->add_child(out.str());
|
errs_->add_child(out.str());
|
||||||
throw runtime_error(out.str());
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename node>
|
template <typename node>
|
||||||
void check_nr_entries(node const &n, bool is_root) const {
|
bool check_nr_entries(node const &n, bool is_root) const {
|
||||||
if (n.get_nr_entries() > n.get_max_entries()) {
|
if (n.get_nr_entries() > n.get_max_entries()) {
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
out << "bad nr_entries: "
|
out << "bad nr_entries: "
|
||||||
<< n.get_nr_entries() << " < "
|
<< n.get_nr_entries() << " < "
|
||||||
<< n.get_max_entries();
|
<< n.get_max_entries();
|
||||||
errs_->add_child(out.str());
|
errs_->add_child(out.str());
|
||||||
throw std::runtime_error(out.str());
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
block_address min = n.get_max_entries() / 3;
|
block_address min = n.get_max_entries() / 3;
|
||||||
@ -213,16 +223,18 @@ namespace persistent_data {
|
|||||||
<< min
|
<< min
|
||||||
<< "(max_entries = " << n.get_max_entries() << ")";
|
<< "(max_entries = " << n.get_max_entries() << ")";
|
||||||
errs_->add_child(out.str());
|
errs_->add_child(out.str());
|
||||||
throw runtime_error(out.str());
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename node>
|
template <typename node>
|
||||||
void check_ordered_keys(node const &n) const {
|
bool check_ordered_keys(node const &n) const {
|
||||||
unsigned nr_entries = n.get_nr_entries();
|
unsigned nr_entries = n.get_nr_entries();
|
||||||
|
|
||||||
if (nr_entries == 0)
|
if (nr_entries == 0)
|
||||||
return; // can only happen if a root node
|
return true; // can only happen if a root node
|
||||||
|
|
||||||
uint64_t last_key = n.key_at(0);
|
uint64_t last_key = n.key_at(0);
|
||||||
|
|
||||||
@ -231,50 +243,59 @@ namespace persistent_data {
|
|||||||
if (k <= last_key) {
|
if (k <= last_key) {
|
||||||
ostringstream out;
|
ostringstream out;
|
||||||
out << "keys are out of order, " << k << " <= " << last_key;
|
out << "keys are out of order, " << k << " <= " << last_key;
|
||||||
throw runtime_error(out.str());
|
errs_->add_child(out.str());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
last_key = k;
|
last_key = k;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename node>
|
template <typename node>
|
||||||
void check_parent_key(boost::optional<uint64_t> key, node const &n) const {
|
bool check_parent_key(boost::optional<uint64_t> key, node const &n) const {
|
||||||
if (!key)
|
if (!key)
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
if (*key > n.key_at(0)) {
|
if (*key > n.key_at(0)) {
|
||||||
ostringstream out;
|
ostringstream out;
|
||||||
out << "parent key mismatch: parent was " << *key
|
out << "parent key mismatch: parent was " << *key
|
||||||
<< ", but lowest in node was " << n.key_at(0);
|
<< ", but lowest in node was " << n.key_at(0);
|
||||||
throw runtime_error(out.str());
|
errs_->add_child(out.str());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename node>
|
template <typename node>
|
||||||
void check_leaf_key(unsigned level, node const &n) {
|
bool check_leaf_key(unsigned level, node const &n) {
|
||||||
if (n.get_nr_entries() == 0)
|
if (n.get_nr_entries() == 0)
|
||||||
return; // can only happen if a root node
|
return true; // can only happen if a root node
|
||||||
|
|
||||||
if (last_leaf_key_[level] && *last_leaf_key_[level] >= n.key_at(0)) {
|
if (last_leaf_key_[level] && *last_leaf_key_[level] >= n.key_at(0)) {
|
||||||
ostringstream out;
|
ostringstream out;
|
||||||
out << "the last key of the previous leaf was " << *last_leaf_key_[level]
|
out << "the last key of the previous leaf was " << *last_leaf_key_[level]
|
||||||
<< " and the first key of this leaf is " << n.key_at(0);
|
<< " and the first key of this leaf is " << n.key_at(0);
|
||||||
throw runtime_error(out.str());
|
errs_->add_child(out.str());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
last_leaf_key_[level] = n.key_at(n.get_nr_entries() - 1);
|
last_leaf_key_[level] = n.key_at(n.get_nr_entries() - 1);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void new_root(unsigned level) {
|
void new_root(unsigned level) {
|
||||||
// we're starting a new subtree, so should
|
// we're starting a new subtree, so should
|
||||||
// reset the last_leaf value.
|
// reset the last_leaf value.
|
||||||
last_leaf_key_[level] = boost::optional<uint64_t>();
|
last_leaf_key_[level] = boost::optional<uint64_t>();
|
||||||
}
|
}
|
||||||
|
|
||||||
block_counter &counter_;
|
block_counter &counter_;
|
||||||
std::set<block_address> seen_;
|
std::set<block_address> seen_;
|
||||||
error_set::ptr errs_;
|
error_set::ptr errs_;
|
||||||
boost::optional<uint64_t> last_leaf_key_[Levels];
|
boost::optional<uint64_t> last_leaf_key_[Levels];
|
||||||
|
bool avoid_repeated_visits_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,8 @@ namespace thin_provisioning {
|
|||||||
virtual void begin_superblock(std::string const &uuid,
|
virtual void begin_superblock(std::string const &uuid,
|
||||||
uint64_t time,
|
uint64_t time,
|
||||||
uint64_t trans_id,
|
uint64_t trans_id,
|
||||||
uint32_t data_block_size) = 0;
|
uint32_t data_block_size,
|
||||||
|
uint64_t nr_data_blocks) = 0;
|
||||||
virtual void end_superblock() = 0;
|
virtual void end_superblock() = 0;
|
||||||
|
|
||||||
virtual void begin_device(uint32_t dev_id,
|
virtual void begin_device(uint32_t dev_id,
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#ifndef ENDIAN_H
|
#ifndef ENDIAN_H
|
||||||
#define ENDIAN_H
|
#define ENDIAN_H
|
||||||
|
|
||||||
|
#include <endian.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <boost/static_assert.hpp>
|
#include <boost/static_assert.hpp>
|
||||||
|
|
||||||
@ -69,32 +70,32 @@ namespace base {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline uint16_t to_cpu<uint16_t, __le16>(__le16 const &d) {
|
inline uint16_t to_cpu<uint16_t, __le16>(__le16 const &d) {
|
||||||
return d.v_;
|
return le16toh(d.v_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline __le16 to_disk<__le16, uint16_t>(uint16_t const &v) {
|
inline __le16 to_disk<__le16, uint16_t>(uint16_t const &v) {
|
||||||
return __le16(v);
|
return __le16(htole16(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline uint32_t to_cpu<uint32_t, __le32>(__le32 const &d) {
|
inline uint32_t to_cpu<uint32_t, __le32>(__le32 const &d) {
|
||||||
return d.v_;
|
return le32toh(d.v_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline __le32 to_disk<__le32, uint32_t>(uint32_t const &v) {
|
inline __le32 to_disk<__le32, uint32_t>(uint32_t const &v) {
|
||||||
return __le32(v);
|
return __le32(htole32(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline uint64_t to_cpu<uint64_t, __le64>(__le64 const &d) {
|
inline uint64_t to_cpu<uint64_t, __le64>(__le64 const &d) {
|
||||||
return d.v_;
|
return le64toh(d.v_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline __le64 to_disk<__le64, uint64_t>(uint64_t const &v) {
|
inline __le64 to_disk<__le64, uint64_t>(uint64_t const &v) {
|
||||||
return __le64(v);
|
return __le64(htole64(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------
|
//--------------------------------
|
||||||
|
@ -61,6 +61,12 @@ error_set::add_child(string const &err)
|
|||||||
add_child(e);
|
add_child(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
error_set::empty() const
|
||||||
|
{
|
||||||
|
return !children_.size();
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------
|
//--------------------------------
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -43,6 +43,7 @@ namespace persistent_data {
|
|||||||
void add_child(error_set::ptr err);
|
void add_child(error_set::ptr err);
|
||||||
void add_child(boost::optional<error_set::ptr> maybe_errs);
|
void add_child(boost::optional<error_set::ptr> maybe_errs);
|
||||||
void add_child(std::string const &err);
|
void add_child(std::string const &err);
|
||||||
|
bool empty() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string err_;
|
std::string err_;
|
||||||
|
37
errors.h
Normal file
37
errors.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// 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 THINP_EXCEPTION_H
|
||||||
|
#define THINP_EXCEPTION_H
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
class checksum_error : public std::runtime_error {
|
||||||
|
public:
|
||||||
|
explicit checksum_error(std::string const &what)
|
||||||
|
: std::runtime_error(what) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
|
#endif
|
@ -35,11 +35,13 @@ namespace {
|
|||||||
void begin_superblock(string const &uuid,
|
void begin_superblock(string const &uuid,
|
||||||
uint64_t time,
|
uint64_t time,
|
||||||
uint64_t trans_id,
|
uint64_t trans_id,
|
||||||
uint32_t data_block_size) {
|
uint32_t data_block_size,
|
||||||
|
uint64_t nr_data_blocks) {
|
||||||
out_ << "begin superblock: \"" << uuid << "\""
|
out_ << "begin superblock: \"" << uuid << "\""
|
||||||
<< ", " << time
|
<< ", " << time
|
||||||
<< ", " << trans_id
|
<< ", " << trans_id
|
||||||
<< ", " << data_block_size
|
<< ", " << data_block_size
|
||||||
|
<< ", " << nr_data_blocks
|
||||||
<< endl;
|
<< endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
51
man8/thin_check.8
Normal file
51
man8/thin_check.8
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
.TH THIN_REPAIR 8 "Thin Provisioning Tools" "Red Hat, Inc." \" -*- nroff -*-
|
||||||
|
.SH NAME
|
||||||
|
thin_check \- repair thin provisioning metadata on device or file
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B thin_check
|
||||||
|
.RB [ options ]
|
||||||
|
.I {device|file}
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.B thin_check
|
||||||
|
checks thin provisioning metadata created by
|
||||||
|
the device-mapper thin provisioning target on a
|
||||||
|
.I device
|
||||||
|
or
|
||||||
|
.I file.
|
||||||
|
See
|
||||||
|
.B thin_dump(8)
|
||||||
|
how to dump metadata to a file.
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.IP "\fB\-q, \-\-quiet\fP"
|
||||||
|
Suppress output messages, return only exit code.
|
||||||
|
|
||||||
|
.IP "\fB\-h, \-\-help\fP"
|
||||||
|
Print help and exit.
|
||||||
|
|
||||||
|
.IP "\fB\-V, \-\-version\fP"
|
||||||
|
Output version information and exit.
|
||||||
|
|
||||||
|
.SH EXAMPLE
|
||||||
|
Analyses and repairs thin provisioning metadata on logical volume
|
||||||
|
/dev/vg/metadata:
|
||||||
|
.sp
|
||||||
|
.B thin_check /dev/vg/metadata
|
||||||
|
|
||||||
|
The device may not be actively used by the target
|
||||||
|
when repairing!
|
||||||
|
|
||||||
|
.SH DIAGNOSTICS
|
||||||
|
.B thin_check
|
||||||
|
returns an exit code of 0 for success or 1 for error.
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.B thin_dump(8)
|
||||||
|
.B thin_restore(8)
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Joe Thornber <ejt@redhat.com>
|
||||||
|
.br
|
||||||
|
Heinz Mauelshagen <HeinzM@RedHat.com>
|
@ -3,41 +3,49 @@
|
|||||||
thin_dump \- dump thin provisioning metadata from device or file to standard output
|
thin_dump \- dump thin provisioning metadata from device or file to standard output
|
||||||
|
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B thin_dump [options] {metadata device|file}
|
.B thin_dump
|
||||||
|
.RB [options]
|
||||||
|
.I {metadata device|file}
|
||||||
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
thin_dump dumps thin provisioning metadata created by the device-mapper
|
.B thin_dump
|
||||||
|
dumps thin provisioning metadata created by the device-mapper
|
||||||
thin provisioning target on a device or file to standard output for
|
thin provisioning target on a device or file to standard output for
|
||||||
analysis or postprocessing in either XML or human readable format.
|
analysis or postprocessing in either XML or human readable format.
|
||||||
XML formated metadata can be fed into thin_restore (see
|
XML formated metadata can be fed into thin_restore (see
|
||||||
.B thin_restore(8)
|
.BR thin_restore(8) )
|
||||||
) in order to put it back onto a metadata device (to process by
|
in order to put it back onto a metadata device (to process by
|
||||||
the device-mapper target) or file.
|
the device-mapper target) or file.
|
||||||
|
|
||||||
.B thin_dump
|
.IP "\fB\-f, \-\-format\fP \fI{xml|human_readable}\fP".
|
||||||
{-i|--input} {device|file}
|
Print output in XML or human readable format.
|
||||||
[{-f|--format} {xml|human_readable}]
|
|
||||||
|
|
||||||
.B thin_dump
|
.IP "\fB\-r, \-\-repair\fP".
|
||||||
{-h|--help}
|
|
||||||
|
|
||||||
.B thin_dump
|
.IP "\fB\-h, \-\-help\fP".
|
||||||
{-V|--version}
|
Print help and exit.
|
||||||
|
|
||||||
|
.IP "\fB\-V, \-\-version\fP".
|
||||||
|
Output version information and exit.
|
||||||
|
|
||||||
.SH EXAMPLES
|
.SH EXAMPLES
|
||||||
"thin_dump -f human_redable -i /dev/vg/metadata"
|
Dumps the thin provisioning metadata on logical volume /dev/vg/metadata
|
||||||
dumps the thin provisioning metadata on logical volume /dev/vg/metadata
|
to standard output in human readable format:
|
||||||
to standard output in human readable format.
|
.sp
|
||||||
|
.B thin_dump -f human_redable /dev/vg/metadata
|
||||||
|
|
||||||
"thin_dump /dev/vg/metadata"
|
Dumps the thin provisioning metadata on logical volume /dev/vg/metadata
|
||||||
dumps the thin provisioning metadata on logical volume /dev/vg/metadata
|
to standard output in XML format:
|
||||||
to standard output in XML format.
|
.sp
|
||||||
|
.B thin_dump /dev/vg/metadata
|
||||||
|
|
||||||
.SH DIAGNOSTICS
|
.SH DIAGNOSTICS
|
||||||
thin_dump returns an exit code of 0 for success or 1 for error.
|
.B thin_dump
|
||||||
|
returns an exit code of 0 for success or 1 for error.
|
||||||
|
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
.B thin_repair(8), thin_restore(8)
|
.B thin_check(8)
|
||||||
|
.B thin_restore(8)
|
||||||
|
|
||||||
.SH AUTHOR
|
.SH AUTHOR
|
||||||
Joe Thornber <ejt@redhat.com>
|
Joe Thornber <ejt@redhat.com>
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
.TH THIN_REPAIR 8 "Thin Provisioning Tools" "Red Hat, Inc." \" -*- nroff -*-
|
|
||||||
.SH NAME
|
|
||||||
thin_repair \- repair thin provisioning metadata on device or file
|
|
||||||
|
|
||||||
.SH SYNOPSIS
|
|
||||||
.B thin_repair {device|file}
|
|
||||||
|
|
||||||
.SH DESCRIPTION
|
|
||||||
thin_repair checks thin provisioning metadata created by
|
|
||||||
the device-mapper thin provisioning target on a device or file (see
|
|
||||||
.B thin_dump(8)
|
|
||||||
) on how to dump metadata to a file.
|
|
||||||
A future release will be able to fix faulty metadata.
|
|
||||||
|
|
||||||
.B thin_repair (device|file}
|
|
||||||
|
|
||||||
.B thin_repair
|
|
||||||
{-h|--help}
|
|
||||||
|
|
||||||
.B thin_dump
|
|
||||||
{-V|--version}
|
|
||||||
|
|
||||||
.SH EXAMPLE
|
|
||||||
"thin_repair /dev/vg/metadata"
|
|
||||||
analyses and repairs thin provisioning metadata on logical volume
|
|
||||||
/dev/vg/metadata. The device may not be actively used by the target
|
|
||||||
when repairing!
|
|
||||||
|
|
||||||
.SH DIAGNOSTICS
|
|
||||||
thin_repair returns an exit code of 0 for success or 1 for error.
|
|
||||||
|
|
||||||
.SH SEE ALSO
|
|
||||||
.B thin_dump(8), thin_restore(8)
|
|
||||||
|
|
||||||
.SH AUTHOR
|
|
||||||
Joe Thornber <ejt@redhat.com>
|
|
||||||
.br
|
|
||||||
Heinz Mauelshagen <HeinzM@RedHat.com>
|
|
@ -3,37 +3,49 @@
|
|||||||
thin_restore \- restore thin provisioning metadata file to device or file
|
thin_restore \- restore thin provisioning metadata file to device or file
|
||||||
|
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B thin_restore [options] file {metadata device|file}
|
.B thin_restore
|
||||||
|
.RB [ options ]
|
||||||
|
.RB -i
|
||||||
|
.I {device|file}
|
||||||
|
.RB -o
|
||||||
|
.I {device|file}
|
||||||
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
thin_restore restores thin provisioning metadata created by the
|
.B thin_restore
|
||||||
respective device-mapper target dumped into an XML formated (see
|
restores thin provisioning metadata created by the
|
||||||
.B thin_dump(8)
|
respective device-mapper target dumped into an XML formatted (see
|
||||||
) file, which optionally can be preproccessed to another device or file.
|
.BR thin_dump(8) )
|
||||||
|
file, which optionally can be preprocessed to another device or file.
|
||||||
If restored to a metadata device, the metadata can be processed
|
If restored to a metadata device, the metadata can be processed
|
||||||
by the device-mapper target.
|
by the device-mapper target.
|
||||||
|
|
||||||
.B thin_restore
|
.IP "\fB\-i, \-\-input\fP \fI{device|file}\fP"
|
||||||
{-i|--input} {device|file}
|
Input file or device with metadata.
|
||||||
{-o|--output} {device|file}
|
|
||||||
|
|
||||||
.B thin_restore
|
.IP "\fB\-o, \-\-output\fP \fI{device|file}\fP"
|
||||||
-h|--help
|
Output file or device.
|
||||||
|
|
||||||
.B thin_dump
|
.IP "\fB\-h, \-\-help\fP"
|
||||||
{-V|--version}
|
Print help and exit.
|
||||||
|
|
||||||
|
.IP "\fB\-V, \-\-version\fP"
|
||||||
|
Output version information and exit.
|
||||||
|
|
||||||
.SH EXAMPLE
|
.SH EXAMPLE
|
||||||
"thin_restore -i metadata -o /dev/vg/metadata"
|
Restores the XML formatted thin provisioning metadata on file
|
||||||
restores the XML formatted thin provisioning metadata on file "metadata"
|
.B metadata
|
||||||
to logical volume /dev/vg/metadata for further processing by the
|
to logical volume /dev/vg/metadata for further processing by the
|
||||||
respective device-mapper target.
|
respective device-mapper target:
|
||||||
|
.sp
|
||||||
|
.B thin_restore -i metadata -o /dev/vg/metadata
|
||||||
|
|
||||||
.SH DIAGNOSTICS
|
.SH DIAGNOSTICS
|
||||||
thin_dump returns an exit code of 0 for success or 1 for error.
|
.B thin_dump
|
||||||
|
returns an exit code of 0 for success or 1 for error.
|
||||||
|
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
.B thin_dump(8), thin_repair(8)
|
.B thin_dump(8)
|
||||||
|
.B thin_check(8)
|
||||||
|
|
||||||
.SH AUTHOR
|
.SH AUTHOR
|
||||||
Joe Thornber <ejt@redhat.com>
|
Joe Thornber <ejt@redhat.com>
|
||||||
|
@ -45,7 +45,7 @@ namespace {
|
|||||||
crc32c sum(SUPERBLOCK_CSUM_SEED);
|
crc32c sum(SUPERBLOCK_CSUM_SEED);
|
||||||
sum.append(&sbd->flags_, MD_BLOCK_SIZE - sizeof(uint32_t));
|
sum.append(&sbd->flags_, MD_BLOCK_SIZE - sizeof(uint32_t));
|
||||||
if (sum.get_sum() != to_cpu<uint32_t>(sbd->csum_))
|
if (sum.get_sum() != to_cpu<uint32_t>(sbd->csum_))
|
||||||
throw runtime_error("bad checksum in superblock");
|
throw checksum_error("bad checksum in superblock");
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void prepare(block_manager<>::buffer &b, block_address location) const {
|
virtual void prepare(block_manager<>::buffer &b, block_address location) const {
|
||||||
@ -65,8 +65,8 @@ namespace {
|
|||||||
if (r)
|
if (r)
|
||||||
throw runtime_error("Couldn't stat dev path");
|
throw runtime_error("Couldn't stat dev path");
|
||||||
|
|
||||||
if (S_ISREG(info.st_mode))
|
if (S_ISREG(info.st_mode) && info.st_size)
|
||||||
nr_blocks = div_down<block_address>(info.st_size, MD_BLOCK_SIZE);
|
nr_blocks = div_up<block_address>(info.st_size, MD_BLOCK_SIZE);
|
||||||
|
|
||||||
else if (S_ISBLK(info.st_mode)) {
|
else if (S_ISBLK(info.st_mode)) {
|
||||||
// To get the size of a block device we need to
|
// To get the size of a block device we need to
|
||||||
|
@ -27,26 +27,30 @@ namespace {
|
|||||||
// devices having mappings defined, which can later be cross
|
// devices having mappings defined, which can later be cross
|
||||||
// referenced with the details tree. A separate block_counter is
|
// referenced with the details tree. A separate block_counter is
|
||||||
// used to later verify the data space map.
|
// used to later verify the data space map.
|
||||||
class mapping_validator : public btree_checker<2, block_traits> {
|
class mapping_validator : public btree<2, block_traits>::visitor {
|
||||||
public:
|
public:
|
||||||
typedef boost::shared_ptr<mapping_validator> ptr;
|
typedef boost::shared_ptr<mapping_validator> ptr;
|
||||||
|
typedef btree_checker<2, block_traits> checker;
|
||||||
|
|
||||||
mapping_validator(block_counter &metadata_counter, block_counter &data_counter)
|
mapping_validator(block_counter &metadata_counter, block_counter &data_counter)
|
||||||
: btree_checker<2, block_traits>(metadata_counter),
|
: checker_(metadata_counter),
|
||||||
data_counter_(data_counter) {
|
data_counter_(data_counter)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit_internal(unsigned level,
|
||||||
|
bool sub_root,
|
||||||
|
optional<uint64_t> key,
|
||||||
|
btree_detail::node_ref<uint64_traits> const &n) {
|
||||||
|
return checker_.visit_internal(level, sub_root, key, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sharing can only occur in level 1 nodes.
|
|
||||||
// FIXME: not true once we start having held roots.
|
|
||||||
bool visit_internal_leaf(unsigned level,
|
bool visit_internal_leaf(unsigned level,
|
||||||
bool sub_root,
|
bool sub_root,
|
||||||
optional<uint64_t> key,
|
optional<uint64_t> key,
|
||||||
btree_detail::node_ref<uint64_traits> const &n) {
|
btree_detail::node_ref<uint64_traits> const &n) {
|
||||||
|
|
||||||
bool r = btree_checker<2, block_traits>::visit_internal_leaf(level, sub_root, key, n);
|
bool r = checker_.visit_internal_leaf(level, sub_root, key, n);
|
||||||
if (!r && level == 0) {
|
|
||||||
throw runtime_error("unexpected sharing in level 0 of mapping tree.");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < n.get_nr_entries(); i++)
|
for (unsigned i = 0; i < n.get_nr_entries(); i++)
|
||||||
devices_.insert(n.key_at(i));
|
devices_.insert(n.key_at(i));
|
||||||
@ -58,7 +62,7 @@ namespace {
|
|||||||
bool sub_root,
|
bool sub_root,
|
||||||
optional<uint64_t> key,
|
optional<uint64_t> key,
|
||||||
btree_detail::node_ref<block_traits> const &n) {
|
btree_detail::node_ref<block_traits> const &n) {
|
||||||
bool r = btree_checker<2, block_traits>::visit_leaf(level, sub_root, key, n);
|
bool r = checker_.visit_leaf(level, sub_root, key, n);
|
||||||
|
|
||||||
if (r)
|
if (r)
|
||||||
for (unsigned i = 0; i < n.get_nr_entries(); i++)
|
for (unsigned i = 0; i < n.get_nr_entries(); i++)
|
||||||
@ -72,29 +76,46 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
checker checker_;
|
||||||
block_counter &data_counter_;
|
block_counter &data_counter_;
|
||||||
set<uint64_t> devices_;
|
set<uint64_t> devices_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class details_validator : public btree_checker<1, device_details_traits> {
|
class details_validator : public btree<1, device_details_traits>::visitor {
|
||||||
public:
|
public:
|
||||||
typedef boost::shared_ptr<details_validator> ptr;
|
typedef boost::shared_ptr<details_validator> ptr;
|
||||||
|
typedef btree_checker<1, device_details_traits> checker;
|
||||||
|
|
||||||
details_validator(block_counter &counter)
|
details_validator(block_counter &counter)
|
||||||
: btree_checker<1, device_details_traits>(counter) {
|
: checker_(counter) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit_internal(unsigned level,
|
||||||
|
bool sub_root,
|
||||||
|
optional<uint64_t> key,
|
||||||
|
btree_detail::node_ref<uint64_traits> const &n) {
|
||||||
|
return checker_.visit_internal(level, sub_root, key, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit_internal_leaf(unsigned level,
|
||||||
|
bool sub_root,
|
||||||
|
optional<uint64_t> key,
|
||||||
|
btree_detail::node_ref<uint64_traits> const &n) {
|
||||||
|
return checker_.visit_internal_leaf(level, sub_root, key, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit_leaf(unsigned level,
|
bool visit_leaf(unsigned level,
|
||||||
bool sub_root,
|
bool sub_root,
|
||||||
optional<uint64_t> key,
|
optional<uint64_t> key,
|
||||||
btree_detail::node_ref<device_details_traits> const &n) {
|
btree_detail::node_ref<device_details_traits> const &n) {
|
||||||
bool r = btree_checker<1, device_details_traits>::visit_leaf(level, sub_root, key, n);
|
|
||||||
|
|
||||||
if (r)
|
if (!checker_.visit_leaf(level, sub_root, key, n))
|
||||||
for (unsigned i = 0; i < n.get_nr_entries(); i++)
|
return false;
|
||||||
devices_.insert(n.key_at(i));
|
|
||||||
|
|
||||||
return r;
|
for (unsigned i = 0; i < n.get_nr_entries(); i++)
|
||||||
|
devices_.insert(n.key_at(i));
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
set<uint64_t> const &get_devices() const {
|
set<uint64_t> const &get_devices() const {
|
||||||
@ -102,6 +123,7 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
checker checker_;
|
||||||
set<uint64_t> devices_;
|
set<uint64_t> devices_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ void
|
|||||||
superblock_traits::unpack(superblock_disk const &disk, superblock &value)
|
superblock_traits::unpack(superblock_disk const &disk, superblock &value)
|
||||||
{
|
{
|
||||||
value.csum_ = to_cpu<uint32_t>(disk.csum_);
|
value.csum_ = to_cpu<uint32_t>(disk.csum_);
|
||||||
value.flags_ = to_cpu<uint32_t>(disk.csum_);
|
value.flags_ = to_cpu<uint32_t>(disk.flags_);
|
||||||
value.blocknr_ = to_cpu<uint64_t>(disk.blocknr_);
|
value.blocknr_ = to_cpu<uint64_t>(disk.blocknr_);
|
||||||
|
|
||||||
::memcpy(value.uuid_, disk.uuid_, sizeof(value.uuid_));
|
::memcpy(value.uuid_, disk.uuid_, sizeof(value.uuid_));
|
||||||
@ -79,7 +79,7 @@ void
|
|||||||
superblock_traits::pack(superblock const &value, superblock_disk &disk)
|
superblock_traits::pack(superblock const &value, superblock_disk &disk)
|
||||||
{
|
{
|
||||||
disk.csum_ = to_disk<__le32>(value.csum_);
|
disk.csum_ = to_disk<__le32>(value.csum_);
|
||||||
disk.flags_ = to_disk<__le32>(value.csum_);
|
disk.flags_ = to_disk<__le32>(value.flags_);
|
||||||
disk.blocknr_ = to_disk<__le64>(value.blocknr_);
|
disk.blocknr_ = to_disk<__le64>(value.blocknr_);
|
||||||
|
|
||||||
::memcpy(disk.uuid_, value.uuid_, sizeof(disk.uuid_));
|
::memcpy(disk.uuid_, value.uuid_, sizeof(disk.uuid_));
|
||||||
|
@ -27,28 +27,48 @@ namespace {
|
|||||||
class mappings_extractor : public btree<2, block_traits>::visitor {
|
class mappings_extractor : public btree<2, block_traits>::visitor {
|
||||||
public:
|
public:
|
||||||
typedef boost::shared_ptr<mappings_extractor> ptr;
|
typedef boost::shared_ptr<mappings_extractor> ptr;
|
||||||
|
typedef btree_checker<2, block_traits> checker;
|
||||||
|
|
||||||
mappings_extractor(uint64_t dev_id, emitter::ptr e,
|
mappings_extractor(uint64_t dev_id, emitter::ptr e,
|
||||||
space_map::ptr md_sm, space_map::ptr data_sm)
|
space_map::ptr md_sm, space_map::ptr data_sm)
|
||||||
: dev_id_(dev_id),
|
: counter_(),
|
||||||
|
checker_(counter_),
|
||||||
|
dev_id_(dev_id),
|
||||||
e_(e),
|
e_(e),
|
||||||
md_sm_(md_sm),
|
md_sm_(md_sm),
|
||||||
data_sm_(data_sm),
|
data_sm_(data_sm),
|
||||||
in_range_(false) {
|
in_range_(false),
|
||||||
|
found_errors_(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit_internal(unsigned level, bool sub_root, boost::optional<uint64_t> key,
|
bool visit_internal(unsigned level, bool sub_root, boost::optional<uint64_t> key,
|
||||||
btree_detail::node_ref<uint64_traits> const &n) {
|
btree_detail::node_ref<uint64_traits> const &n) {
|
||||||
|
|
||||||
|
if (!checker_.visit_internal(level, sub_root, key, n)) {
|
||||||
|
found_errors_ = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return (sub_root && key) ? (*key == dev_id_) : true;
|
return (sub_root && key) ? (*key == dev_id_) : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit_internal_leaf(unsigned level, bool sub_root, boost::optional<uint64_t> key,
|
bool visit_internal_leaf(unsigned level, bool sub_root, boost::optional<uint64_t> key,
|
||||||
btree_detail::node_ref<uint64_traits> const &n) {
|
btree_detail::node_ref<uint64_traits> const &n) {
|
||||||
|
if (!checker_.visit_internal_leaf(level, sub_root, key, n)) {
|
||||||
|
found_errors_ = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit_leaf(unsigned level, bool sub_root, boost::optional<uint64_t> maybe_key,
|
bool visit_leaf(unsigned level, bool sub_root, boost::optional<uint64_t> maybe_key,
|
||||||
btree_detail::node_ref<block_traits> const &n) {
|
btree_detail::node_ref<block_traits> const &n) {
|
||||||
|
if (!checker_.visit_leaf(level, sub_root, maybe_key, n)) {
|
||||||
|
found_errors_ = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (unsigned i = 0; i < n.get_nr_entries(); i++) {
|
for (unsigned i = 0; i < n.get_nr_entries(); i++) {
|
||||||
block_time bt = n.value_at(i);
|
block_time bt = n.value_at(i);
|
||||||
add_mapping(n.key_at(i), bt.block_, bt.time_);
|
add_mapping(n.key_at(i), bt.block_, bt.time_);
|
||||||
@ -57,11 +77,14 @@ namespace {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void visit_complete() {
|
void visit_complete() {
|
||||||
end_mapping();
|
end_mapping();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool corruption() const {
|
||||||
|
return !checker_.get_errors()->empty();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void start_mapping(uint64_t origin_block, uint64_t dest_block, uint32_t time) {
|
void start_mapping(uint64_t origin_block, uint64_t dest_block, uint32_t time) {
|
||||||
origin_start_ = origin_block;
|
origin_start_ = origin_block;
|
||||||
@ -97,6 +120,9 @@ namespace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Declaration order of counter_ and checker_ is important.
|
||||||
|
block_counter counter_;
|
||||||
|
checker checker_;
|
||||||
uint64_t dev_id_;
|
uint64_t dev_id_;
|
||||||
emitter::ptr e_;
|
emitter::ptr e_;
|
||||||
space_map::ptr md_sm_;
|
space_map::ptr md_sm_;
|
||||||
@ -105,28 +131,34 @@ namespace {
|
|||||||
bool in_range_;
|
bool in_range_;
|
||||||
uint64_t origin_start_, dest_start_, len_;
|
uint64_t origin_start_, dest_start_, len_;
|
||||||
uint32_t time_;
|
uint32_t time_;
|
||||||
|
bool found_errors_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class details_extractor : public btree<1, device_details_traits>::visitor {
|
class details_extractor : public btree<1, device_details_traits>::visitor {
|
||||||
public:
|
public:
|
||||||
typedef boost::shared_ptr<details_extractor> ptr;
|
typedef boost::shared_ptr<details_extractor> ptr;
|
||||||
|
typedef btree_checker<1, device_details_traits> checker;
|
||||||
|
|
||||||
details_extractor() {
|
details_extractor()
|
||||||
|
: counter_(),
|
||||||
|
checker_(counter_, false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit_internal(unsigned level, bool sub_root, boost::optional<uint64_t> key,
|
bool visit_internal(unsigned level, bool sub_root, boost::optional<uint64_t> key,
|
||||||
btree_detail::node_ref<uint64_traits> const &n) {
|
btree_detail::node_ref<uint64_traits> const &n) {
|
||||||
return true;
|
return checker_.visit_internal(level, sub_root, key, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit_internal_leaf(unsigned level, bool sub_root, boost::optional<uint64_t> key,
|
bool visit_internal_leaf(unsigned level, bool sub_root, boost::optional<uint64_t> key,
|
||||||
btree_detail::node_ref<uint64_traits> const &n) {
|
btree_detail::node_ref<uint64_traits> const &n) {
|
||||||
return true;
|
return checker_.visit_internal_leaf(level, sub_root, key, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit_leaf(unsigned level, bool sub_root, boost::optional<uint64_t> maybe_key,
|
bool visit_leaf(unsigned level, bool sub_root, boost::optional<uint64_t> maybe_key,
|
||||||
btree_detail::node_ref<device_details_traits> const &n) {
|
btree_detail::node_ref<device_details_traits> const &n) {
|
||||||
|
if (!checker_.visit_leaf(level, sub_root, maybe_key, n))
|
||||||
|
return false;
|
||||||
|
|
||||||
for (unsigned i = 0; i < n.get_nr_entries(); i++)
|
for (unsigned i = 0; i < n.get_nr_entries(); i++)
|
||||||
devices_.insert(make_pair(n.key_at(i), n.value_at(i)));
|
devices_.insert(make_pair(n.key_at(i), n.value_at(i)));
|
||||||
|
|
||||||
@ -137,7 +169,14 @@ namespace {
|
|||||||
return devices_;
|
return devices_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool corruption() const {
|
||||||
|
return !checker_.get_errors()->empty();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Declaration order of counter_ and checker_ is important.
|
||||||
|
block_counter counter_;
|
||||||
|
checker checker_;
|
||||||
map<uint64_t, device_details> devices_;
|
map<uint64_t, device_details> devices_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -145,13 +184,19 @@ namespace {
|
|||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
void
|
void
|
||||||
thin_provisioning::metadata_dump(metadata::ptr md, emitter::ptr e)
|
thin_provisioning::metadata_dump(metadata::ptr md, emitter::ptr e, bool repair)
|
||||||
{
|
{
|
||||||
e->begin_superblock("", md->sb_.time_, md->sb_.trans_id_, md->sb_.data_block_size_);
|
e->begin_superblock("", md->sb_.time_,
|
||||||
|
md->sb_.trans_id_,
|
||||||
|
md->sb_.data_block_size_,
|
||||||
|
md->data_sm_->get_nr_blocks());
|
||||||
|
|
||||||
details_extractor::ptr de(new details_extractor);
|
details_extractor::ptr de(new details_extractor);
|
||||||
|
|
||||||
md->details_->visit(de);
|
md->details_->visit(de);
|
||||||
|
if (de->corruption() && !repair)
|
||||||
|
throw runtime_error("corruption in device details tree");
|
||||||
|
|
||||||
map<uint64_t, device_details> const &devs = de->get_devices();
|
map<uint64_t, device_details> const &devs = de->get_devices();
|
||||||
|
|
||||||
map<uint64_t, device_details>::const_iterator it, end = devs.end();
|
map<uint64_t, device_details>::const_iterator it, end = devs.end();
|
||||||
@ -168,6 +213,12 @@ thin_provisioning::metadata_dump(metadata::ptr md, emitter::ptr e)
|
|||||||
mappings_extractor::ptr me(new mappings_extractor(dev_id, e, md->metadata_sm_, md->data_sm_));
|
mappings_extractor::ptr me(new mappings_extractor(dev_id, e, md->metadata_sm_, md->data_sm_));
|
||||||
md->mappings_->visit(me);
|
md->mappings_->visit(me);
|
||||||
|
|
||||||
|
if (me->corruption() && !repair) {
|
||||||
|
ostringstream out;
|
||||||
|
out << "corruption in mappings for device " << dev_id;
|
||||||
|
throw runtime_error(out.str());
|
||||||
|
}
|
||||||
|
|
||||||
e->end_device();
|
e->end_device();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,10 @@
|
|||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
namespace thin_provisioning {
|
namespace thin_provisioning {
|
||||||
void metadata_dump(metadata::ptr md, emitter::ptr e);
|
// Set the @repair flag if your metadata is corrupt, and you'd like
|
||||||
|
// the dumper to do it's best to recover info. If not set, any
|
||||||
|
// corruption encountered will cause an exception to be thrown.
|
||||||
|
void metadata_dump(metadata::ptr md, emitter::ptr e, bool repair);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
0
mk_release
Normal file → Executable file
0
mk_release
Normal file → Executable file
@ -33,15 +33,13 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual ~restorer() {
|
virtual ~restorer() {
|
||||||
if (in_superblock_) {
|
|
||||||
throw runtime_error("still in superblock");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void begin_superblock(std::string const &uuid,
|
virtual void begin_superblock(std::string const &uuid,
|
||||||
uint64_t time,
|
uint64_t time,
|
||||||
uint64_t trans_id,
|
uint64_t trans_id,
|
||||||
uint32_t data_block_size) {
|
uint32_t data_block_size,
|
||||||
|
uint64_t nr_data_blocks) {
|
||||||
in_superblock_ = true;
|
in_superblock_ = true;
|
||||||
|
|
||||||
superblock &sb = md_->sb_;
|
superblock &sb = md_->sb_;
|
||||||
@ -49,6 +47,7 @@ namespace {
|
|||||||
sb.time_ = time;
|
sb.time_ = time;
|
||||||
sb.trans_id_ = trans_id;
|
sb.trans_id_ = trans_id;
|
||||||
sb.data_block_size_ = data_block_size;
|
sb.data_block_size_ = data_block_size;
|
||||||
|
md_->data_sm_->extend(nr_data_blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void end_superblock() {
|
virtual void end_superblock() {
|
||||||
@ -72,7 +71,7 @@ namespace {
|
|||||||
|
|
||||||
// Add entry to the details tree
|
// Add entry to the details tree
|
||||||
uint64_t key[1] = {dev};
|
uint64_t key[1] = {dev};
|
||||||
device_details details = {mapped_blocks, trans_id, creation_time, snap_time};
|
device_details details = {mapped_blocks, trans_id, (uint32_t)creation_time, (uint32_t)snap_time};
|
||||||
md_->details_->insert(key, details);
|
md_->details_->insert(key, details);
|
||||||
|
|
||||||
// Insert an empty mapping tree
|
// Insert an empty mapping tree
|
||||||
|
@ -43,10 +43,10 @@ namespace {
|
|||||||
crc32c sum(BITMAP_CSUM_XOR);
|
crc32c sum(BITMAP_CSUM_XOR);
|
||||||
sum.append(&data->not_used, MD_BLOCK_SIZE - sizeof(uint32_t));
|
sum.append(&data->not_used, MD_BLOCK_SIZE - sizeof(uint32_t));
|
||||||
if (sum.get_sum() != to_cpu<uint32_t>(data->csum))
|
if (sum.get_sum() != to_cpu<uint32_t>(data->csum))
|
||||||
throw runtime_error("bad checksum in space map bitmap");
|
throw checksum_error("bad checksum in space map bitmap");
|
||||||
|
|
||||||
if (to_cpu<uint64_t>(data->blocknr) != location)
|
if (to_cpu<uint64_t>(data->blocknr) != location)
|
||||||
throw runtime_error("bad block nr in space map bitmap");
|
throw checksum_error("bad block nr in space map bitmap");
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void prepare(block_manager<>::buffer &b, block_address location) const {
|
virtual void prepare(block_manager<>::buffer &b, block_address location) const {
|
||||||
@ -75,10 +75,10 @@ namespace {
|
|||||||
crc32c sum(INDEX_CSUM_XOR);
|
crc32c sum(INDEX_CSUM_XOR);
|
||||||
sum.append(&mi->padding_, MD_BLOCK_SIZE - sizeof(uint32_t));
|
sum.append(&mi->padding_, MD_BLOCK_SIZE - sizeof(uint32_t));
|
||||||
if (sum.get_sum() != to_cpu<uint32_t>(mi->csum_))
|
if (sum.get_sum() != to_cpu<uint32_t>(mi->csum_))
|
||||||
throw runtime_error("bad checksum in metadata index block");
|
throw checksum_error("bad checksum in metadata index block");
|
||||||
|
|
||||||
if (to_cpu<uint64_t>(mi->blocknr_) != location)
|
if (to_cpu<uint64_t>(mi->blocknr_) != location)
|
||||||
throw runtime_error("bad block nr in metadata index block");
|
throw checksum_error("bad block nr in metadata index block");
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void prepare(block_manager<>::buffer &b, block_address location) const {
|
virtual void prepare(block_manager<>::buffer &b, block_address location) const {
|
||||||
|
@ -29,31 +29,41 @@ using namespace std;
|
|||||||
using namespace thin_provisioning;
|
using namespace thin_provisioning;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int check(string const &path) {
|
int check(string const &path, bool quiet) {
|
||||||
metadata::ptr md(new metadata(path, metadata::OPEN));
|
try {
|
||||||
|
metadata::ptr md(new metadata(path, metadata::OPEN));
|
||||||
|
|
||||||
optional<error_set::ptr> maybe_errors = metadata_check(md);
|
optional<error_set::ptr> maybe_errors = metadata_check(md);
|
||||||
if (maybe_errors) {
|
if (maybe_errors) {
|
||||||
cerr << error_selector(*maybe_errors, 3);
|
if (!quiet)
|
||||||
|
cerr << error_selector(*maybe_errors, 3);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
if (!quiet)
|
||||||
|
cerr << e.what() << endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void usage(string const &cmd) {
|
void usage(ostream &out, string const &cmd) {
|
||||||
cerr << "Usage: " << cmd << " {device|file}" << endl;
|
out << "Usage: " << cmd << " [options] {device|file}" << endl
|
||||||
cerr << "Options:" << endl;
|
<< "Options:" << endl
|
||||||
cerr << " {-h|--help}" << endl;
|
<< " {-q|--quiet}" << endl
|
||||||
cerr << " {-V|--version}" << endl;
|
<< " {-h|--help}" << endl
|
||||||
|
<< " {-V|--version}" << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
const char shortopts[] = "hV";
|
bool quiet = false;
|
||||||
|
const char shortopts[] = "qhV";
|
||||||
const struct option longopts[] = {
|
const struct option longopts[] = {
|
||||||
|
{ "quiet", no_argument, NULL, 'q'},
|
||||||
{ "help", no_argument, NULL, 'h'},
|
{ "help", no_argument, NULL, 'h'},
|
||||||
{ "version", no_argument, NULL, 'V'},
|
{ "version", no_argument, NULL, 'V'},
|
||||||
{ NULL, no_argument, NULL, 0 }
|
{ NULL, no_argument, NULL, 0 }
|
||||||
@ -61,19 +71,29 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
|
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
usage(basename(argv[0]));
|
usage(cout, basename(argv[0]));
|
||||||
return 0;
|
return 0;
|
||||||
case 'V':
|
|
||||||
cerr << THIN_PROVISIONING_TOOLS_VERSION << endl;
|
case 'q':
|
||||||
return 0;
|
quiet = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'V':
|
||||||
|
cout << THIN_PROVISIONING_TOOLS_VERSION << endl;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
default:
|
||||||
|
usage(cerr, basename(argv[0]));
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc != 2) {
|
if (argc == optind) {
|
||||||
usage(basename(argv[0]));
|
cerr << "No input file provided." << endl;
|
||||||
|
usage(cerr, basename(argv[0]));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return check(argv[1]);
|
return check(argv[optind], quiet);
|
||||||
}
|
}
|
345
thin_debug.cc
Normal file
345
thin_debug.cc
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
// 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 <boost/algorithm/string/classification.hpp>
|
||||||
|
#include <boost/algorithm/string/split.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <boost/tuple/tuple.hpp>
|
||||||
|
#include <boost/variant.hpp>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "btree.h"
|
||||||
|
#include "metadata.h"
|
||||||
|
#include "metadata_checker.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
using namespace boost;
|
||||||
|
using namespace persistent_data;
|
||||||
|
using namespace std;
|
||||||
|
using namespace thin_provisioning;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
typedef vector<string> strings;
|
||||||
|
|
||||||
|
class formatter {
|
||||||
|
public:
|
||||||
|
typedef shared_ptr<formatter> ptr;
|
||||||
|
|
||||||
|
virtual ~formatter() {}
|
||||||
|
|
||||||
|
typedef optional<string> maybe_string;
|
||||||
|
|
||||||
|
void field(string const &name, string const &value) {
|
||||||
|
fields_.push_back(field_type(name, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void child(string const &name, formatter::ptr t) {
|
||||||
|
fields_.push_back(field_type(name, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void output(ostream &out, int depth = 0) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
typedef variant<string, ptr> value;
|
||||||
|
typedef tuple<string, value> field_type;
|
||||||
|
|
||||||
|
vector<field_type> fields_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void
|
||||||
|
field(formatter &t, string const &name, T const &value) {
|
||||||
|
t.field(name, lexical_cast<string>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------
|
||||||
|
|
||||||
|
class xml_formatter : public formatter {
|
||||||
|
public:
|
||||||
|
virtual void output(ostream &out, int depth) {
|
||||||
|
indent(depth, out);
|
||||||
|
out << "<fields>" << endl;
|
||||||
|
vector<field_type>::const_iterator it;
|
||||||
|
for (it = fields_.begin(); it != fields_.end(); ++it) {
|
||||||
|
if (string const *s = get<string>(&it->get<1>())) {
|
||||||
|
indent(depth + 1, out);
|
||||||
|
out << "<field key=\""
|
||||||
|
<< it->get<0>()
|
||||||
|
<< "\" value=\""
|
||||||
|
<< *s
|
||||||
|
<< "\"/>"
|
||||||
|
<< endl;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
formatter::ptr f = get<formatter::ptr>(it->get<1>());
|
||||||
|
f->output(out, depth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
indent(depth, out);
|
||||||
|
out << "</fields>" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void indent(int depth, ostream &out) const {
|
||||||
|
for (int i = 0; i < depth * 2; i++)
|
||||||
|
out << ' ';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//--------------------------------
|
||||||
|
|
||||||
|
class command {
|
||||||
|
public:
|
||||||
|
typedef boost::shared_ptr<command> ptr;
|
||||||
|
|
||||||
|
virtual ~command() {}
|
||||||
|
virtual void exec(strings const &args, ostream &out) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class command_interpreter {
|
||||||
|
public:
|
||||||
|
command_interpreter(istream &in, ostream &out)
|
||||||
|
: in_(in),
|
||||||
|
out_(out) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void register_command(string const &str, command::ptr cmd) {
|
||||||
|
commands_.insert(make_pair(str, cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
void enter_main_loop() {
|
||||||
|
while (true)
|
||||||
|
do_once();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
strings read_input() {
|
||||||
|
using namespace boost::algorithm;
|
||||||
|
|
||||||
|
string input;
|
||||||
|
getline(in_, input);
|
||||||
|
|
||||||
|
strings toks;
|
||||||
|
split(toks, input, is_any_of(" \t"), token_compress_on);
|
||||||
|
|
||||||
|
return toks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_once() {
|
||||||
|
if (in_.eof())
|
||||||
|
throw runtime_error("input closed");
|
||||||
|
|
||||||
|
out_ << "> ";
|
||||||
|
strings args = read_input();
|
||||||
|
|
||||||
|
map<string, command::ptr>::iterator it;
|
||||||
|
it = commands_.find(args[0]);
|
||||||
|
if (it == commands_.end())
|
||||||
|
out_ << "Unrecognised command" << endl;
|
||||||
|
else
|
||||||
|
it->second->exec(args, out_);
|
||||||
|
}
|
||||||
|
|
||||||
|
istream &in_;
|
||||||
|
ostream &out_;
|
||||||
|
map <string, command::ptr> commands_;
|
||||||
|
};
|
||||||
|
|
||||||
|
//--------------------------------
|
||||||
|
|
||||||
|
class hello : public command {
|
||||||
|
virtual void exec(strings const &args, ostream &out) {
|
||||||
|
out << "Hello, world!" << endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class show_superblock : public command {
|
||||||
|
public:
|
||||||
|
explicit show_superblock(metadata::ptr md)
|
||||||
|
: md_(md) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void exec(strings const &args, ostream &out) {
|
||||||
|
xml_formatter f;
|
||||||
|
|
||||||
|
superblock const &sb = md_->sb_;
|
||||||
|
|
||||||
|
field(f, "csum", sb.csum_);
|
||||||
|
field(f, "flags", sb.flags_);
|
||||||
|
field(f, "blocknr", sb.blocknr_);
|
||||||
|
field(f, "uuid", sb.uuid_); // FIXME: delimit, and handle non-printable chars
|
||||||
|
field(f, "magic", sb.magic_);
|
||||||
|
field(f, "version", sb.version_);
|
||||||
|
field(f, "time", sb.time_);
|
||||||
|
field(f, "trans id", sb.trans_id_);
|
||||||
|
field(f, "held root", sb.held_root_);
|
||||||
|
field(f, "data mapping root", sb.data_mapping_root_);
|
||||||
|
field(f, "device details root", sb.device_details_root_);
|
||||||
|
field(f, "data block size", sb.data_block_size_);
|
||||||
|
field(f, "metadata block size", sb.metadata_block_size_);
|
||||||
|
field(f, "metadata nr blocks", sb.metadata_nr_blocks_);
|
||||||
|
field(f, "compat flags", sb.compat_flags_);
|
||||||
|
field(f, "compat ro flags", sb.compat_ro_flags_);
|
||||||
|
field(f, "incompat flags", sb.incompat_flags_);
|
||||||
|
|
||||||
|
f.output(out, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
metadata::ptr md_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class device_details_show_traits : public device_details_traits {
|
||||||
|
public:
|
||||||
|
static void show(formatter &f, string const &key, device_details const &value) {
|
||||||
|
field(f, "mapped blocks", value.mapped_blocks_);
|
||||||
|
field(f, "transaction id", value.transaction_id_);
|
||||||
|
field(f, "creation time", value.creation_time_);
|
||||||
|
field(f, "snap time", value.snapshotted_time_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class uint64_show_traits : public uint64_traits {
|
||||||
|
public:
|
||||||
|
static void show(formatter &f, string const &key, uint64_t const &value) {
|
||||||
|
field(f, key, lexical_cast<string>(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class block_show_traits : public block_traits {
|
||||||
|
public:
|
||||||
|
static void show(formatter &f, string const &key, block_time const &value) {
|
||||||
|
field(f, "block", value.block_);
|
||||||
|
field(f, "time", value.time_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ValueTraits>
|
||||||
|
class show_btree_node : public command {
|
||||||
|
public:
|
||||||
|
explicit show_btree_node(metadata::ptr md)
|
||||||
|
: md_(md) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void exec(strings const &args, ostream &out) {
|
||||||
|
using namespace persistent_data::btree_detail;
|
||||||
|
|
||||||
|
if (args.size() != 2)
|
||||||
|
throw runtime_error("incorrect number of arguments");
|
||||||
|
|
||||||
|
block_address block = lexical_cast<block_address>(args[1]);
|
||||||
|
block_manager<>::read_ref rr = md_->tm_->read_lock(block);
|
||||||
|
|
||||||
|
node_ref<uint64_show_traits> n = btree_detail::to_node<uint64_show_traits>(rr);
|
||||||
|
if (n.get_type() == INTERNAL)
|
||||||
|
show_node<uint64_show_traits>(n, out);
|
||||||
|
else {
|
||||||
|
node_ref<ValueTraits> n = btree_detail::to_node<ValueTraits>(rr);
|
||||||
|
show_node<ValueTraits>(n, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename VT>
|
||||||
|
void show_node(node_ref<VT> n, ostream &out) {
|
||||||
|
xml_formatter f;
|
||||||
|
|
||||||
|
field(f, "csum", n.get_checksum());
|
||||||
|
field(f, "blocknr", n.get_location());
|
||||||
|
field(f, "type", n.get_type() == INTERNAL ? "internal" : "leaf");
|
||||||
|
field(f, "nr entries", n.get_nr_entries());
|
||||||
|
field(f, "max entries", n.get_max_entries());
|
||||||
|
field(f, "value size", n.get_value_size());
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < n.get_nr_entries(); i++) {
|
||||||
|
formatter::ptr f2(new xml_formatter);
|
||||||
|
field(*f2, "key", n.key_at(i));
|
||||||
|
VT::show(*f2, "value", n.value_at(i));
|
||||||
|
f.child("child", f2);
|
||||||
|
}
|
||||||
|
|
||||||
|
f.output(out, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata::ptr md_;
|
||||||
|
};
|
||||||
|
|
||||||
|
//--------------------------------
|
||||||
|
|
||||||
|
int debug(string const &path) {
|
||||||
|
try {
|
||||||
|
metadata::ptr md(new metadata(path, metadata::OPEN));
|
||||||
|
command_interpreter interp(cin, cout);
|
||||||
|
interp.register_command("hello", command::ptr(new hello));
|
||||||
|
interp.register_command("superblock", command::ptr(new show_superblock(md)));
|
||||||
|
interp.register_command("m1_node", command::ptr(new show_btree_node<uint64_show_traits>(md)));
|
||||||
|
interp.register_command("m2_node", command::ptr(new show_btree_node<block_show_traits>(md)));
|
||||||
|
interp.register_command("detail_node", command::ptr(new show_btree_node<device_details_show_traits>(md)));
|
||||||
|
interp.enter_main_loop();
|
||||||
|
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
cerr << e.what();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void usage(string const &cmd) {
|
||||||
|
cerr << "Usage: " << cmd << " {device|file}" << endl
|
||||||
|
<< "Options:" << endl
|
||||||
|
<< " {-h|--help}" << endl
|
||||||
|
<< " {-V|--version}" << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
const char shortopts[] = "hV";
|
||||||
|
const struct option longopts[] = {
|
||||||
|
{ "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(basename(argv[0]));
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case 'V':
|
||||||
|
cerr << THIN_PROVISIONING_TOOLS_VERSION << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc == optind) {
|
||||||
|
usage(basename(argv[0]));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return debug(argv[optind]);
|
||||||
|
}
|
98
thin_dump.cc
98
thin_dump.cc
@ -30,77 +30,83 @@ using namespace persistent_data;
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace thin_provisioning;
|
using namespace thin_provisioning;
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void dump(string const &path, string const &format) {
|
int dump(string const &path, string const &format, bool repair) {
|
||||||
metadata::ptr md(new metadata(path, metadata::OPEN));
|
try {
|
||||||
emitter::ptr e;
|
metadata::ptr md(new metadata(path, metadata::OPEN));
|
||||||
|
emitter::ptr e;
|
||||||
|
|
||||||
if (format == "xml")
|
if (format == "xml")
|
||||||
e = create_xml_emitter(cout);
|
e = create_xml_emitter(cout);
|
||||||
else if (format == "human_readable")
|
else if (format == "human_readable")
|
||||||
e = create_human_readable_emitter(cout);
|
e = create_human_readable_emitter(cout);
|
||||||
else {
|
else {
|
||||||
cerr << "unknown format '" << format << "'" << endl;
|
cerr << "unknown format '" << format << "'" << endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata_dump(md, e, repair);
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
cerr << e.what() << endl;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata_dump(md, e);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void usage(string const &cmd) {
|
void usage(ostream &out, string const &cmd) {
|
||||||
cerr << "Usage: " << cmd << " [options] {device|file}" << endl << endl;
|
out << "Usage: " << cmd << " [options] {device|file}" << endl
|
||||||
cerr << "Options:" << endl;
|
<< "Options:" << endl
|
||||||
cerr << " {-h|--help}" << endl;
|
<< " {-h|--help}" << endl
|
||||||
cerr << " {-f|--format} {xml|human_readable}" << endl;
|
<< " {-f|--format} {xml|human_readable}" << endl
|
||||||
cerr << " {-i|--input} {xml|human_readable} input_file" << endl;
|
<< " {-r|--repair}" << endl
|
||||||
cerr << " {-V|--version}" << endl;
|
<< " {-V|--version}" << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
const char shortopts[] = "hf:i:V";
|
bool repair = false;
|
||||||
string filename, format = "xml";
|
const char shortopts[] = "hf:rV";
|
||||||
|
string format = "xml";
|
||||||
const struct option longopts[] = {
|
const struct option longopts[] = {
|
||||||
{ "help", no_argument, NULL, 'h'},
|
{ "help", no_argument, NULL, 'h'},
|
||||||
{ "format", required_argument, NULL, 'f' },
|
{ "format", required_argument, NULL, 'f' },
|
||||||
{ "input", required_argument, NULL, 'i'},
|
{ "repair", no_argument, NULL, 'r'},
|
||||||
{ "version", no_argument, NULL, 'V'},
|
{ "version", no_argument, NULL, 'V'},
|
||||||
{ NULL, no_argument, NULL, 0 }
|
{ NULL, no_argument, NULL, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
|
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
usage(basename(argv[0]));
|
usage(cout, basename(argv[0]));
|
||||||
return 0;
|
return 0;
|
||||||
case 'f':
|
|
||||||
format = optarg;
|
case 'f':
|
||||||
break;
|
format = optarg;
|
||||||
case 'i':
|
break;
|
||||||
filename = optarg;
|
|
||||||
break;
|
case 'r':
|
||||||
case 'V':
|
repair = true;
|
||||||
cerr << THIN_PROVISIONING_TOOLS_VERSION << endl;
|
break;
|
||||||
return 0;
|
|
||||||
|
case 'V':
|
||||||
|
cout << THIN_PROVISIONING_TOOLS_VERSION << endl;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
default:
|
||||||
|
usage(cerr, basename(argv[0]));
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc == 1) {
|
if (argc == optind) {
|
||||||
usage(basename(argv[0]));
|
cerr << "No input file provided." << endl;
|
||||||
|
usage(cerr, basename(argv[0]));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filename.empty()) {
|
return dump(argv[optind], format, repair);
|
||||||
cerr << "No output file provided." << endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
dump(filename, format);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
|
||||||
|
@ -24,42 +24,43 @@
|
|||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
#include <iostream>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
using namespace persistent_data;
|
using namespace persistent_data;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace thin_provisioning;
|
using namespace thin_provisioning;
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void restore(string const &backup_file, string const &dev) {
|
int restore(string const &backup_file, string const &dev) {
|
||||||
// FIXME: hard coded
|
try {
|
||||||
block_address const NR_BLOCKS = 100000;
|
// 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, NR_BLOCKS));
|
emitter::ptr restorer = create_restore_emitter(md);
|
||||||
emitter::ptr restorer = create_restore_emitter(md);
|
ifstream in(backup_file.c_str(), ifstream::in);
|
||||||
ifstream in(backup_file.c_str(), ifstream::in);
|
|
||||||
// FIXME:
|
|
||||||
//try {
|
|
||||||
parse_xml(in, restorer);
|
parse_xml(in, restorer);
|
||||||
#if 0
|
|
||||||
} catch (...) {
|
} catch (std::exception &e) {
|
||||||
in.close();
|
cerr << e.what() << endl;
|
||||||
throw;
|
return 1;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void usage(string const &cmd) {
|
void usage(ostream &out, string const &cmd) {
|
||||||
cerr << "Usage: " << cmd << " [options]" << endl << endl;
|
out << "Usage: " << cmd << " [options]" << endl
|
||||||
cerr << "Options:" << endl;
|
<< "Options:" << endl
|
||||||
cerr << " {-h|--help}" << endl;
|
<< " {-h|--help}" << endl
|
||||||
cerr << " {-i|--input} input_file" << endl;
|
<< " {-i|--input} input_file" << endl
|
||||||
cerr << " {-o [ --output} {device|file}" << endl;
|
<< " {-o|--output} {device|file}" << endl
|
||||||
cerr << " {-V|--version}" << endl;
|
<< " {-V|--version}" << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,38 +80,43 @@ int main(int argc, char **argv)
|
|||||||
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
|
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
usage(basename(argv[0]));
|
usage(cout, basename(argv[0]));
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case 'i':
|
case 'i':
|
||||||
input = optarg;
|
input = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'o':
|
case 'o':
|
||||||
output = optarg;
|
output = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'V':
|
case 'V':
|
||||||
cerr << THIN_PROVISIONING_TOOLS_VERSION << endl;
|
cout << THIN_PROVISIONING_TOOLS_VERSION << endl;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
default:
|
||||||
|
usage(cerr, basename(argv[0]));
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc == 1) {
|
if (argc != optind) {
|
||||||
usage(basename(argv[0]));
|
usage(cerr, basename(argv[0]));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.empty()) {
|
if (input.empty()) {
|
||||||
cerr << "No input file provided." << endl;
|
cerr << "No input file provided." << endl;
|
||||||
|
usage(cerr, basename(argv[0]));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output.empty()) {
|
if (output.empty()) {
|
||||||
cerr << "No output file provided." << endl;
|
cerr << "No output file provided." << endl;
|
||||||
|
usage(cerr, basename(argv[0]));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
restore(input, output);
|
return restore(input, output);
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
|
||||||
|
@ -48,12 +48,14 @@ namespace {
|
|||||||
void begin_superblock(string const &uuid,
|
void begin_superblock(string const &uuid,
|
||||||
uint64_t time,
|
uint64_t time,
|
||||||
uint64_t trans_id,
|
uint64_t trans_id,
|
||||||
uint32_t data_block_size) {
|
uint32_t data_block_size,
|
||||||
|
uint64_t nr_data_blocks) {
|
||||||
indent();
|
indent();
|
||||||
out_ << "<superblock uuid=\"" << uuid << "\""
|
out_ << "<superblock uuid=\"" << uuid << "\""
|
||||||
<< " time=\"" << time << "\""
|
<< " time=\"" << time << "\""
|
||||||
<< " transaction=\"" << trans_id << "\""
|
<< " transaction=\"" << trans_id << "\""
|
||||||
<< " data_block_size=\"" << data_block_size << "\">"
|
<< " data_block_size=\"" << data_block_size << "\""
|
||||||
|
<< " nr_data_blocks=\"" << nr_data_blocks << "\">"
|
||||||
<< endl;
|
<< endl;
|
||||||
inc();
|
inc();
|
||||||
}
|
}
|
||||||
@ -176,7 +178,8 @@ namespace {
|
|||||||
e->begin_superblock(get_attr<string>(attr, "uuid"),
|
e->begin_superblock(get_attr<string>(attr, "uuid"),
|
||||||
get_attr<uint64_t>(attr, "time"),
|
get_attr<uint64_t>(attr, "time"),
|
||||||
get_attr<uint64_t>(attr, "transaction"),
|
get_attr<uint64_t>(attr, "transaction"),
|
||||||
get_attr<uint32_t>(attr, "data_block_size"));
|
get_attr<uint32_t>(attr, "data_block_size"),
|
||||||
|
get_attr<uint64_t>(attr, "nr_data_blocks"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse_device(emitter *e, attributes const &attr) {
|
void parse_device(emitter *e, attributes const &attr) {
|
||||||
|
Loading…
Reference in New Issue
Block a user