diff --git a/thin-provisioning/metadata_checker.cc b/thin-provisioning/metadata_checker.cc index 7f3124a..2765d55 100644 --- a/thin-provisioning/metadata_checker.cc +++ b/thin-provisioning/metadata_checker.cc @@ -16,423 +16,348 @@ // with thin-provisioning-tools. If not, see // . +#include "base/nested_output.h" #include "persistent-data/file_utils.h" #include "thin-provisioning/metadata.h" #include "thin-provisioning/metadata_checker.h" +#include "thin-provisioning/metadata_counter.h" +#include "thin-provisioning/superblock.h" using namespace persistent_data; using namespace thin_provisioning; //---------------------------------------------------------------- -#if 0 -void -metadata_damage::set_message(std::string const &message) -{ - message_ = message; -} - -std::string const & -metadata_damage::get_message() const -{ - return message_; -} - -//-------------------------------- - -void -super_block_corruption::visit(metadata_damage_visitor &visitor) const -{ - visitor.visit(*this); -} - -bool -super_block_corruption::operator ==(super_block_corruption const &rhs) const -{ - return true; -} - -//-------------------------------- - -missing_device_details::missing_device_details(range64 missing) - : missing_(missing) -{ -} - -void -missing_device_details::visit(metadata_damage_visitor &visitor) const -{ - visitor.visit(*this); -} - -bool -missing_device_details::operator ==(missing_device_details const &rhs) const -{ - return missing_ == rhs.missing_; -} - -//-------------------------------- - -missing_devices::missing_devices(range64 missing) - : missing_(missing) -{ -} - -void -missing_devices::visit(metadata_damage_visitor &visitor) const -{ - visitor.visit(*this); -} - -bool -missing_devices::operator ==(missing_devices const &rhs) const -{ - return missing_ == rhs.missing_; -} - -//-------------------------------- - -missing_mappings::missing_mappings(uint64_t dev, range64 missing) - : dev_(dev), - missing_(missing) -{ -} - -void -missing_mappings::visit(metadata_damage_visitor &visitor) const -{ - visitor.visit(*this); -} - -bool -missing_mappings::operator ==(missing_mappings const &rhs) const -{ - return dev_ == rhs.dev_ && missing_ == rhs.missing_; -} - -//-------------------------------- - -bad_metadata_ref_count::bad_metadata_ref_count(block_address b, - ref_t actual, - ref_t expected) - : b_(b), - actual_(actual), - expected_(expected) -{ -} - -void -bad_metadata_ref_count::visit(metadata_damage_visitor &visitor) const -{ - visitor.visit(*this); -} - -bool -bad_metadata_ref_count::operator ==(bad_metadata_ref_count const &rhs) const -{ - return b_ == rhs.b_ && actual_ == rhs.actual_ && expected_ == rhs.expected_; -} - -//-------------------------------- - -bad_data_ref_count::bad_data_ref_count(block_address b, - ref_t actual, - ref_t expected) - : b_(b), - actual_(actual), - expected_(expected) -{ -} - -void -bad_data_ref_count::visit(metadata_damage_visitor &visitor) const -{ - visitor.visit(*this); -} - -bool -bad_data_ref_count::operator ==(bad_data_ref_count const &rhs) const -{ - return b_ == rhs.b_ && actual_ == rhs.actual_ && expected_ == rhs.expected_; -} - -//-------------------------------- - -missing_metadata_ref_counts::missing_metadata_ref_counts(range64 missing) - : missing_(missing) -{ -} - -void -missing_metadata_ref_counts::visit(metadata_damage_visitor &visitor) const -{ - visitor.visit(*this); -} - -bool -missing_metadata_ref_counts::operator ==(missing_metadata_ref_counts const &rhs) const -{ - return missing_ == rhs.missing_; -} - -//-------------------------------- - -missing_data_ref_counts::missing_data_ref_counts(range64 missing) - : missing_(missing) -{ -} - -void -missing_data_ref_counts::visit(metadata_damage_visitor &visitor) const -{ - visitor.visit(*this); -} - -bool -missing_data_ref_counts::operator ==(missing_data_ref_counts const &rhs) const -{ - return missing_ == rhs.missing_; -} - -//-------------------------------- - -void -metadata_damage_visitor::visit(metadata_damage const &damage) -{ - damage.visit(*this); -} - -//-------------------------------- - -checker::checker(block_manager::ptr bm) - : bm_(bm) -{ -} - -//---------------------------------------------------------------- - -#if 0 - 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<2, block_traits>::visitor { + class superblock_reporter : public superblock_detail::damage_visitor { public: - typedef boost::shared_ptr ptr; - typedef btree_checker<2, block_traits> checker; - - mapping_validator(block_counter &metadata_counter, block_counter &data_counter) - : checker_(metadata_counter), - data_counter_(data_counter) - { + superblock_reporter(nested_output &out) + : out_(out), + err_(NO_ERROR) { } - 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); + virtual void visit(superblock_detail::superblock_corruption const &d) { + out_ << "superblock is corrupt" << end_message(); + { + nested_output::nest _ = out_.push(); + out_ << d.desc_ << end_message(); + } + err_ << FATAL; } - bool visit_internal_leaf(unsigned level, - bool sub_root, - optional key, - btree_detail::node_ref const &n) { - - 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)); - - return r; - } - - bool visit_leaf(unsigned level, - bool sub_root, - optional key, - btree_detail::node_ref const &n) { - bool r = checker_.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_; + base::error_state get_error() const { + return err_; } private: - checker checker_; - block_counter &data_counter_; - set devices_; + nested_output &out_; + error_state err_; }; - 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)) { + //-------------------------------- + + class devices_reporter : public device_tree_detail::damage_visitor { + public: + devices_reporter(nested_output &out) + : out_(out), + err_(NO_ERROR) { } - 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; + virtual void visit(device_tree_detail::missing_devices const &d) { + out_ << "missing devices: " << d.keys_ << end_message(); + { + nested_output::nest _ = out_.push(); + out_ << d.desc_ << end_message(); } + + err_ << FATAL; } - bool bad_; - block_counter const &expected_; - error_set::ptr errors_; + error_state get_error() const { + return err_; + } + + private: + nested_output &out_; + error_state err_; }; - 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(); + class mapping_reporter : public mapping_tree_detail::damage_visitor { + public: + mapping_reporter(nested_output &out) + : out_(out), + err_(NO_ERROR) { + } + + virtual void visit(mapping_tree_detail::missing_devices const &d) { + out_ << "missing all mappings for devices: " << d.keys_ << end_message(); + { + nested_output::nest _ = out_.push(); + out_ << d.desc_ << end_message(); + } + err_ << FATAL; + } + + virtual void visit(mapping_tree_detail::missing_mappings const &d) { + out_ << "thin device " << d.thin_dev_ << " is missing mappings " << d.keys_ << end_message(); + { + nested_output::nest _ = out_.push(); + out_ << d.desc_ << end_message(); + } + err_ << FATAL; + } + + error_state get_error() const { + return err_; + } + + private: + nested_output &out_; + error_state err_; + }; + + //-------------------------------- + + error_state examine_superblock(block_manager<>::ptr bm, + nested_output &out) { + out << "examining superblock" << end_message(); + nested_output::nest _ = out.push(); + + superblock_reporter sb_rep(out); + check_superblock(bm, sb_rep); + + return sb_rep.get_error(); } - class metadata_checker { - public: - metadata_checker(string const &dev_path) - : bm_(open_bm(dev_path)), - errors_(new error_set("Errors in metadata")) { + error_state examine_devices_tree_(transaction_manager::ptr tm, + superblock_detail::superblock const &sb, + nested_output &out) { + out << "examining devices tree" << end_message(); + nested_output::nest _ = out.push(); + + devices_reporter dev_rep(out); + device_tree dtree(*tm, sb.device_details_root_, + device_tree_detail::device_details_traits::ref_counter()); + check_device_tree(dtree, dev_rep); + + return dev_rep.get_error(); + } + + error_state examine_top_level_mapping_tree_(transaction_manager::ptr tm, + superblock_detail::superblock const &sb, + nested_output &out) { + out << "examining top level of mapping tree" << end_message(); + nested_output::nest _ = out.push(); + + mapping_reporter mapping_rep(out); + dev_tree dtree(*tm, sb.data_mapping_root_, + mapping_tree_detail::mtree_traits::ref_counter(*tm)); + check_mapping_tree(dtree, mapping_rep); + + return mapping_rep.get_error(); + } + + error_state examine_mapping_tree_(transaction_manager::ptr tm, + superblock_detail::superblock const &sb, + nested_output &out) { + out << "examining mapping tree" << end_message(); + nested_output::nest _ = out.push(); + + mapping_reporter mapping_rep(out); + mapping_tree mtree(*tm, sb.data_mapping_root_, + mapping_tree_detail::block_traits::ref_counter(tm->get_sm())); + check_mapping_tree(mtree, mapping_rep); + + return mapping_rep.get_error(); + } + + error_state examine_top_level_mapping_tree(transaction_manager::ptr tm, + superblock_detail::superblock const &sb, + nested_output &out) { + error_state err = examine_devices_tree_(tm, sb, out); + err << examine_top_level_mapping_tree_(tm, sb, out); + + return err; + } + + error_state examine_mapping_tree(transaction_manager::ptr tm, + superblock_detail::superblock const &sb, + nested_output &out) { + error_state err = examine_devices_tree_(tm, sb, out); + err << examine_mapping_tree_(tm, sb, out); + + return err; + } + + error_state check_space_map_counts(transaction_manager::ptr tm, + superblock_detail::superblock const &sb, + nested_output &out) { + out << "checking space map counts" << end_message(); + nested_output::nest _ = out.push(); + + block_counter bc; + count_metadata(tm, sb, bc); + + // Finally we need to check the metadata space map agrees + // with the counts we've just calculated. + error_state err = NO_ERROR; + persistent_space_map::ptr metadata_sm = + open_metadata_sm(*tm, static_cast(&sb.metadata_space_map_root_)); + for (unsigned b = 0; b < metadata_sm->get_nr_blocks(); b++) { + ref_t c_actual = metadata_sm->get_count(b); + ref_t c_expected = bc.get_count(b); + + if (c_actual != c_expected) { + out << "metadata reference counts differ for block " << b + << ", expected " << c_expected + << ", but got " << c_actual + << end_message(); + + err << (c_actual > c_expected ? NON_FATAL : FATAL); + } } - boost::optional check() { -#if 1 - superblock sb = read_superblock(); + return err; + } - // FIXME: check version? + void print_info(transaction_manager::ptr tm, + superblock_detail::superblock const &sb, + nested_output &out) + { + out << "TRANSACTION_ID=" << sb.trans_id_ << "\n" + << "METADATA_FREE_BLOCKS=" << tm->get_sm()->get_nr_free() + << end_message(); + } - check_mappings(); + block_address mapping_root(superblock_detail::superblock const &sb, check_options const &opts) + { + return opts.override_mapping_root_ ? *opts.override_mapping_root_ : sb.data_mapping_root_; + } - return (errors_->get_children().size() > 0) ? - optional(errors_) : - optional(); -#else - error_set::ptr errors(new error_set("Errors in metadata")); + //-------------------------------- + class base_metadata_checker : public metadata_checker { + public: + base_metadata_checker(block_manager<>::ptr bm, + check_options check_opts, + output_options output_opts) + : bm_(bm), + options_(check_opts), + out_(cerr, 2), + info_out_(cout, 0) { - if (md->sb_.metadata_snap_) { - block_manager<>::ptr bm = md->tm_->get_bm(); + if (output_opts == OUTPUT_QUIET) { + out_.disable(); + info_out_.disable(); + } + } + error_state check() { + error_state err = NO_ERROR; - block_address root = md->sb_.metadata_snap_; + err << examine_superblock(bm_, out_); - metadata_counter.inc(root); - - superblock sb; - block_manager<>::read_ref r = bm->read_lock(root); - superblock_disk const *sbd = reinterpret_cast(&r.data()); - superblock_traits::unpack(*sbd, sb); - - metadata_counter.inc(sb.data_mapping_root_); - metadata_counter.inc(sb.device_details_root_); + if (err == FATAL) { + if (check_for_xml(bm_)) + out_ << "This looks like XML. thin_check only checks the binary metadata format." << end_message(); + return err; } + superblock_detail::superblock sb = read_superblock(bm_); + transaction_manager::ptr tm = + open_tm(bm_, superblock_detail::SUPERBLOCK_LOCATION); + sb.data_mapping_root_ = mapping_root(sb, options_); - set const &mapped_devs = mv->get_devices(); - details_validator::ptr dv(new details_validator(metadata_counter)); - md->details_->visit(dv); + print_info(tm, sb, info_out_); - set const &details_devs = dv->get_devices(); + err << examine_data_mappings(tm, sb, options_.check_data_mappings_, out_); - 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()); - } + // if we're checking everything, and there were no errors, + // then we should check the space maps too. + if (err != FATAL) + err << examine_metadata_space_map(tm, sb, options_.check_metadata_space_map_, out_); - 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(); -#endif + return err; } private: - static block_manager<>::ptr - open_bm(string const &dev_path) { - block_address nr_blocks = thin_provisioning::get_nr_blocks(dev_path); - return block_manager<>::ptr(new block_manager<>(dev_path, nr_blocks, 1, block_manager<>::READ_ONLY)); + static error_state + examine_data_mappings(transaction_manager::ptr tm, + superblock_detail::superblock const &sb, + check_options::data_mapping_options option, + nested_output &out) { + error_state err = NO_ERROR; + + switch (option) { + case check_options::DATA_MAPPING_LEVEL1: + err << examine_top_level_mapping_tree(tm, sb, out); + break; + case check_options::DATA_MAPPING_LEVEL2: + err << examine_mapping_tree(tm, sb, out); + break; + default: + break; // do nothing + } + + return err; } - // FIXME: common code with metadata.cc - superblock read_superblock() { - superblock sb; - block_manager<>::read_ref r = bm_->read_lock(SUPERBLOCK_LOCATION, superblock_validator()); - superblock_disk const *sbd = reinterpret_cast(&r.data()); - superblock_traits::unpack(*sbd, sb); - return sb; + static error_state + examine_metadata_space_map(transaction_manager::ptr tm, + superblock_detail::superblock const &sb, + check_options::metadata_space_map_options option, + nested_output &out) { + error_state err = NO_ERROR; + + switch (option) { + case check_options::METADATA_SPACE_MAP_FULL: + err << check_space_map_counts(tm, sb, out); + break; + default: + break; // do nothing + } + + return err; } - void check_mappings() { - mapping_validator::ptr mv( - new mapping_validator(metadata_counter_, - data_counter_)); - - - - md->mappings_->visit(mv); - } - - typedef block_manager<>::read_ref read_ref; - typedef block_manager<>::write_ref write_ref; - typedef boost::shared_ptr ptr; - block_manager<>::ptr bm_; - error_set::ptr errors_; - - block_counter metadata_counter_, data_counter_; - - -#if 0 - tm_ptr tm_; - superblock sb_; - - checked_space_map::ptr metadata_sm_; - checked_space_map::ptr data_sm_; - detail_tree::ptr details_; - dev_tree::ptr mappings_top_level_; - mapping_tree::ptr mappings_; -#endif + check_options options_; + nested_output out_; + nested_output info_out_; }; } - //---------------------------------------------------------------- -boost::optional -thin_provisioning::metadata_check(std::string const &dev_path) +check_options::check_options() + : check_data_mappings_(DATA_MAPPING_LEVEL2), + check_metadata_space_map_(METADATA_SPACE_MAP_FULL) { +} + +void check_options::set_superblock_only() { + check_data_mappings_ = DATA_MAPPING_NONE; + check_metadata_space_map_ = METADATA_SPACE_MAP_NONE; +} + +void check_options::set_skip_mappings() { + check_data_mappings_ = DATA_MAPPING_LEVEL1; + check_metadata_space_map_ = METADATA_SPACE_MAP_NONE; +} + +void check_options::set_override_mapping_root(block_address b) { + override_mapping_root_ = b; +} + +metadata_checker::ptr +thin_provisioning::create_base_checker(block_manager<>::ptr bm, + check_options const &check_opts, + output_options output_opts) { - metadata_checker checker(dev_path); - return checker.check(); + metadata_checker::ptr checker; + checker = metadata_checker::ptr(new base_metadata_checker(bm, check_opts, output_opts)); + return checker; } //---------------------------------------------------------------- -#endif -#endif diff --git a/thin-provisioning/metadata_checker.h b/thin-provisioning/metadata_checker.h index b2121d6..4b84c4b 100644 --- a/thin-provisioning/metadata_checker.h +++ b/thin-provisioning/metadata_checker.h @@ -19,142 +19,54 @@ #ifndef METADATA_CHECKER_H #define METADATA_CHECKER_H +#include "base/error_state.h" +#include "block-cache/block_cache.h" #include "persistent-data/block.h" -#include "persistent-data/error_set.h" -#include "persistent-data/run.h" -#include "persistent-data/space_map.h" - -#include //---------------------------------------------------------------- namespace thin_provisioning { - // FIXME: should take a block manager or transaction manager - void check_metadata(std::string const &path); + struct check_options { + enum data_mapping_options { + DATA_MAPPING_NONE, + DATA_MAPPING_LEVEL1, + DATA_MAPPING_LEVEL2, + }; + enum metadata_space_map_options { + METADATA_SPACE_MAP_NONE, + METADATA_SPACE_MAP_FULL, + }; + check_options(); + void set_superblock_only(); + void set_skip_mappings(); + void set_override_mapping_root(bcache::block_address b); + data_mapping_options check_data_mappings_; + metadata_space_map_options check_metadata_space_map_; + boost::optional override_mapping_root_; + }; + enum output_options { + OUTPUT_NORMAL, + OUTPUT_QUIET, + }; - - - -#if 0 - - // Base class for all types of metadata damage. Used in reporting. - class metadata_damage { + class metadata_checker { public: - typedef boost::shared_ptr ptr; - virtual ~metadata_damage() {} - virtual void visit(metadata_damage_visitor &visitor) const = 0; + typedef boost::shared_ptr ptr; - void set_message(std::string const &message); - std::string const &get_message() const; + virtual ~metadata_checker() {} - private: - std::string message_; + virtual base::error_state check() = 0; }; - // FIXME: there's a mix of abstraction here, some classes represent - // the actual damage on disk (bad_ref_count), others represent the - // repercussions (missing_mapping). Need to revist, probably once - // we've got the reporting layer in. - - class super_block_corruption : public metadata_damage { - void visit(metadata_damage_visitor &visitor) const; - bool operator ==(super_block_corruption const &rhs) const; - }; - - typedef base::run run64; - - struct missing_device_details : public metadata_damage { - missing_device_details(run64 missing); - virtual void visit(metadata_damage_visitor &visitor) const; - bool operator ==(missing_device_details const &rhs) const; - - run64 missing_; - }; - - struct missing_devices : public metadata_damage { - missing_devices(run64 missing); - virtual void visit(metadata_damage_visitor &visitor) const; - bool operator ==(missing_devices const &rhs) const; - - run64 missing_; - }; - - struct missing_mappings : public metadata_damage { - missing_mappings(uint64_t dev, run64 missing); - virtual void visit(metadata_damage_visitor &visitor) const; - bool operator ==(missing_mappings const &rhs) const; - - uint64_t dev_; - run64 missing_; - }; - - struct bad_metadata_ref_count : public metadata_damage { - bad_metadata_ref_count(block_address b, - ref_t actual, - ref_t expected); - - virtual void visit(metadata_damage_visitor &visitor) const; - bool operator ==(bad_metadata_ref_count const &rhs) const; - - block_address b_; - ref_t actual_; - ref_t expected_; - }; - - struct bad_data_ref_count : public metadata_damage { - bad_data_ref_count(block_address b, - ref_t actual, - ref_t expected); - - virtual void visit(metadata_damage_visitor &visitor) const; - bool operator ==(bad_data_ref_count const &rhs) const; - - block_address b_; - ref_t actual_; - ref_t expected_; - }; - - struct missing_metadata_ref_counts : public metadata_damage { - missing_metadata_ref_counts(run64 missing); - virtual void visit(metadata_damage_visitor &visitor) const; - bool operator ==(missing_metadata_ref_counts const &rhs) const; - - run64 missing_; - }; - - struct missing_data_ref_counts : public metadata_damage { - missing_data_ref_counts(run64 missing); - virtual void visit(metadata_damage_visitor &visitor) const; - bool operator ==(missing_data_ref_counts const &rhs) const; - - run64 missing_; - }; - - class metadata_damage_visitor { - public: - typedef boost::shared_ptr ptr; - - virtual ~metadata_damage_visitor() {} - - void visit(metadata_damage const &damage); - virtual void visit(super_block_corruption const &damage) = 0; - virtual void visit(missing_device_details const &damage) = 0; - virtual void visit(missing_devices const &damage) = 0; - virtual void visit(missing_mappings const &damage) = 0; - virtual void visit(bad_metadata_ref_count const &damage) = 0; - virtual void visit(bad_data_ref_count const &damage) = 0; - virtual void visit(missing_metadata_ref_counts const &damage) = 0; - virtual void visit(missing_data_ref_counts const &damage) = 0; - }; - - typedef std::deque damage_list; - typedef boost::shared_ptr damage_list_ptr; -#endif + metadata_checker::ptr + create_base_checker(persistent_data::block_manager<>::ptr bm, + check_options const &check_opts, + output_options output_opts); } //---------------------------------------------------------------- diff --git a/thin-provisioning/thin_check.cc b/thin-provisioning/thin_check.cc index 6373603..36ba41b 100644 --- a/thin-provisioning/thin_check.cc +++ b/thin-provisioning/thin_check.cc @@ -28,258 +28,34 @@ #include "base/application.h" #include "base/error_state.h" #include "base/file_utils.h" -#include "base/nested_output.h" -#include "persistent-data/data-structures/btree_counter.h" -#include "persistent-data/space-maps/core.h" -#include "persistent-data/space-maps/disk.h" #include "persistent-data/file_utils.h" -#include "thin-provisioning/metadata.h" -#include "thin-provisioning/device_tree.h" -#include "thin-provisioning/mapping_tree.h" -#include "thin-provisioning/metadata_counter.h" -#include "thin-provisioning/superblock.h" #include "thin-provisioning/commands.h" +#include "thin-provisioning/metadata_checker.h" +#include "thin-provisioning/superblock.h" using namespace base; using namespace std; +using namespace persistent_data; using namespace thin_provisioning; //---------------------------------------------------------------- namespace { - class superblock_reporter : public superblock_detail::damage_visitor { - public: - superblock_reporter(nested_output &out) - : out_(out), - err_(NO_ERROR) { - } - - virtual void visit(superblock_detail::superblock_corruption const &d) { - out_ << "superblock is corrupt" << end_message(); - { - nested_output::nest _ = out_.push(); - out_ << d.desc_ << end_message(); - } - err_ << FATAL; - } - - base::error_state get_error() const { - return err_; - } - - private: - nested_output &out_; - error_state err_; - }; - - //-------------------------------- - - class devices_reporter : public device_tree_detail::damage_visitor { - public: - devices_reporter(nested_output &out) - : out_(out), - err_(NO_ERROR) { - } - - virtual void visit(device_tree_detail::missing_devices const &d) { - out_ << "missing devices: " << d.keys_ << end_message(); - { - nested_output::nest _ = out_.push(); - out_ << d.desc_ << end_message(); - } - - err_ << FATAL; - } - - error_state get_error() const { - return err_; - } - - private: - nested_output &out_; - error_state err_; - }; - - //-------------------------------- - - class mapping_reporter : public mapping_tree_detail::damage_visitor { - public: - mapping_reporter(nested_output &out) - : out_(out), - err_(NO_ERROR) { - } - - virtual void visit(mapping_tree_detail::missing_devices const &d) { - out_ << "missing all mappings for devices: " << d.keys_ << end_message(); - { - nested_output::nest _ = out_.push(); - out_ << d.desc_ << end_message(); - } - err_ << FATAL; - } - - virtual void visit(mapping_tree_detail::missing_mappings const &d) { - out_ << "thin device " << d.thin_dev_ << " is missing mappings " << d.keys_ << end_message(); - { - nested_output::nest _ = out_.push(); - out_ << d.desc_ << end_message(); - } - err_ << FATAL; - } - - error_state get_error() const { - return err_; - } - - private: - nested_output &out_; - error_state err_; - }; - - //-------------------------------- - struct flags { flags() - : check_device_tree(true), - check_mapping_tree_level1(true), - check_mapping_tree_level2(true), - ignore_non_fatal_errors(false), + : ignore_non_fatal_errors(false), quiet(false), clear_needs_check_flag_on_success(false) { } - bool check_device_tree; - bool check_mapping_tree_level1; - bool check_mapping_tree_level2; + check_options check_opts; bool ignore_non_fatal_errors; bool quiet; - boost::optional override_mapping_root; bool clear_needs_check_flag_on_success; }; - error_state check_space_map_counts(flags const &fs, nested_output &out, - superblock_detail::superblock &sb, - block_manager<>::ptr bm, - transaction_manager::ptr tm) { - block_counter bc; - - count_metadata(tm, sb, bc); - - // Finally we need to check the metadata space map agrees - // with the counts we've just calculated. - error_state err = NO_ERROR; - nested_output::nest _ = out.push(); - persistent_space_map::ptr metadata_sm = - open_metadata_sm(*tm, static_cast(&sb.metadata_space_map_root_)); - for (unsigned b = 0; b < metadata_sm->get_nr_blocks(); b++) { - ref_t c_actual = metadata_sm->get_count(b); - ref_t c_expected = bc.get_count(b); - - if (c_actual != c_expected) { - out << "metadata reference counts differ for block " << b - << ", expected " << c_expected - << ", but got " << c_actual - << end_message(); - - err << (c_actual > c_expected ? NON_FATAL : FATAL); - } - } - - return err; - } - - block_address mapping_root(superblock_detail::superblock const &sb, flags const &fs) - { - return fs.override_mapping_root ? *fs.override_mapping_root : sb.data_mapping_root_; - } - - void print_info(superblock_detail::superblock const &sb, - transaction_manager::ptr tm) - { - cout << "TRANSACTION_ID=" << sb.trans_id_ << "\n"; - cout << "METADATA_FREE_BLOCKS=" << tm->get_sm()->get_nr_free() << "\n"; - } - - error_state metadata_check(string const &path, flags fs) { - nested_output out(cerr, 2); - if (fs.quiet) - out.disable(); - - if (file_utils::get_file_length(path) < persistent_data::MD_BLOCK_SIZE) { - out << "Metadata device/file too small. Is this binary metadata?" - << end_message(); - return FATAL; - } - - block_manager<>::ptr bm = open_bm(path); - - superblock_reporter sb_rep(out); - devices_reporter dev_rep(out); - mapping_reporter mapping_rep(out); - - out << "examining superblock" << end_message(); - { - nested_output::nest _ = out.push(); - check_superblock(bm, sb_rep); - } - - if (sb_rep.get_error() == FATAL) { - if (check_for_xml(bm)) - out << "This looks like XML. thin_check only checks the binary metadata format." << end_message(); - return FATAL; - } - - superblock_detail::superblock sb = read_superblock(bm); - transaction_manager::ptr tm = - open_tm(bm, superblock_detail::SUPERBLOCK_LOCATION); - - if (!fs.quiet) - print_info(sb, tm); - - if (fs.check_device_tree) { - out << "examining devices tree" << end_message(); - { - nested_output::nest _ = out.push(); - device_tree dtree(*tm, sb.device_details_root_, - device_tree_detail::device_details_traits::ref_counter()); - check_device_tree(dtree, dev_rep); - } - } - - if (fs.check_mapping_tree_level1 && !fs.check_mapping_tree_level2) { - out << "examining top level of mapping tree" << end_message(); - { - nested_output::nest _ = out.push(); - dev_tree dtree(*tm, mapping_root(sb, fs), - mapping_tree_detail::mtree_traits::ref_counter(*tm)); - check_mapping_tree(dtree, mapping_rep); - } - - } else if (fs.check_mapping_tree_level2) { - out << "examining mapping tree" << end_message(); - { - nested_output::nest _ = out.push(); - mapping_tree mtree(*tm, mapping_root(sb, fs), - mapping_tree_detail::block_traits::ref_counter(tm->get_sm())); - check_mapping_tree(mtree, mapping_rep); - } - } - - error_state err = NO_ERROR; - err << sb_rep.get_error() << mapping_rep.get_error() << dev_rep.get_error(); - - // if we're checking everything, and there were no errors, - // then we should check the space maps too. - if (fs.check_device_tree && fs.check_mapping_tree_level2 && err != FATAL) { - out << "checking space map counts" << end_message(); - err << check_space_map_counts(fs, out, sb, bm, tm); - } - - return err; - } - void clear_needs_check(string const &path) { block_manager<>::ptr bm = open_bm(path, block_manager<>::READ_WRITE); @@ -291,11 +67,19 @@ namespace { // Returns 0 on success, 1 on failure (this gets returned directly // by main). int check(string const &path, flags fs) { - error_state err; bool success = false; try { - err = metadata_check(path, fs); + if (file_utils::get_file_length(path) < persistent_data::MD_BLOCK_SIZE) { + cerr << "Metadata device/file too small. Is this binary metadata?" + << endl; + return 1; + } + + block_manager<>::ptr bm = open_bm(path); + output_options output_opts = !fs.quiet ? OUTPUT_NORMAL : OUTPUT_QUIET; + metadata_checker::ptr checker = create_base_checker(bm, fs.check_opts, output_opts); + error_state err = checker->check(); if (fs.ignore_non_fatal_errors) success = (err == FATAL) ? false : true; @@ -373,14 +157,12 @@ thin_check_cmd::run(int argc, char **argv) case 1: // super-block-only - fs.check_device_tree = false; - fs.check_mapping_tree_level1 = false; - fs.check_mapping_tree_level2 = false; + fs.check_opts.set_superblock_only(); break; case 2: // skip-mappings - fs.check_mapping_tree_level2 = false; + fs.check_opts.set_skip_mappings(); break; case 3: @@ -395,7 +177,7 @@ thin_check_cmd::run(int argc, char **argv) case 5: // override-mapping-root - fs.override_mapping_root = boost::lexical_cast(optarg); + fs.check_opts.set_override_mapping_root(boost::lexical_cast(optarg)); break; default: