From a4b41e90ad6b20c80a6345fb33fc946323b11f78 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 27 Feb 2012 14:07:16 +0000 Subject: [PATCH 01/35] introduce checksum_exception --- btree.tcc | 6 ++-- btree_checker.h | 4 +-- errors.h | 37 ++++++++++++++++++++++ metadata.cc | 2 +- space_map_disk.cc | 8 ++--- thin_check.cc | 79 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 127 insertions(+), 9 deletions(-) create mode 100644 errors.h create mode 100644 thin_check.cc diff --git a/btree.tcc b/btree.tcc index ebf0636..fa16243 100644 --- a/btree.tcc +++ b/btree.tcc @@ -18,11 +18,13 @@ #include "btree.h" +#include "errors.h" #include "checksum.h" #include "transaction_manager.h" #include +using namespace base; using namespace btree_detail; using namespace persistent_data; using namespace std; @@ -37,10 +39,10 @@ namespace { crc32c sum(BTREE_CSUM_XOR); sum.append(&n->flags, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(n->csum)) - throw runtime_error("bad checksum in btree node"); + throw checksum_error("bad checksum in btree node"); if (to_cpu(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 { diff --git a/btree_checker.h b/btree_checker.h index 4078034..9b681d6 100644 --- a/btree_checker.h +++ b/btree_checker.h @@ -160,7 +160,7 @@ namespace persistent_data { << ", sum was " << sum.get_sum() << ", on disk " << n.get_checksum(); errs_->add_child(out.str()); - throw runtime_error(out.str()); + throw checksum_error(out.str()); } } @@ -172,7 +172,7 @@ namespace persistent_data { << n.get_location() << ", claims " << n.get_block_nr(); errs_->add_child(out.str()); - throw runtime_error(out.str()); + throw checksum_error(out.str()); } } diff --git a/errors.h b/errors.h new file mode 100644 index 0000000..f0c830a --- /dev/null +++ b/errors.h @@ -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 +// . + +#ifndef THINP_EXCEPTION_H +#define THINP_EXCEPTION_H + +#include + +//---------------------------------------------------------------- + +namespace base { + class checksum_error : public std::runtime_error { + public: + explicit checksum_error(std::string const &what) + : std::runtime_error(what) { + } + }; +} + +//---------------------------------------------------------------- + +#endif diff --git a/metadata.cc b/metadata.cc index 7194dc8..8de2974 100644 --- a/metadata.cc +++ b/metadata.cc @@ -45,7 +45,7 @@ namespace { crc32c sum(SUPERBLOCK_CSUM_SEED); sum.append(&sbd->flags_, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(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 { diff --git a/space_map_disk.cc b/space_map_disk.cc index 42ca283..4c7a7ed 100644 --- a/space_map_disk.cc +++ b/space_map_disk.cc @@ -43,10 +43,10 @@ namespace { crc32c sum(BITMAP_CSUM_XOR); sum.append(&data->not_used, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(data->csum)) - throw runtime_error("bad checksum in space map bitmap"); + throw checksum_error("bad checksum in space map bitmap"); if (to_cpu(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 { @@ -75,10 +75,10 @@ namespace { crc32c sum(INDEX_CSUM_XOR); sum.append(&mi->padding_, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(mi->csum_)) - throw runtime_error("bad checksum in metadata index block"); + throw checksum_error("bad checksum in metadata index block"); if (to_cpu(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 { diff --git a/thin_check.cc b/thin_check.cc new file mode 100644 index 0000000..9993e80 --- /dev/null +++ b/thin_check.cc @@ -0,0 +1,79 @@ +// 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 +// . + +#include +#include +#include + +#include "metadata.h" +#include "metadata_checker.h" +#include "version.h" + +using namespace persistent_data; +using namespace std; +using namespace thin_provisioning; + +namespace { + int check(string const &path) { + metadata::ptr md(new metadata(path, metadata::OPEN)); + + optional maybe_errors = metadata_check(md); + if (maybe_errors) { + cerr << error_selector(*maybe_errors, 3); + return 1; + } + + return 0; + } + + void usage(string const &cmd) { + cerr << "Usage: " << cmd << " {device|file}" << endl; + cerr << "Options:" << endl; + cerr << " {-h|--help}" << endl; + cerr << " {-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 != 2) { + usage(basename(argv[0])); + exit(1); + } + + return check(argv[1]); +} From 5d2f39ad198a4aa590eca1824ad8ed7f50c6452a Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 29 Feb 2012 11:00:13 +0000 Subject: [PATCH 02/35] add -fno-strict-aliasing --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index e737ff1..50bfcca 100644 --- a/Makefile.in +++ b/Makefile.in @@ -53,7 +53,7 @@ CXX:=@CXX@ OBJECTS:=$(subst .cc,.o,$(SOURCE)) TOP_DIR:=@top_srcdir@ TOP_BUILDDIR:=@top_builddir@ -CXXFLAGS+=-Wall +CXXFLAGS+=-Wall -fno-strict-aliasing CXXFLAGS+=@CXXOPTIMISE_FLAG@ CXXFLAGS+=@CXXDEBUG_FLAG@ INCLUDES+=-I$(TOP_BUILDDIR) -I$(TOP_DIR) From 9bf9f2210c4a071d30c9df82924450c9776352e9 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 29 Feb 2012 16:38:33 +0000 Subject: [PATCH 03/35] tweak a comment --- btree_checker.h | 1 - 1 file changed, 1 deletion(-) diff --git a/btree_checker.h b/btree_checker.h index 9b681d6..d1e1425 100644 --- a/btree_checker.h +++ b/btree_checker.h @@ -51,7 +51,6 @@ namespace persistent_data { // Not implemented // --------------- // - // - checksum // - leaf | internal flags (this can be inferred from siblings) //---------------------------------------------------------------- template From 37e915df97c5a238ca93e6ec5f314760d98c00b0 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 29 Feb 2012 16:42:27 +0000 Subject: [PATCH 04/35] simplify metadata_checker via a typedef --- metadata_checker.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/metadata_checker.cc b/metadata_checker.cc index 1dd3231..635142e 100644 --- a/metadata_checker.cc +++ b/metadata_checker.cc @@ -30,9 +30,10 @@ namespace { class mapping_validator : public btree_checker<2, block_traits> { public: typedef boost::shared_ptr ptr; + typedef btree_checker<2, block_traits> super; mapping_validator(block_counter &metadata_counter, block_counter &data_counter) - : btree_checker<2, block_traits>(metadata_counter), + : super(metadata_counter), data_counter_(data_counter) { } @@ -43,7 +44,7 @@ namespace { optional key, btree_detail::node_ref const &n) { - bool r = btree_checker<2, block_traits>::visit_internal_leaf(level, sub_root, key, n); + bool r = super::visit_internal_leaf(level, sub_root, key, n); if (!r && level == 0) { throw runtime_error("unexpected sharing in level 0 of mapping tree."); } @@ -58,7 +59,7 @@ namespace { bool sub_root, optional key, btree_detail::node_ref const &n) { - bool r = btree_checker<2, block_traits>::visit_leaf(level, sub_root, key, n); + bool r = super::visit_leaf(level, sub_root, key, n); if (r) for (unsigned i = 0; i < n.get_nr_entries(); i++) @@ -79,16 +80,17 @@ namespace { class details_validator : public btree_checker<1, device_details_traits> { public: typedef boost::shared_ptr ptr; + typedef btree_checker<1, device_details_traits> super; details_validator(block_counter &counter) - : btree_checker<1, device_details_traits>(counter) { + : super(counter) { } bool visit_leaf(unsigned level, bool sub_root, optional key, btree_detail::node_ref const &n) { - bool r = btree_checker<1, device_details_traits>::visit_leaf(level, sub_root, key, n); + bool r = super::visit_leaf(level, sub_root, key, n); if (r) for (unsigned i = 0; i < n.get_nr_entries(); i++) From 347003e6f330800236b511971f6ef8a9348964c3 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 1 Mar 2012 14:57:09 +0000 Subject: [PATCH 05/35] stop btree_checker throwing. Instead it records the errors and steps around the bad parts of the tree. --- btree_checker.h | 149 +++++++++++++++++++++++++++--------------------- 1 file changed, 83 insertions(+), 66 deletions(-) diff --git a/btree_checker.h b/btree_checker.h index d1e1425..f7d2512 100644 --- a/btree_checker.h +++ b/btree_checker.h @@ -65,63 +65,21 @@ namespace persistent_data { bool sub_root, optional key, btree_detail::node_ref const &n) { - if (already_visited(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() : key, n); - return true; + return check_internal(level, sub_root, key, n); } bool visit_internal_leaf(unsigned level, bool sub_root, optional key, btree_detail::node_ref const &n) { - if (already_visited(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() : key, n); - check_leaf_key(level, n); - - return true; + return check_leaf(level, sub_root, key, n); } bool visit_leaf(unsigned level, bool sub_root, optional key, btree_detail::node_ref const &n) { - if (already_visited(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() : key, n); - check_leaf_key(level, n); - return true; + return check_leaf(level, sub_root, key, n); } boost::optional get_errors() const { @@ -134,6 +92,48 @@ namespace persistent_data { } private: + bool check_internal(unsigned level, + bool sub_root, + optional key, + btree_detail::node_ref 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() : key, n)) { + if (sub_root) + new_root(level); + + return true; + } + + return false; + } + + template + bool check_leaf(unsigned level, + bool sub_root, + optional key, + btree_detail::node_ref 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() : key, n)) { + if (sub_root) + new_root(level); + + return check_leaf_key(level, n); + } + + return false; + } + + template bool already_visited(node const &n) { block_address b = n.get_location(); @@ -148,7 +148,7 @@ namespace persistent_data { } template - void check_sum(node const &n) const { + bool check_sum(node const &n) const { crc32c sum(BTREE_CSUM_XOR); disk_node const *data = n.raw(); @@ -159,48 +159,55 @@ namespace persistent_data { << ", sum was " << sum.get_sum() << ", on disk " << n.get_checksum(); errs_->add_child(out.str()); - throw checksum_error(out.str()); + return false; } + + return true; } template - void check_block_nr(node const &n) const { + bool check_block_nr(node const &n) const { if (n.get_location() != n.get_block_nr()) { std::ostringstream out; out << "block number mismatch: actually " << n.get_location() << ", claims " << n.get_block_nr(); errs_->add_child(out.str()); - throw checksum_error(out.str()); + return false; } + + return true; } template - 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(); if (elt_size * n.get_max_entries() + sizeof(node_header) > MD_BLOCK_SIZE) { std::ostringstream out; out << "max entries too large: " << n.get_max_entries(); errs_->add_child(out.str()); + return false; } if (n.get_max_entries() % 3) { std::ostringstream out; out << "max entries is not divisible by 3: " << n.get_max_entries(); errs_->add_child(out.str()); - throw runtime_error(out.str()); + return false; } + + return true; } template - 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()) { std::ostringstream out; out << "bad nr_entries: " << n.get_nr_entries() << " < " << n.get_max_entries(); errs_->add_child(out.str()); - throw std::runtime_error(out.str()); + return false; } block_address min = n.get_max_entries() / 3; @@ -212,16 +219,18 @@ namespace persistent_data { << min << "(max_entries = " << n.get_max_entries() << ")"; errs_->add_child(out.str()); - throw runtime_error(out.str()); + return false; } + + return true; } template - void check_ordered_keys(node const &n) const { + bool check_ordered_keys(node const &n) const { unsigned nr_entries = n.get_nr_entries(); 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); @@ -230,44 +239,52 @@ namespace persistent_data { if (k <= last_key) { ostringstream out; out << "keys are out of order, " << k << " <= " << last_key; - throw runtime_error(out.str()); + errs_->add_child(out.str()); + return false; } last_key = k; } + + return true; } template - void check_parent_key(boost::optional key, node const &n) const { + bool check_parent_key(boost::optional key, node const &n) const { if (!key) - return; + return true; if (*key > n.key_at(0)) { ostringstream out; out << "parent key mismatch: parent was " << *key << ", but lowest in node was " << n.key_at(0); - throw runtime_error(out.str()); + errs_->add_child(out.str()); + return false; } + + return true; } template - void check_leaf_key(unsigned level, node const &n) { + bool check_leaf_key(unsigned level, node const &n) { 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)) { ostringstream out; 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); - throw runtime_error(out.str()); + errs_->add_child(out.str()); + return false; } last_leaf_key_[level] = n.key_at(n.get_nr_entries() - 1); + return true; } void new_root(unsigned level) { - // we're starting a new subtree, so should - // reset the last_leaf value. - last_leaf_key_[level] = boost::optional(); + // we're starting a new subtree, so should + // reset the last_leaf value. + last_leaf_key_[level] = boost::optional(); } block_counter &counter_; From 606c25d8285172a3588fe9230577fca474dbb891 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 2 Mar 2012 10:00:31 +0000 Subject: [PATCH 06/35] thin_dump now takes a --repair option --- Makefile.in | 14 ++--- btree_checker.h | 17 +++--- error_set.cc | 6 +++ error_set.h | 1 + man8/{thin_repair.8 => thin_check.8} | 0 man8/thin_dump.8 | 3 ++ metadata_checker.cc | 58 ++++++++++++++------- metadata_dumper.cc | 64 ++++++++++++++++++++--- metadata_dumper.h | 5 +- thin_check.cc | 13 +++-- thin_dump.cc | 78 +++++++++++++++++----------- thin_restore.cc | 28 +++++----- 12 files changed, 196 insertions(+), 91 deletions(-) rename man8/{thin_repair.8 => thin_check.8} (100%) diff --git a/Makefile.in b/Makefile.in index 50bfcca..d659b4e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -19,7 +19,7 @@ .PHONY: all PROGRAMS=\ - thin_repair \ + thin_check \ thin_dump \ thin_restore @@ -45,8 +45,8 @@ SOURCE=\ xml_format.cc PROGRAM_SOURCE=\ + thin_check.cc \ thin_dump.cc \ - thin_repair.cc \ thin_restore.cc CXX:=@CXX@ @@ -87,7 +87,7 @@ test-programs: $(TEST_PROGRAMS) THIN_DUMP_SOURCE=$(SOURCE) THIN_RESTORE_SOURCE=$(SOURCE) -THIN_REPAIR_SOURCE=\ +THIN_CHECK_SOURCE=\ checksum.cc \ endian_utils.cc \ error_set.cc \ @@ -103,7 +103,7 @@ THIN_REPAIR_SOURCE=\ THIN_DUMP_OBJECTS=$(subst .cc,.o,$(THIN_DUMP_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_dump: $(THIN_DUMP_OBJECTS) thin_dump.o $(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) @@ -111,7 +111,7 @@ thin_dump: $(THIN_DUMP_OBJECTS) thin_dump.o thin_restore: $(THIN_RESTORE_OBJECTS) thin_restore.o $(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) clean: @@ -122,11 +122,11 @@ distclean: clean install: $(PROGRAMS) $(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_restore $(BINDIR)/thin_restore $(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_restore.8 $(MANPATH)/man8/thin_restore.8 .PHONY: install diff --git a/btree_checker.h b/btree_checker.h index f7d2512..2b579ae 100644 --- a/btree_checker.h +++ b/btree_checker.h @@ -56,9 +56,10 @@ namespace persistent_data { template class btree_checker : public btree::visitor { public: - btree_checker(block_counter &counter) + btree_checker(block_counter &counter, bool avoid_repeated_visits = true) : 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, @@ -82,7 +83,7 @@ namespace persistent_data { return check_leaf(level, sub_root, key, n); } - boost::optional get_errors() const { + error_set::ptr get_errors() const { return errs_; } @@ -140,10 +141,13 @@ namespace persistent_data { counter_.inc(b); - if (seen_.count(b) > 0) - return true; + if (avoid_repeated_visits_) { + if (seen_.count(b) > 0) + return true; + + seen_.insert(b); + } - seen_.insert(b); return false; } @@ -291,6 +295,7 @@ namespace persistent_data { std::set seen_; error_set::ptr errs_; boost::optional last_leaf_key_[Levels]; + bool avoid_repeated_visits_; }; } diff --git a/error_set.cc b/error_set.cc index a8dfae7..d225824 100644 --- a/error_set.cc +++ b/error_set.cc @@ -61,6 +61,12 @@ error_set::add_child(string const &err) add_child(e); } +bool +error_set::empty() const +{ + return !children_.size(); +} + //-------------------------------- namespace { diff --git a/error_set.h b/error_set.h index 52d12df..f3b5ece 100644 --- a/error_set.h +++ b/error_set.h @@ -43,6 +43,7 @@ namespace persistent_data { void add_child(error_set::ptr err); void add_child(boost::optional maybe_errs); void add_child(std::string const &err); + bool empty() const; private: std::string err_; diff --git a/man8/thin_repair.8 b/man8/thin_check.8 similarity index 100% rename from man8/thin_repair.8 rename to man8/thin_check.8 diff --git a/man8/thin_dump.8 b/man8/thin_dump.8 index 5dd8920..82f2e26 100644 --- a/man8/thin_dump.8 +++ b/man8/thin_dump.8 @@ -18,6 +18,9 @@ the device-mapper target) or file. {-i|--input} {device|file} [{-f|--format} {xml|human_readable}] +.B thin_dump + {-r|--repair} + .B thin_dump {-h|--help} diff --git a/metadata_checker.cc b/metadata_checker.cc index 635142e..19a60e9 100644 --- a/metadata_checker.cc +++ b/metadata_checker.cc @@ -27,27 +27,30 @@ namespace { // devices having mappings defined, which can later be cross // referenced with the details tree. A separate block_counter is // 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: typedef boost::shared_ptr ptr; - typedef btree_checker<2, block_traits> super; + typedef btree_checker<2, block_traits> checker; mapping_validator(block_counter &metadata_counter, block_counter &data_counter) - : super(metadata_counter), - data_counter_(data_counter) { + : checker_(metadata_counter), + data_counter_(data_counter) + { + } + + bool visit_internal(unsigned level, + bool sub_root, + optional key, + btree_detail::node_ref 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 sub_root, optional key, btree_detail::node_ref const &n) { - bool r = super::visit_internal_leaf(level, sub_root, key, n); - if (!r && level == 0) { - throw runtime_error("unexpected sharing in level 0 of mapping tree."); - } + bool r = checker_.visit_internal_leaf(level, sub_root, key, n); for (unsigned i = 0; i < n.get_nr_entries(); i++) devices_.insert(n.key_at(i)); @@ -59,7 +62,7 @@ namespace { bool sub_root, optional key, btree_detail::node_ref const &n) { - bool r = super::visit_leaf(level, sub_root, key, n); + bool r = checker_.visit_leaf(level, sub_root, key, n); if (r) for (unsigned i = 0; i < n.get_nr_entries(); i++) @@ -73,30 +76,46 @@ namespace { } private: + checker checker_; block_counter &data_counter_; set devices_; }; - class details_validator : public btree_checker<1, device_details_traits> { + class details_validator : public btree<1, device_details_traits>::visitor { public: typedef boost::shared_ptr ptr; - typedef btree_checker<1, device_details_traits> super; + typedef btree_checker<1, device_details_traits> checker; details_validator(block_counter &counter) - : super(counter) { + : checker_(counter) { + } + + bool visit_internal(unsigned level, + bool sub_root, + optional key, + btree_detail::node_ref const &n) { + return checker_.visit_internal(level, sub_root, key, n); + } + + bool visit_internal_leaf(unsigned level, + bool sub_root, + optional key, + btree_detail::node_ref const &n) { + return checker_.visit_internal_leaf(level, sub_root, key, n); } bool visit_leaf(unsigned level, bool sub_root, optional key, btree_detail::node_ref const &n) { - bool r = super::visit_leaf(level, sub_root, key, n); - if (r) - for (unsigned i = 0; i < n.get_nr_entries(); i++) - devices_.insert(n.key_at(i)); + if (!checker_.visit_leaf(level, sub_root, key, n)) + return false; - return r; + for (unsigned i = 0; i < n.get_nr_entries(); i++) + devices_.insert(n.key_at(i)); + + return true; } set const &get_devices() const { @@ -104,6 +123,7 @@ namespace { } private: + checker checker_; set devices_; }; diff --git a/metadata_dumper.cc b/metadata_dumper.cc index d20bac7..1448ae2 100644 --- a/metadata_dumper.cc +++ b/metadata_dumper.cc @@ -27,28 +27,48 @@ namespace { class mappings_extractor : public btree<2, block_traits>::visitor { public: typedef boost::shared_ptr ptr; + typedef btree_checker<2, block_traits> checker; mappings_extractor(uint64_t dev_id, emitter::ptr e, space_map::ptr md_sm, space_map::ptr data_sm) - : dev_id_(dev_id), + : counter_(), + checker_(counter_), + dev_id_(dev_id), e_(e), md_sm_(md_sm), data_sm_(data_sm), - in_range_(false) { + in_range_(false), + found_errors_(false) { } bool visit_internal(unsigned level, bool sub_root, boost::optional key, btree_detail::node_ref const &n) { + + if (!checker_.visit_internal(level, sub_root, key, n)) { + found_errors_ = true; + return false; + } + return (sub_root && key) ? (*key == dev_id_) : true; } bool visit_internal_leaf(unsigned level, bool sub_root, boost::optional key, btree_detail::node_ref const &n) { + if (!checker_.visit_internal_leaf(level, sub_root, key, n)) { + found_errors_ = true; + return false; + } + return true; } bool visit_leaf(unsigned level, bool sub_root, boost::optional maybe_key, btree_detail::node_ref 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++) { block_time bt = n.value_at(i); add_mapping(n.key_at(i), bt.block_, bt.time_); @@ -57,11 +77,14 @@ namespace { return true; } - void visit_complete() { end_mapping(); } + bool corruption() const { + return !checker_.get_errors()->empty(); + } + private: void start_mapping(uint64_t origin_block, uint64_t dest_block, uint32_t time) { 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_; emitter::ptr e_; space_map::ptr md_sm_; @@ -105,28 +131,34 @@ namespace { bool in_range_; uint64_t origin_start_, dest_start_, len_; uint32_t time_; - + bool found_errors_; }; class details_extractor : public btree<1, device_details_traits>::visitor { public: typedef boost::shared_ptr 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 key, btree_detail::node_ref const &n) { - return true; + return checker_.visit_internal(level, sub_root, key, n); } bool visit_internal_leaf(unsigned level, bool sub_root, boost::optional key, btree_detail::node_ref const &n) { - return true; + return checker_.visit_internal_leaf(level, sub_root, key, n); } bool visit_leaf(unsigned level, bool sub_root, boost::optional maybe_key, btree_detail::node_ref const &n) { + if (!checker_.visit_leaf(level, sub_root, maybe_key, n)) + return false; + for (unsigned i = 0; i < n.get_nr_entries(); i++) devices_.insert(make_pair(n.key_at(i), n.value_at(i))); @@ -137,7 +169,14 @@ namespace { return devices_; } + bool corruption() const { + return !checker_.get_errors()->empty(); + } + private: + // Declaration order of counter_ and checker_ is important. + block_counter counter_; + checker checker_; map devices_; }; } @@ -145,13 +184,16 @@ namespace { //---------------------------------------------------------------- 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_); details_extractor::ptr de(new details_extractor); md->details_->visit(de); + if (de->corruption() && !repair) + throw runtime_error("corruption in device details tree"); + map const &devs = de->get_devices(); map::const_iterator it, end = devs.end(); @@ -168,6 +210,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_)); 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(); } diff --git a/metadata_dumper.h b/metadata_dumper.h index ec03335..c96d22e 100644 --- a/metadata_dumper.h +++ b/metadata_dumper.h @@ -25,7 +25,10 @@ //---------------------------------------------------------------- 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); } //---------------------------------------------------------------- diff --git a/thin_check.cc b/thin_check.cc index 9993e80..3e3e533 100644 --- a/thin_check.cc +++ b/thin_check.cc @@ -30,11 +30,16 @@ using namespace thin_provisioning; namespace { int check(string const &path) { - metadata::ptr md(new metadata(path, metadata::OPEN)); + try { + metadata::ptr md(new metadata(path, metadata::OPEN)); - optional maybe_errors = metadata_check(md); - if (maybe_errors) { - cerr << error_selector(*maybe_errors, 3); + optional maybe_errors = metadata_check(md); + if (maybe_errors) { + cerr << error_selector(*maybe_errors, 3); + return 1; + } + } catch (std::exception &e) { + cerr << e.what(); return 1; } diff --git a/thin_dump.cc b/thin_dump.cc index bf907ff..25b9eb4 100644 --- a/thin_dump.cc +++ b/thin_dump.cc @@ -33,59 +33,76 @@ using namespace thin_provisioning; //---------------------------------------------------------------- namespace { - void dump(string const &path, string const &format) { - metadata::ptr md(new metadata(path, metadata::OPEN)); - emitter::ptr e; + int dump(string const &path, string const &format, bool repair) { + try { + metadata::ptr md(new metadata(path, metadata::OPEN)); + emitter::ptr e; - if (format == "xml") - e = create_xml_emitter(cout); - else if (format == "human_readable") - e = create_human_readable_emitter(cout); - else { - cerr << "unknown format '" << format << "'" << endl; - exit(1); + if (format == "xml") + e = create_xml_emitter(cout); + else if (format == "human_readable") + e = create_human_readable_emitter(cout); + else { + cerr << "unknown format '" << format << "'" << endl; + exit(1); + } + + metadata_dump(md, e, repair); + } catch (std::exception &e) { + cerr << e.what(); + return 1; } - metadata_dump(md, e); + return 0; } void usage(string const &cmd) { - cerr << "Usage: " << cmd << " [options] {device|file}" << endl << endl; - cerr << "Options:" << endl; - cerr << " {-h|--help}" << endl; - cerr << " {-f|--format} {xml|human_readable}" << endl; - cerr << " {-i|--input} {xml|human_readable} input_file" << endl; - cerr << " {-V|--version}" << endl; + cerr << "Usage: " << cmd << " [options] {device|file}" << endl << endl + << "Options:" << endl + << " {-h|--help}" << endl + << " {-f|--format} {xml|human_readable}" << endl + << " {-i|--input} {xml|human_readable} input_file" << endl + << " {-r|--repair}" << endl + << " {-V|--version}" << endl; } } int main(int argc, char **argv) { int c; + bool repair = false; const char shortopts[] = "hf:i:V"; string filename, format = "xml"; const struct option longopts[] = { { "help", no_argument, NULL, 'h'}, { "format", required_argument, NULL, 'f' }, { "input", required_argument, NULL, 'i'}, + { "repair", no_argument, NULL, 'r'}, { "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 'f': - format = optarg; - break; - case 'i': - filename = optarg; - break; - case 'V': - cerr << THIN_PROVISIONING_TOOLS_VERSION << endl; - return 0; + case 'h': + usage(basename(argv[0])); + return 0; + + case 'f': + format = optarg; + break; + + case 'i': + filename = optarg; + break; + + case 'r': + repair = true; + break; + + case 'V': + cerr << THIN_PROVISIONING_TOOLS_VERSION << endl; + return 0; } } @@ -99,8 +116,7 @@ int main(int argc, char **argv) return 1; } - dump(filename, format); - return 0; + return dump(filename, format, repair); } //---------------------------------------------------------------- diff --git a/thin_restore.cc b/thin_restore.cc index 789731f..d05115f 100644 --- a/thin_restore.cc +++ b/thin_restore.cc @@ -35,22 +35,21 @@ using namespace thin_provisioning; //---------------------------------------------------------------- namespace { - void restore(string const &backup_file, string const &dev) { - // FIXME: hard coded - block_address const NR_BLOCKS = 100000; + int restore(string const &backup_file, string const &dev) { + try { + // FIXME: hard coded + block_address const NR_BLOCKS = 100000; - metadata::ptr md(new metadata(dev, metadata::CREATE, 128, NR_BLOCKS)); - emitter::ptr restorer = create_restore_emitter(md); - ifstream in(backup_file.c_str(), ifstream::in); - // FIXME: - //try { + metadata::ptr md(new metadata(dev, metadata::CREATE, 128, NR_BLOCKS)); + emitter::ptr restorer = create_restore_emitter(md); + ifstream in(backup_file.c_str(), ifstream::in); parse_xml(in, restorer); -#if 0 - } catch (...) { - in.close(); - throw; + } catch (std::exception &e) { + cerr << e.what(); + return 1; } -#endif + + return 0; } void usage(string const &cmd) { @@ -108,8 +107,7 @@ int main(int argc, char **argv) return 1; } - restore(input, output); - return 0; + return restore(input, output); } From d0fd550a7fbf1a6d1da0a81e13d62629174cb796 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 2 Mar 2012 10:44:23 +0000 Subject: [PATCH 07/35] do not throw in a destructor, idiot --- restore_emitter.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/restore_emitter.cc b/restore_emitter.cc index 2db15bd..9c7de30 100644 --- a/restore_emitter.cc +++ b/restore_emitter.cc @@ -33,9 +33,6 @@ namespace { } virtual ~restorer() { - if (in_superblock_) { - throw runtime_error("still in superblock"); - } } virtual void begin_superblock(std::string const &uuid, From fcd56069df07e9378b09a8df7ba54bf3b50d4a67 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 2 Mar 2012 12:59:59 +0000 Subject: [PATCH 08/35] add -q option to thin_check --- thin_check.cc | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/thin_check.cc b/thin_check.cc index 3e3e533..491057f 100644 --- a/thin_check.cc +++ b/thin_check.cc @@ -29,17 +29,19 @@ using namespace std; using namespace thin_provisioning; namespace { - int check(string const &path) { + int check(string const &path, bool quiet) { try { metadata::ptr md(new metadata(path, metadata::OPEN)); optional maybe_errors = metadata_check(md); if (maybe_errors) { - cerr << error_selector(*maybe_errors, 3); + if (!quiet) + cerr << error_selector(*maybe_errors, 3); return 1; } } catch (std::exception &e) { - cerr << e.what(); + if (!quiet) + cerr << e.what(); return 1; } @@ -47,18 +49,21 @@ namespace { } void usage(string const &cmd) { - cerr << "Usage: " << cmd << " {device|file}" << endl; - cerr << "Options:" << endl; - cerr << " {-h|--help}" << endl; - cerr << " {-V|--version}" << endl; + cerr << "Usage: " << cmd << " {device|file}" << endl + << "Options:" << endl + << " {-q|--quiet}" << endl + << " {-h|--help}" << endl + << " {-V|--version}" << endl; } } int main(int argc, char **argv) { int c; + bool quiet = false; const char shortopts[] = "hV"; const struct option longopts[] = { + { "quiet", no_argument, NULL, 'q'}, { "help", no_argument, NULL, 'h'}, { "version", no_argument, NULL, 'V'}, { NULL, no_argument, NULL, 0 } @@ -69,6 +74,11 @@ int main(int argc, char **argv) case 'h': usage(basename(argv[0])); return 0; + + case 'q': + quiet = true; + break; + case 'V': cerr << THIN_PROVISIONING_TOOLS_VERSION << endl; return 0; @@ -80,5 +90,5 @@ int main(int argc, char **argv) exit(1); } - return check(argv[1]); + return check(argv[1], quiet); } From fa2a5fe6505b22ba5d3a236da847f3d787e9cfe9 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 2 Mar 2012 13:01:07 +0000 Subject: [PATCH 09/35] write the data device size to the xml output --- emitter.h | 3 ++- human_readable_format.cc | 4 +++- metadata_dumper.cc | 5 ++++- restore_emitter.cc | 4 +++- thin_restore.cc | 13 ++++++++----- xml_format.cc | 9 ++++++--- 6 files changed, 26 insertions(+), 12 deletions(-) diff --git a/emitter.h b/emitter.h index 14e3e82..8409cfb 100644 --- a/emitter.h +++ b/emitter.h @@ -47,7 +47,8 @@ namespace thin_provisioning { virtual void begin_superblock(std::string const &uuid, uint64_t time, 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 begin_device(uint32_t dev_id, diff --git a/human_readable_format.cc b/human_readable_format.cc index ad60c21..f30f50c 100644 --- a/human_readable_format.cc +++ b/human_readable_format.cc @@ -35,11 +35,13 @@ namespace { void begin_superblock(string const &uuid, uint64_t time, uint64_t trans_id, - uint32_t data_block_size) { + uint32_t data_block_size, + uint64_t nr_data_blocks) { out_ << "begin superblock: \"" << uuid << "\"" << ", " << time << ", " << trans_id << ", " << data_block_size + << ", " << nr_data_blocks << endl; } diff --git a/metadata_dumper.cc b/metadata_dumper.cc index 1448ae2..f73c44a 100644 --- a/metadata_dumper.cc +++ b/metadata_dumper.cc @@ -186,7 +186,10 @@ namespace { void 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); diff --git a/restore_emitter.cc b/restore_emitter.cc index 9c7de30..88f365c 100644 --- a/restore_emitter.cc +++ b/restore_emitter.cc @@ -38,7 +38,8 @@ namespace { virtual void begin_superblock(std::string const &uuid, uint64_t time, uint64_t trans_id, - uint32_t data_block_size) { + uint32_t data_block_size, + uint64_t nr_data_blocks) { in_superblock_ = true; superblock &sb = md_->sb_; @@ -46,6 +47,7 @@ namespace { sb.time_ = time; sb.trans_id_ = trans_id; sb.data_block_size_ = data_block_size; + md_->data_sm_->extend(nr_data_blocks); } virtual void end_superblock() { diff --git a/thin_restore.cc b/thin_restore.cc index d05115f..4ec2409 100644 --- a/thin_restore.cc +++ b/thin_restore.cc @@ -24,9 +24,14 @@ #include "version.h" #include -#include #include +#include #include +#include +#include +#include +#include +#include using namespace persistent_data; using namespace std; @@ -37,13 +42,11 @@ using namespace thin_provisioning; namespace { int restore(string const &backup_file, string const &dev) { try { - // FIXME: hard coded - block_address const NR_BLOCKS = 100000; - - metadata::ptr md(new metadata(dev, metadata::CREATE, 128, NR_BLOCKS)); + metadata::ptr md(new metadata(dev, metadata::CREATE, 128, 0)); emitter::ptr restorer = create_restore_emitter(md); ifstream in(backup_file.c_str(), ifstream::in); parse_xml(in, restorer); + } catch (std::exception &e) { cerr << e.what(); return 1; diff --git a/xml_format.cc b/xml_format.cc index 2809878..5e5004d 100644 --- a/xml_format.cc +++ b/xml_format.cc @@ -48,12 +48,14 @@ namespace { void begin_superblock(string const &uuid, uint64_t time, uint64_t trans_id, - uint32_t data_block_size) { + uint32_t data_block_size, + uint64_t nr_data_blocks) { indent(); out_ << "" + << " data_block_size=\"" << data_block_size << "\"" + << " nr_data_blocks=\"" << nr_data_blocks << "\">" << endl; inc(); } @@ -176,7 +178,8 @@ namespace { e->begin_superblock(get_attr(attr, "uuid"), get_attr(attr, "time"), get_attr(attr, "transaction"), - get_attr(attr, "data_block_size")); + get_attr(attr, "data_block_size"), + get_attr(attr, "nr_data_blocks")); } void parse_device(emitter *e, attributes const &attr) { From d65b67fd5eb4f0cffbf411bd4dc569d8d4edbe33 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 2 Mar 2012 13:10:53 +0000 Subject: [PATCH 10/35] another -q patch --- thin_check.cc | 4 ++-- thin_dump.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/thin_check.cc b/thin_check.cc index 491057f..e8eac42 100644 --- a/thin_check.cc +++ b/thin_check.cc @@ -61,7 +61,7 @@ int main(int argc, char **argv) { int c; bool quiet = false; - const char shortopts[] = "hV"; + const char shortopts[] = "qhV"; const struct option longopts[] = { { "quiet", no_argument, NULL, 'q'}, { "help", no_argument, NULL, 'h'}, @@ -85,7 +85,7 @@ int main(int argc, char **argv) } } - if (argc != 2) { + if (argc == 1) { usage(basename(argv[0])); exit(1); } diff --git a/thin_dump.cc b/thin_dump.cc index 25b9eb4..0d11915 100644 --- a/thin_dump.cc +++ b/thin_dump.cc @@ -71,7 +71,7 @@ int main(int argc, char **argv) { int c; bool repair = false; - const char shortopts[] = "hf:i:V"; + const char shortopts[] = "hf:i:rV"; string filename, format = "xml"; const struct option longopts[] = { { "help", no_argument, NULL, 'h'}, From 784390bb4fc9337b998efb33b1c543a61b80181a Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 2 Mar 2012 13:28:25 +0000 Subject: [PATCH 11/35] bump version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 19bbd7b..8fe00aa 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.1-github(0) (2011-12-06) \ No newline at end of file +0.1.0-github(0) (2012-03-02) \ No newline at end of file From 43bd901f162cb306fba5fda2853173acbf68f371 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Fri, 2 Mar 2012 14:05:33 +0000 Subject: [PATCH 12/35] big endian support --- endian_utils.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/endian_utils.h b/endian_utils.h index 5ddb1e5..50a65e2 100644 --- a/endian_utils.h +++ b/endian_utils.h @@ -19,6 +19,7 @@ #ifndef ENDIAN_H #define ENDIAN_H +#include #include #include @@ -69,32 +70,32 @@ namespace base { template <> inline uint16_t to_cpu(__le16 const &d) { - return d.v_; + return le16toh(d.v_); } template <> inline __le16 to_disk<__le16, uint16_t>(uint16_t const &v) { - return __le16(v); + return __le16(htole16(v)); } template <> inline uint32_t to_cpu(__le32 const &d) { - return d.v_; + return le32toh(d.v_); } template <> inline __le32 to_disk<__le32, uint32_t>(uint32_t const &v) { - return __le32(v); + return __le32(htole32(v)); } template <> inline uint64_t to_cpu(__le64 const &d) { - return d.v_; + return le64toh(d.v_); } template <> inline __le64 to_disk<__le64, uint64_t>(uint64_t const &v) { - return __le64(v); + return __le64(htole64(v)); } //-------------------------------- From 399ef0ba4efab524f0ed33da682c8f9df8a18cc1 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 5 Mar 2012 11:46:07 +0000 Subject: [PATCH 13/35] fix bug in command line parsing (Milan) --- thin_check.cc | 2 +- thin_repair.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/thin_check.cc b/thin_check.cc index e8eac42..9db11bb 100644 --- a/thin_check.cc +++ b/thin_check.cc @@ -90,5 +90,5 @@ int main(int argc, char **argv) exit(1); } - return check(argv[1], quiet); + return check(argv[optind], quiet); } diff --git a/thin_repair.cc b/thin_repair.cc index 22b8ea1..43c62e4 100644 --- a/thin_repair.cc +++ b/thin_repair.cc @@ -75,5 +75,5 @@ int main(int argc, char **argv) exit(1); } - return check(argv[1]); + return check(argv[optind]); } From 8f0a868554e2b6a267fae5b756a3b97d5feef9ec Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 5 Mar 2012 11:46:20 +0000 Subject: [PATCH 14/35] make mk_release executable --- mk_release | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 mk_release diff --git a/mk_release b/mk_release old mode 100644 new mode 100755 From af1af617352d248fbe32d12b0fe15924aba45a1c Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Mon, 5 Mar 2012 19:34:05 +0100 Subject: [PATCH 15/35] Fix some command line options [Milan] --- thin_check.cc | 8 ++++++-- thin_dump.cc | 29 +++++++++-------------------- thin_restore.cc | 14 ++++++-------- 3 files changed, 21 insertions(+), 30 deletions(-) diff --git a/thin_check.cc b/thin_check.cc index 9db11bb..fdc09d6 100644 --- a/thin_check.cc +++ b/thin_check.cc @@ -49,7 +49,7 @@ namespace { } void usage(string const &cmd) { - cerr << "Usage: " << cmd << " {device|file}" << endl + cerr << "Usage: " << cmd << " [options] {device|file}" << endl << "Options:" << endl << " {-q|--quiet}" << endl << " {-h|--help}" << endl @@ -82,10 +82,14 @@ int main(int argc, char **argv) case 'V': cerr << THIN_PROVISIONING_TOOLS_VERSION << endl; return 0; + default: + usage(basename(argv[0])); + return 1; } } - if (argc == 1) { + if (argc == optind) { + cerr << "No output file provided." << endl; usage(basename(argv[0])); exit(1); } diff --git a/thin_dump.cc b/thin_dump.cc index 0d11915..3189178 100644 --- a/thin_dump.cc +++ b/thin_dump.cc @@ -30,8 +30,6 @@ using namespace persistent_data; using namespace std; using namespace thin_provisioning; -//---------------------------------------------------------------- - namespace { int dump(string const &path, string const &format, bool repair) { try { @@ -49,7 +47,7 @@ namespace { metadata_dump(md, e, repair); } catch (std::exception &e) { - cerr << e.what(); + cerr << e.what() << endl; return 1; } @@ -61,7 +59,6 @@ namespace { << "Options:" << endl << " {-h|--help}" << endl << " {-f|--format} {xml|human_readable}" << endl - << " {-i|--input} {xml|human_readable} input_file" << endl << " {-r|--repair}" << endl << " {-V|--version}" << endl; } @@ -71,12 +68,11 @@ int main(int argc, char **argv) { int c; bool repair = false; - const char shortopts[] = "hf:i:rV"; - string filename, format = "xml"; + const char shortopts[] = "hf:rV"; + string format = "xml"; const struct option longopts[] = { { "help", no_argument, NULL, 'h'}, { "format", required_argument, NULL, 'f' }, - { "input", required_argument, NULL, 'i'}, { "repair", no_argument, NULL, 'r'}, { "version", no_argument, NULL, 'V'}, { NULL, no_argument, NULL, 0 } @@ -92,10 +88,6 @@ int main(int argc, char **argv) format = optarg; break; - case 'i': - filename = optarg; - break; - case 'r': repair = true; break; @@ -103,20 +95,17 @@ int main(int argc, char **argv) case 'V': cerr << THIN_PROVISIONING_TOOLS_VERSION << endl; return 0; + default: + usage(basename(argv[0])); + return 1; } } - if (argc == 1) { + if (argc == optind) { + cerr << "No output file provided." << endl; usage(basename(argv[0])); return 1; } - if (filename.empty()) { - cerr << "No output file provided." << endl; - return 1; - } - - return dump(filename, format, repair); + return dump(argv[optind], format, repair); } - -//---------------------------------------------------------------- diff --git a/thin_restore.cc b/thin_restore.cc index 4ec2409..a3ce23b 100644 --- a/thin_restore.cc +++ b/thin_restore.cc @@ -37,8 +37,6 @@ using namespace persistent_data; using namespace std; using namespace thin_provisioning; -//---------------------------------------------------------------- - namespace { int restore(string const &backup_file, string const &dev) { try { @@ -48,7 +46,7 @@ namespace { parse_xml(in, restorer); } catch (std::exception &e) { - cerr << e.what(); + cerr << e.what() << endl; return 1; } @@ -60,7 +58,7 @@ namespace { cerr << "Options:" << endl; cerr << " {-h|--help}" << endl; cerr << " {-i|--input} input_file" << endl; - cerr << " {-o [ --output} {device|file}" << endl; + cerr << " {-o|--output} {device|file}" << endl; cerr << " {-V|--version}" << endl; } } @@ -92,10 +90,13 @@ int main(int argc, char **argv) case 'V': cerr << THIN_PROVISIONING_TOOLS_VERSION << endl; return 0; + default: + usage(basename(argv[0])); + return 1; } } - if (argc == 1) { + if (argc != optind) { usage(basename(argv[0])); return 1; } @@ -111,7 +112,4 @@ int main(int argc, char **argv) } return restore(input, output); - } - -//---------------------------------------------------------------- From 3c6a58fb5a085bccb2753336935f17f480a463ad Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Mon, 5 Mar 2012 19:34:55 +0100 Subject: [PATCH 16/35] Reformat and fix man pages [Milan] --- man8/thin_check.8 | 43 ++++++++++++++++++++++++++--------------- man8/thin_dump.8 | 47 +++++++++++++++++++++++++-------------------- man8/thin_restore.8 | 46 ++++++++++++++++++++++++++++---------------- 3 files changed, 82 insertions(+), 54 deletions(-) diff --git a/man8/thin_check.8 b/man8/thin_check.8 index 303ffb5..37ec2f4 100644 --- a/man8/thin_check.8 +++ b/man8/thin_check.8 @@ -1,36 +1,47 @@ .TH THIN_REPAIR 8 "Thin Provisioning Tools" "Red Hat, Inc." \" -*- nroff -*- .SH NAME -thin_repair \- repair thin provisioning metadata on device or file +thin_check \- repair thin provisioning metadata on device or file .SH SYNOPSIS -.B thin_repair {device|file} +.B thin_check +.RB [ options ] +.I {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_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) -) on how to dump metadata to a file. +how to dump metadata to a file. A future release will be able to fix faulty metadata. -.B thin_repair (device|file} +.SH OPTIONS +.IP "\fB\-h, \-\-help\fP" +Print help and exit. -.B thin_repair - {-h|--help} - -.B thin_dump - {-V|--version} +.IP "\fB\-V, \-\-version\fP" +Output version information and exit. .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 +Analyses and repairs thin provisioning metadata on logical volume +/dev/vg/metadata: +.sp +.B thin_repair /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. +.B thin_check +returns an exit code of 0 for success or 1 for error. .SH SEE ALSO -.B thin_dump(8), thin_restore(8) +.B thin_dump(8) +.B thin_restore(8) .SH AUTHOR Joe Thornber diff --git a/man8/thin_dump.8 b/man8/thin_dump.8 index 82f2e26..697df83 100644 --- a/man8/thin_dump.8 +++ b/man8/thin_dump.8 @@ -3,44 +3,49 @@ thin_dump \- dump thin provisioning metadata from device or file to standard output .SH SYNOPSIS -.B thin_dump [options] {metadata device|file} +.B thin_dump +.RB [options] +.I {metadata device|file} .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 analysis or postprocessing in either XML or human readable format. XML formated metadata can be fed into thin_restore (see -.B thin_restore(8) -) in order to put it back onto a metadata device (to process by +.BR thin_restore(8) ) +in order to put it back onto a metadata device (to process by the device-mapper target) or file. -.B thin_dump - {-i|--input} {device|file} - [{-f|--format} {xml|human_readable}] +.IP "\fB\-f, \-\-format\fP \fI{xml|human_readable}\fP". +Print output in XML or human readable format. -.B thin_dump - {-r|--repair} +.IP "\fB\-r, \-\-repair\fP". -.B thin_dump - {-h|--help} +.IP "\fB\-h, \-\-help\fP". +Print help and exit. -.B thin_dump - {-V|--version} +.IP "\fB\-V, \-\-version\fP". +Output version information and exit. .SH EXAMPLES -"thin_dump -f human_redable -i /dev/vg/metadata" -dumps the thin provisioning metadata on logical volume /dev/vg/metadata -to standard output in human readable format. +Dumps the thin provisioning metadata on logical volume /dev/vg/metadata +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 -to standard output in XML format. +Dumps the thin provisioning metadata on logical volume /dev/vg/metadata +to standard output in XML format: +.sp +.B thin_dump /dev/vg/metadata .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 -.B thin_repair(8), thin_restore(8) +.B thin_check(8) +.B thin_restore(8) .SH AUTHOR Joe Thornber diff --git a/man8/thin_restore.8 b/man8/thin_restore.8 index c5407ca..b4a07aa 100644 --- a/man8/thin_restore.8 +++ b/man8/thin_restore.8 @@ -3,37 +3,49 @@ thin_restore \- restore thin provisioning metadata file to device or file .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 -thin_restore restores thin provisioning metadata created by the -respective device-mapper target dumped into an XML formated (see -.B thin_dump(8) -) file, which optionally can be preproccessed to another device or file. +.B thin_restore +restores thin provisioning metadata created by the +respective device-mapper target dumped into an XML formatted (see +.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 by the device-mapper target. -.B thin_restore - {-i|--input} {device|file} - {-o|--output} {device|file} +.IP "\fB\-i, \-\-input\fP \fI{device|file}\fP" +Input file or device with metadata. -.B thin_restore - -h|--help +.IP "\fB\-o, \-\-output\fP \fI{device|file}\fP" +Output file or device. -.B thin_dump - {-V|--version} +.IP "\fB\-h, \-\-help\fP" +Print help and exit. + +.IP "\fB\-V, \-\-version\fP" +Output version information and exit. .SH EXAMPLE -"thin_restore -i metadata -o /dev/vg/metadata" -restores the XML formatted thin provisioning metadata on file "metadata" +Restores the XML formatted thin provisioning metadata on file +.B metadata 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 -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 -.B thin_dump(8), thin_repair(8) +.B thin_dump(8) +.B thin_check(8) .SH AUTHOR Joe Thornber From 68597382185084a502ba3ce6122bfc41d88d2c16 Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Mon, 5 Mar 2012 19:35:42 +0100 Subject: [PATCH 17/35] Fix uint32_uint64 warning [Milan] --- restore_emitter.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/restore_emitter.cc b/restore_emitter.cc index 88f365c..d8dbaae 100644 --- a/restore_emitter.cc +++ b/restore_emitter.cc @@ -71,7 +71,7 @@ namespace { // Add entry to the details tree 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); // Insert an empty mapping tree From 33ad18c80e8fa9ba9ab4f6d24ba322b978bca3b5 Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Mon, 5 Mar 2012 19:36:09 +0100 Subject: [PATCH 18/35] Remove thin_repair.cc [Milan] --- thin_repair.cc | 79 -------------------------------------------------- 1 file changed, 79 deletions(-) delete mode 100644 thin_repair.cc diff --git a/thin_repair.cc b/thin_repair.cc deleted file mode 100644 index 43c62e4..0000000 --- a/thin_repair.cc +++ /dev/null @@ -1,79 +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 -// . - -#include -#include -#include - -#include "metadata.h" -#include "metadata_checker.h" -#include "version.h" - -using namespace persistent_data; -using namespace std; -using namespace thin_provisioning; - -namespace { - int check(string const &path) { - metadata::ptr md(new metadata(path, metadata::OPEN)); - - optional maybe_errors = metadata_check(md); - if (maybe_errors) { - cerr << error_selector(*maybe_errors, 3); - return 1; - } - - return 0; - } - - void usage(string const &cmd) { - cerr << "Usage: " << cmd << " {device|file}" << endl; - cerr << "Options:" << endl; - cerr << " {-h|--help}" << endl; - cerr << " {-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 != 2) { - usage(basename(argv[0])); - exit(1); - } - - return check(argv[optind]); -} From 422c56c9b8ce1418ac3951c36abec9112b54b5e7 Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Mon, 5 Mar 2012 19:38:37 +0100 Subject: [PATCH 19/35] Correct thin_check man page WRT repair information --- man8/thin_check.8 | 1 - 1 file changed, 1 deletion(-) diff --git a/man8/thin_check.8 b/man8/thin_check.8 index 37ec2f4..1ba99f0 100644 --- a/man8/thin_check.8 +++ b/man8/thin_check.8 @@ -17,7 +17,6 @@ or See .B thin_dump(8) how to dump metadata to a file. -A future release will be able to fix faulty metadata. .SH OPTIONS .IP "\fB\-h, \-\-help\fP" From dce7013577fdb26e2bd62eab130a7da444225efd Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 8 Mar 2012 13:11:55 +0000 Subject: [PATCH 20/35] sb.flags_ was not being packed/unpacked correctly --- metadata_disk_structures.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metadata_disk_structures.cc b/metadata_disk_structures.cc index 239a3bb..8a0fe41 100644 --- a/metadata_disk_structures.cc +++ b/metadata_disk_structures.cc @@ -46,7 +46,7 @@ void superblock_traits::unpack(superblock_disk const &disk, superblock &value) { value.csum_ = to_cpu(disk.csum_); - value.flags_ = to_cpu(disk.csum_); + value.flags_ = to_cpu(disk.flags_); value.blocknr_ = to_cpu(disk.blocknr_); ::memcpy(value.uuid_, disk.uuid_, sizeof(value.uuid_)); @@ -79,7 +79,7 @@ void superblock_traits::pack(superblock const &value, superblock_disk &disk) { 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_); ::memcpy(disk.uuid_, value.uuid_, sizeof(disk.uuid_)); From da8afff35f21c25d60b172811aa5a778b6c03b25 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 8 Mar 2012 17:20:46 +0000 Subject: [PATCH 21/35] thin_debug --- Makefile.in | 6 ++ thin_debug.cc | 279 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 285 insertions(+) create mode 100644 thin_debug.cc diff --git a/Makefile.in b/Makefile.in index d659b4e..d712b44 100644 --- a/Makefile.in +++ b/Makefile.in @@ -19,6 +19,7 @@ .PHONY: all PROGRAMS=\ + thin_debug \ thin_check \ thin_dump \ thin_restore @@ -85,6 +86,7 @@ test-programs: $(TEST_PROGRAMS) %.o: %.cc $(CXX) -c $(INCLUDES) $(CXXFLAGS) -o $@ $< +THIN_DEBUG_SOURCE=$(SOURCE) THIN_DUMP_SOURCE=$(SOURCE) THIN_RESTORE_SOURCE=$(SOURCE) THIN_CHECK_SOURCE=\ @@ -101,10 +103,14 @@ THIN_CHECK_SOURCE=\ space_map_transactional.cc \ transaction_manager.cc +THIN_DEBUG_OBJECTS=$(subst .cc,.o,$(THIN_DEBUG_SOURCE)) THIN_DUMP_OBJECTS=$(subst .cc,.o,$(THIN_DUMP_SOURCE)) THIN_RESTORE_OBJECTS=$(subst .cc,.o,$(THIN_RESTORE_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 $(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) diff --git a/thin_debug.cc b/thin_debug.cc new file mode 100644 index 0000000..94e431c --- /dev/null +++ b/thin_debug.cc @@ -0,0 +1,279 @@ +// 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 +// . + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 strings; + + class table_formatter { + public: + virtual ~table_formatter() {} + + typedef optional maybe_string; + + void field(string const &name, string const &value, maybe_string const &units = maybe_string()) { + fields_.push_back(field_type(name, value, units)); + } + + virtual void output(ostream &out) = 0; + + protected: + typedef tuple field_type; + + vector fields_; + }; + + template + void + field(table_formatter &t, string const &name, T const &value) { + t.field(name, lexical_cast(value)); + } + + template + void + field(table_formatter &t, string const &name, T const &value, string const &units) { + t.field(name, lexical_cast(value), optional(units)); + } + + //-------------------------------- + + class text_formatter : public table_formatter { + public: + virtual void output(ostream &out) { + vector::const_iterator it; + for (it = fields_.begin(); it != fields_.end(); ++it) { + out << it->get<0>() << ": " << it->get<1>(); + if (it->get<2>()) + out << " " << *(it->get<2>()) << "(s)"; + out << endl; + } + } + }; + + //-------------------------------- + + class command { + public: + typedef boost::shared_ptr 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::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 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) { + text_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_, "sector"); + field(f, "metadata block size", sb.metadata_block_size_, "sector"); + 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); + } + + private: + metadata::ptr md_; + }; + + template + 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(args[1]); + block_manager<>::read_ref rr = md_->tm_->read_lock(block); + node_ref n = btree_detail::to_node(rr); + + text_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()); + + f.output(out); + } + + private: + 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("btree_node", command::ptr(new show_btree_node(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]); +} From 0eac8026826c81d4990229e7f65320d8539c4fe9 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 13 Mar 2012 11:01:14 +0000 Subject: [PATCH 22/35] more thin_debug work --- thin_debug.cc | 122 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 94 insertions(+), 28 deletions(-) diff --git a/thin_debug.cc b/thin_debug.cc index 94e431c..45a3d73 100644 --- a/thin_debug.cc +++ b/thin_debug.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -40,48 +41,69 @@ using namespace thin_provisioning; namespace { typedef vector strings; - class table_formatter { + class formatter { public: - virtual ~table_formatter() {} + typedef shared_ptr ptr; + + virtual ~formatter() {} typedef optional maybe_string; - void field(string const &name, string const &value, maybe_string const &units = maybe_string()) { - fields_.push_back(field_type(name, value, units)); + void field(string const &name, string const &value) { + fields_.push_back(field_type(name, value)); } - virtual void output(ostream &out) = 0; + 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 tuple field_type; + typedef variant value; + typedef tuple field_type; vector fields_; }; template void - field(table_formatter &t, string const &name, T const &value) { + field(formatter &t, string const &name, T const &value) { t.field(name, lexical_cast(value)); } - template - void - field(table_formatter &t, string const &name, T const &value, string const &units) { - t.field(name, lexical_cast(value), optional(units)); - } - //-------------------------------- - class text_formatter : public table_formatter { + class xml_formatter : public formatter { public: - virtual void output(ostream &out) { + virtual void output(ostream &out, int depth) { + indent(depth, out); + out << "" << endl; vector::const_iterator it; for (it = fields_.begin(); it != fields_.end(); ++it) { - out << it->get<0>() << ": " << it->get<1>(); - if (it->get<2>()) - out << " " << *(it->get<2>()) << "(s)"; - out << endl; + if (string const *s = get(&it->get<1>())) { + indent(depth + 1, out); + out << "get<0>() + << "\" value=\"" + << *s + << "\"/>" + << endl; + + } else { + formatter::ptr f = get(it->get<1>()); + f->output(out, depth + 1); + } } + + indent(depth, out); + out << "" << endl; + } + + private: + void indent(int depth, ostream &out) const { + for (int i = 0; i < depth * 2; i++) + out << ' '; } }; @@ -159,7 +181,7 @@ namespace { } virtual void exec(strings const &args, ostream &out) { - text_formatter f; + xml_formatter f; superblock const &sb = md_->sb_; @@ -174,20 +196,45 @@ namespace { 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_, "sector"); - field(f, "metadata block size", sb.metadata_block_size_, "sector"); + 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); + 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(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 class show_btree_node : public command { public: @@ -203,9 +250,20 @@ namespace { block_address block = lexical_cast(args[1]); block_manager<>::read_ref rr = md_->tm_->read_lock(block); - node_ref n = btree_detail::to_node(rr); - text_formatter f; + node_ref n = btree_detail::to_node(rr); + if (n.get_type() == INTERNAL) + show_node(n, out); + else { + node_ref n = btree_detail::to_node(rr); + show_node(n, out); + } + } + + private: + template + void show_node(node_ref n, ostream &out) { + xml_formatter f; field(f, "csum", n.get_checksum()); field(f, "blocknr", n.get_location()); @@ -214,10 +272,16 @@ namespace { field(f, "max entries", n.get_max_entries()); field(f, "value size", n.get_value_size()); - f.output(out); + 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); } - private: metadata::ptr md_; }; @@ -229,7 +293,9 @@ namespace { 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("btree_node", command::ptr(new show_btree_node(md))); + interp.register_command("m1_node", command::ptr(new show_btree_node(md))); + interp.register_command("m2_node", command::ptr(new show_btree_node(md))); + interp.register_command("detail_node", command::ptr(new show_btree_node(md))); interp.enter_main_loop(); } catch (std::exception &e) { From ba6984504e8ccc120335e722a6f04bfaace47f2d Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Tue, 13 Mar 2012 12:15:32 +0100 Subject: [PATCH 23/35] Fix segfault with file argument --- metadata.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metadata.cc b/metadata.cc index 8de2974..d473c17 100644 --- a/metadata.cc +++ b/metadata.cc @@ -66,7 +66,7 @@ namespace { throw runtime_error("Couldn't stat dev path"); if (S_ISREG(info.st_mode)) - nr_blocks = div_down(info.st_size, MD_BLOCK_SIZE); + nr_blocks = div_up(info.st_size, MD_BLOCK_SIZE); else if (S_ISBLK(info.st_mode)) { // To get the size of a block device we need to From ef7e3b7e9412043ff0e2363be55cc5c976491705 Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Tue, 13 Mar 2012 12:26:35 +0100 Subject: [PATCH 24/35] Catch bz802242 artifical zero size file segfault --- metadata.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metadata.cc b/metadata.cc index d473c17..a7da4b9 100644 --- a/metadata.cc +++ b/metadata.cc @@ -65,7 +65,7 @@ namespace { if (r) 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_up(info.st_size, MD_BLOCK_SIZE); else if (S_ISBLK(info.st_mode)) { From 748e21fc5c9fcb748cfe89a4b3b9cfc3cab87ce5 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 13 Mar 2012 11:38:41 +0000 Subject: [PATCH 25/35] bump version number --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 8fe00aa..f797333 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0-github(0) (2012-03-02) \ No newline at end of file +0.1.2-github(0) (2012-03-02) \ No newline at end of file From 212d8417b299532d5fac3048e053cfb4bad83974 Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Tue, 13 Mar 2012 13:10:13 +0100 Subject: [PATCH 26/35] thin_check: add missing LF --- thin_check.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thin_check.cc b/thin_check.cc index fdc09d6..ff2b3da 100644 --- a/thin_check.cc +++ b/thin_check.cc @@ -41,7 +41,7 @@ namespace { } } catch (std::exception &e) { if (!quiet) - cerr << e.what(); + cerr << e.what() << endl; return 1; } From 4a1484330d90264b3812acb777c1405ec621f566 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 13 Mar 2012 12:32:30 +0000 Subject: [PATCH 27/35] --version should output to stdout, not stderr --- thin_check.cc | 3 ++- thin_dump.cc | 3 ++- thin_restore.cc | 6 +++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/thin_check.cc b/thin_check.cc index fdc09d6..c3408b4 100644 --- a/thin_check.cc +++ b/thin_check.cc @@ -80,8 +80,9 @@ int main(int argc, char **argv) break; case 'V': - cerr << THIN_PROVISIONING_TOOLS_VERSION << endl; + cout << THIN_PROVISIONING_TOOLS_VERSION << endl; return 0; + default: usage(basename(argv[0])); return 1; diff --git a/thin_dump.cc b/thin_dump.cc index 3189178..68f6a2c 100644 --- a/thin_dump.cc +++ b/thin_dump.cc @@ -93,8 +93,9 @@ int main(int argc, char **argv) break; case 'V': - cerr << THIN_PROVISIONING_TOOLS_VERSION << endl; + cout << THIN_PROVISIONING_TOOLS_VERSION << endl; return 0; + default: usage(basename(argv[0])); return 1; diff --git a/thin_restore.cc b/thin_restore.cc index a3ce23b..4086910 100644 --- a/thin_restore.cc +++ b/thin_restore.cc @@ -81,15 +81,19 @@ int main(int argc, char **argv) case 'h': usage(basename(argv[0])); return 0; + case 'i': input = optarg; break; + case 'o': output = optarg; break; + case 'V': - cerr << THIN_PROVISIONING_TOOLS_VERSION << endl; + cout << THIN_PROVISIONING_TOOLS_VERSION << endl; return 0; + default: usage(basename(argv[0])); return 1; From 0e47d992182ba1899a6431f0e0548ad60e626a91 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 13 Mar 2012 14:06:10 +0000 Subject: [PATCH 28/35] tweaks to command line --- thin_check.cc | 20 ++++++++++---------- thin_dump.cc | 22 +++++++++++----------- thin_restore.cc | 23 +++++++++++++---------- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/thin_check.cc b/thin_check.cc index c3408b4..104c8b8 100644 --- a/thin_check.cc +++ b/thin_check.cc @@ -48,12 +48,12 @@ namespace { return 0; } - void usage(string const &cmd) { - cerr << "Usage: " << cmd << " [options] {device|file}" << endl - << "Options:" << endl - << " {-q|--quiet}" << endl - << " {-h|--help}" << endl - << " {-V|--version}" << endl; + 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; } } @@ -72,7 +72,7 @@ int main(int argc, char **argv) while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { switch(c) { case 'h': - usage(basename(argv[0])); + usage(cout, basename(argv[0])); return 0; case 'q': @@ -84,14 +84,14 @@ int main(int argc, char **argv) return 0; default: - usage(basename(argv[0])); + usage(cerr, basename(argv[0])); return 1; } } if (argc == optind) { - cerr << "No output file provided." << endl; - usage(basename(argv[0])); + cerr << "No input file provided." << endl; + usage(cerr, basename(argv[0])); exit(1); } diff --git a/thin_dump.cc b/thin_dump.cc index 68f6a2c..5980e2c 100644 --- a/thin_dump.cc +++ b/thin_dump.cc @@ -54,13 +54,13 @@ namespace { return 0; } - void usage(string const &cmd) { - cerr << "Usage: " << cmd << " [options] {device|file}" << endl << endl - << "Options:" << endl - << " {-h|--help}" << endl - << " {-f|--format} {xml|human_readable}" << endl - << " {-r|--repair}" << endl - << " {-V|--version}" << endl; + void usage(ostream &out, string const &cmd) { + out << "Usage: " << cmd << " [options] {device|file}" << endl + << "Options:" << endl + << " {-h|--help}" << endl + << " {-f|--format} {xml|human_readable}" << endl + << " {-r|--repair}" << endl + << " {-V|--version}" << endl; } } @@ -81,7 +81,7 @@ int main(int argc, char **argv) while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { switch(c) { case 'h': - usage(basename(argv[0])); + usage(cout, basename(argv[0])); return 0; case 'f': @@ -97,14 +97,14 @@ int main(int argc, char **argv) return 0; default: - usage(basename(argv[0])); + usage(cerr, basename(argv[0])); return 1; } } if (argc == optind) { - cerr << "No output file provided." << endl; - usage(basename(argv[0])); + cerr << "No input file provided." << endl; + usage(cerr, basename(argv[0])); return 1; } diff --git a/thin_restore.cc b/thin_restore.cc index 4086910..a3ff53f 100644 --- a/thin_restore.cc +++ b/thin_restore.cc @@ -40,6 +40,7 @@ using namespace thin_provisioning; namespace { int restore(string const &backup_file, string const &dev) { try { + // The block size gets updated by the restorer. metadata::ptr md(new metadata(dev, metadata::CREATE, 128, 0)); emitter::ptr restorer = create_restore_emitter(md); ifstream in(backup_file.c_str(), ifstream::in); @@ -53,13 +54,13 @@ namespace { return 0; } - void usage(string const &cmd) { - cerr << "Usage: " << cmd << " [options]" << endl << endl; - cerr << "Options:" << endl; - cerr << " {-h|--help}" << endl; - cerr << " {-i|--input} input_file" << endl; - cerr << " {-o|--output} {device|file}" << endl; - cerr << " {-V|--version}" << endl; + void usage(ostream &out, string const &cmd) { + out << "Usage: " << cmd << " [options]" << endl + << "Options:" << endl + << " {-h|--help}" << endl + << " {-i|--input} input_file" << endl + << " {-o|--output} {device|file}" << endl + << " {-V|--version}" << endl; } } @@ -79,7 +80,7 @@ int main(int argc, char **argv) while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { switch(c) { case 'h': - usage(basename(argv[0])); + usage(cout, basename(argv[0])); return 0; case 'i': @@ -95,23 +96,25 @@ int main(int argc, char **argv) return 0; default: - usage(basename(argv[0])); + usage(cerr, basename(argv[0])); return 1; } } if (argc != optind) { - usage(basename(argv[0])); + usage(cerr, basename(argv[0])); return 1; } if (input.empty()) { cerr << "No input file provided." << endl; + usage(cerr, basename(argv[0])); return 1; } if (output.empty()) { cerr << "No output file provided." << endl; + usage(cerr, basename(argv[0])); return 1; } From 7b1907cf3b6bc7fe8d7e666bd0537d92ed2325f7 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 13 Mar 2012 14:08:25 +0000 Subject: [PATCH 29/35] bump version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index f797333..8294c18 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.2-github(0) (2012-03-02) \ No newline at end of file +0.1.2 \ No newline at end of file From 2a50b54906974e262179eb361122fe7d339fdd37 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 13 Mar 2012 14:29:02 +0000 Subject: [PATCH 30/35] bump version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 8294c18..bdbf9d6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.2 \ No newline at end of file +0.1.2+ \ No newline at end of file From ef39e0077504de26d570145c3f3e75b186db349b Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Wed, 14 Mar 2012 13:31:39 +0100 Subject: [PATCH 31/35] - Document -q option in man page. - Fix thin_repair typo in man page. - Remove some whitespaces. --- man8/thin_check.8 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/man8/thin_check.8 b/man8/thin_check.8 index 1ba99f0..6daff5a 100644 --- a/man8/thin_check.8 +++ b/man8/thin_check.8 @@ -19,17 +19,20 @@ See how to dump metadata to a file. .SH OPTIONS -.IP "\fB\-h, \-\-help\fP" +.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" +.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_repair /dev/vg/metadata +.B thin_check /dev/vg/metadata The device may not be actively used by the target when repairing! From 57e899fd175537f3a878b39022089984f2d8ee95 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Wed, 14 Mar 2012 13:31:39 +0100 Subject: [PATCH 32/35] - Document -q option in man page. - Fix thin_repair typo in man page. - Remove some whitespaces. --- man8/thin_check.8 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/man8/thin_check.8 b/man8/thin_check.8 index 1ba99f0..6daff5a 100644 --- a/man8/thin_check.8 +++ b/man8/thin_check.8 @@ -19,17 +19,20 @@ See how to dump metadata to a file. .SH OPTIONS -.IP "\fB\-h, \-\-help\fP" +.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" +.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_repair /dev/vg/metadata +.B thin_check /dev/vg/metadata The device may not be actively used by the target when repairing! From 6bb2b99691f292ac57997746e004209fdea3ea4f Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 14 Mar 2012 12:44:44 +0000 Subject: [PATCH 33/35] bump version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 8294c18..7693c96 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.2 \ No newline at end of file +0.1.3 \ No newline at end of file From 16205a7fdf45bfc579fcb9051651e47360e45e27 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Thu, 15 Mar 2012 13:05:37 +0100 Subject: [PATCH 34/35] fix thinp Makefile With utilities in /usr/sbin it doesn't work - it must not redefine DESTDIR... (thanks kabi for fix) Milan --- Makefile.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index d659b4e..99037a6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -61,8 +61,8 @@ LIBS:=-lstdc++ LIBEXPAT:=-lexpat INSTALL:=@INSTALL@ STRIP= -DESTDIR:=@prefix@ -BINDIR:=$(DESTDIR)/sbin +PREFIX:=@prefix@ +BINDIR:=$(DESTDIR)$(PREFIX)/sbin MANPATH:=$(DESTDIR)$(MANDIR) vpath %.cc $(TOP_DIR) From c632c52cf516c8b4b48136111bba96adc6f615a6 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 15 Mar 2012 15:09:00 +0000 Subject: [PATCH 35/35] bump version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 7693c96..446ba66 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.3 \ No newline at end of file +0.1.4 \ No newline at end of file