diff --git a/Makefile b/Makefile index f757194..04d0902 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ SOURCE=\ hex_dump.cc \ human_readable_format.cc \ metadata.cc \ + metadata_checker.cc \ metadata_ll.cc \ metadata_dump.cc \ metadata_disk_structures.cc \ diff --git a/metadata.cc b/metadata.cc index 3b76f1e..49a98e3 100644 --- a/metadata.cc +++ b/metadata.cc @@ -15,92 +15,6 @@ using namespace thin_provisioning; //---------------------------------------------------------------- -namespace { - // As well as the standard btree checks, we build up a set of what - // 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> { - public: - typedef boost::shared_ptr ptr; - - mapping_validator(block_counter &metadata_counter, block_counter &data_counter) - : btree_checker<2, block_traits>(metadata_counter), - data_counter_(data_counter) { - } - - // 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 = btree_checker<2, block_traits>::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++) - devices_.insert(n.key_at(i)); - - return r; - } - - bool visit_leaf(unsigned level, - 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); - - if (r) - for (unsigned i = 0; i < n.get_nr_entries(); i++) - data_counter_.inc(n.value_at(i).block_); - - return r; - } - - set const &get_devices() const { - return devices_; - } - - private: - block_counter &data_counter_; - set devices_; - }; - - class details_validator : public btree_checker<1, device_details_traits> { - public: - typedef boost::shared_ptr ptr; - - details_validator(block_counter &counter) - : btree_checker<1, device_details_traits>(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); - - if (r) - for (unsigned i = 0; i < n.get_nr_entries(); i++) - devices_.insert(n.key_at(i)); - - return r; - } - - set const &get_devices() const { - return devices_; - } - - private: - set devices_; - }; -} - -//---------------------------------------------------------------- - thin::thin(thin_dev_t dev, metadata *metadata) : dev_(dev), metadata_(metadata) @@ -298,76 +212,4 @@ metadata::device_exists(thin_dev_t dev) const return md_->details_.lookup(key); } -namespace { - struct check_count : public space_map::iterator { - check_count(string const &desc, block_counter const &expected) - : bad_(false), - expected_(expected), - errors_(new error_set(desc)) { - } - - virtual void operator() (block_address b, ref_t actual) { - ref_t expected = expected_.get_count(b); - - if (actual != expected) { - ostringstream out; - out << b << ": was " << actual - << ", expected " << expected; - errors_->add_child(out.str()); - bad_ = true; - } - } - - bool bad_; - block_counter const &expected_; - error_set::ptr errors_; - }; - - optional - check_ref_counts(string const &desc, block_counter const &counts, - space_map::ptr sm) { - - check_count checker(desc, counts); - sm->iterate(checker); - return checker.bad_ ? optional(checker.errors_) : optional(); - } -} - -boost::optional -metadata::check() -{ - error_set::ptr errors(new error_set("Errors in metadata")); - - block_counter metadata_counter, data_counter; - - mapping_validator::ptr mv(new mapping_validator(metadata_counter, - data_counter)); - md_->mappings_.visit(mv); - set const &mapped_devs = mv->get_devices(); - - details_validator::ptr dv(new details_validator(metadata_counter)); - md_->details_.visit(dv); - set const &details_devs = dv->get_devices(); - - for (set::const_iterator it = mapped_devs.begin(); it != mapped_devs.end(); ++it) - if (details_devs.count(*it) == 0) { - ostringstream out; - out << "mapping exists for device " << *it - << ", yet there is no entry in the details tree."; - throw runtime_error(out.str()); - } - - metadata_counter.inc(SUPERBLOCK_LOCATION); - md_->metadata_sm_->check(metadata_counter); - md_->data_sm_->check(metadata_counter); - errors->add_child(check_ref_counts("Errors in metadata block reference counts", - metadata_counter, md_->metadata_sm_)); - errors->add_child(check_ref_counts("Errors in data block reference counts", - data_counter, md_->data_sm_)); - - return (errors->get_children().size() > 0) ? - optional(errors) : - optional(); -} - //---------------------------------------------------------------- diff --git a/metadata.h b/metadata.h index 71c035f..8c64482 100644 --- a/metadata.h +++ b/metadata.h @@ -2,11 +2,9 @@ #define MULTISNAP_METADATA_H #include "emitter.h" -#include "error_set.h" #include "metadata_ll.h" #include - #include //---------------------------------------------------------------- @@ -62,10 +60,6 @@ namespace thin_provisioning { thin::ptr open_thin(thin_dev_t); - // FIXME: split out into a separate interface - // Validation - boost::optional check(); - // FIXME: split out into a separate interface // Dumping metadata void dump(emitter::ptr e); diff --git a/metadata_checker.cc b/metadata_checker.cc new file mode 100644 index 0000000..9d86966 --- /dev/null +++ b/metadata_checker.cc @@ -0,0 +1,164 @@ +#include "metadata_checker.h" + +using namespace thin_provisioning; + +//---------------------------------------------------------------- + +namespace { + // As well as the standard btree checks, we build up a set of what + // 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> { + public: + typedef boost::shared_ptr ptr; + + mapping_validator(block_counter &metadata_counter, block_counter &data_counter) + : btree_checker<2, block_traits>(metadata_counter), + data_counter_(data_counter) { + } + + // 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 = btree_checker<2, block_traits>::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++) + devices_.insert(n.key_at(i)); + + return r; + } + + bool visit_leaf(unsigned level, + 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); + + if (r) + for (unsigned i = 0; i < n.get_nr_entries(); i++) + data_counter_.inc(n.value_at(i).block_); + + return r; + } + + set const &get_devices() const { + return devices_; + } + + private: + block_counter &data_counter_; + set devices_; + }; + + class details_validator : public btree_checker<1, device_details_traits> { + public: + typedef boost::shared_ptr ptr; + + details_validator(block_counter &counter) + : btree_checker<1, device_details_traits>(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); + + if (r) + for (unsigned i = 0; i < n.get_nr_entries(); i++) + devices_.insert(n.key_at(i)); + + return r; + } + + set const &get_devices() const { + return devices_; + } + + private: + set devices_; + }; + + struct check_count : public space_map::iterator { + check_count(string const &desc, block_counter const &expected) + : bad_(false), + expected_(expected), + errors_(new error_set(desc)) { + } + + virtual void operator() (block_address b, ref_t actual) { + ref_t expected = expected_.get_count(b); + + if (actual != expected) { + ostringstream out; + out << b << ": was " << actual + << ", expected " << expected; + errors_->add_child(out.str()); + bad_ = true; + } + } + + bool bad_; + block_counter const &expected_; + error_set::ptr errors_; + }; + + optional + check_ref_counts(string const &desc, block_counter const &counts, + space_map::ptr sm) { + + check_count checker(desc, counts); + sm->iterate(checker); + return checker.bad_ ? optional(checker.errors_) : optional(); + } +} + + +//---------------------------------------------------------------- + +boost::optional +thin_provisioning::metadata_check(metadata_ll::ptr md) +{ + error_set::ptr errors(new error_set("Errors in metadata")); + + block_counter metadata_counter, data_counter; + + mapping_validator::ptr mv(new mapping_validator(metadata_counter, + data_counter)); + md->mappings_.visit(mv); + set const &mapped_devs = mv->get_devices(); + + details_validator::ptr dv(new details_validator(metadata_counter)); + md->details_.visit(dv); + set const &details_devs = dv->get_devices(); + + for (set::const_iterator it = mapped_devs.begin(); it != mapped_devs.end(); ++it) + if (details_devs.count(*it) == 0) { + ostringstream out; + out << "mapping exists for device " << *it + << ", yet there is no entry in the details tree."; + throw runtime_error(out.str()); + } + + metadata_counter.inc(SUPERBLOCK_LOCATION); + md->metadata_sm_->check(metadata_counter); + md->data_sm_->check(metadata_counter); + errors->add_child(check_ref_counts("Errors in metadata block reference counts", + metadata_counter, md->metadata_sm_)); + errors->add_child(check_ref_counts("Errors in data block reference counts", + data_counter, md->data_sm_)); + + return (errors->get_children().size() > 0) ? + optional(errors) : + optional(); +} + +//---------------------------------------------------------------- diff --git a/metadata_checker.h b/metadata_checker.h new file mode 100644 index 0000000..69c3063 --- /dev/null +++ b/metadata_checker.h @@ -0,0 +1,15 @@ +#ifndef METADATA_CHECKER_H +#define METADATA_CHECKER_H + +#include "error_set.h" +#include "metadata_ll.h" + +//---------------------------------------------------------------- + +namespace thin_provisioning { + boost::optional metadata_check(metadata_ll::ptr md); +} + +//---------------------------------------------------------------- + +#endif diff --git a/thin_repair.cc b/thin_repair.cc index 8bdd50b..b559641 100644 --- a/thin_repair.cc +++ b/thin_repair.cc @@ -1,6 +1,7 @@ #include #include "metadata.h" +#include "metadata_checker.h" using namespace boost; using namespace persistent_data; @@ -9,10 +10,9 @@ using namespace thin_provisioning; namespace { int check(string const &path) { - metadata_ll::ptr ll(new metadata_ll(path)); - metadata md(ll); + metadata_ll::ptr md(new metadata_ll(path)); - optional maybe_errors = md.check(); + optional maybe_errors = metadata_check(md); if (maybe_errors) { cerr << error_selector(*maybe_errors, 3); return 1;