From 91b4714e389b3e81fd2c18b64e30021e437f197c Mon Sep 17 00:00:00 2001 From: Nikhil Kshirsagar Date: Fri, 25 Oct 2019 15:02:56 +0530 Subject: [PATCH] Corrupt superblock repair --- Makefile.in | 1 + functional-tests/thin-functional-tests.scm | 117 +++++++++++ thin-provisioning/metadata.cc | 1 - thin-provisioning/metadata_dumper.cc | 220 ++++++++++++++------- thin-provisioning/metadata_dumper.h | 15 +- thin-provisioning/override_emitter.cc | 95 +++++++++ thin-provisioning/override_emitter.h | 85 ++++++++ thin-provisioning/restore_emitter.cc | 3 + thin-provisioning/restore_emitter.h | 2 + thin-provisioning/superblock.h | 2 + thin-provisioning/thin_dump.cc | 60 +++--- thin-provisioning/thin_repair.cc | 126 +++++++----- thin-provisioning/thin_restore.cc | 171 +++++++++------- thin-provisioning/thin_show_duplicates.cc | 2 +- 14 files changed, 674 insertions(+), 226 deletions(-) create mode 100644 thin-provisioning/override_emitter.cc create mode 100644 thin-provisioning/override_emitter.h diff --git a/Makefile.in b/Makefile.in index 19453cc..c3ac8e8 100644 --- a/Makefile.in +++ b/Makefile.in @@ -101,6 +101,7 @@ SOURCE=\ thin-provisioning/metadata_checker.cc \ thin-provisioning/metadata_counter.cc \ thin-provisioning/metadata_dumper.cc \ + thin-provisioning/override_emitter.cc \ thin-provisioning/pool_stream.cc \ thin-provisioning/restore_emitter.cc \ thin-provisioning/rmap_visitor.cc \ diff --git a/functional-tests/thin-functional-tests.scm b/functional-tests/thin-functional-tests.scm index 6bb7d24..ff86c1f 100644 --- a/functional-tests/thin-functional-tests.scm +++ b/functional-tests/thin-functional-tests.scm @@ -51,6 +51,16 @@ (with-temp-file-sized ((md "thin.bin" (meg 4))) b1 b2 ...)))) + (define (damage-superblock md) + (system (string-append "dd if=/dev/zero of=" md " bs=4K count=1 conv=notrunc > /dev/null 2>&1"))) + + (define-syntax with-damaged-superblock + (syntax-rules () + ((_ (md) b1 b2 ...) + (with-valid-metadata (md) + (damage-superblock md) + b1 b2 ...)))) + ;; We have to export something that forces all the initialisation expressions ;; to run. (define (register-thin-tests) #t) @@ -230,6 +240,54 @@ (run-ok-rcv (stdout stderr) (thin-dump md) (assert-eof stderr)))) + (define-scenario (thin-dump override transaction-id) + "thin_dump obeys the --transaction-id override" + (with-valid-metadata (md) + (run-ok-rcv (stdout stderr) (thin-dump "--transaction-id 2345" md) + (assert-eof stderr) + (assert-matches ".*transaction=\"2345\"" stdout)))) + + (define-scenario (thin-dump override data-block-size) + "thin_dump obeys the --data-block-size override" + (with-valid-metadata (md) + (run-ok-rcv (stdout stderr) (thin-dump "--data-block-size 8192" md) + (assert-eof stderr) + (assert-matches ".*data_block_size=\"8192\"" stdout)))) + + (define-scenario (thin-dump override nr-data-blocks) + "thin_dump obeys the --nr-data-blocks override" + (with-valid-metadata (md) + (run-ok-rcv (stdout stderr) (thin-dump "--nr-data-blocks 234500" md) + (assert-eof stderr) + (assert-matches ".*nr_data_blocks=\"234500\"" stdout)))) + + (define-scenario (thin-dump repair-superblock succeeds) + "thin_dump can restore a missing superblock" + (with-valid-metadata (md) + (run-ok-rcv (expected-xml stderr) (thin-dump "--transaction-id=5" "--data-block-size=128" "--nr-data-blocks=4096000" md) + (damage-superblock md) + (run-ok-rcv (repaired-xml stderr) (thin-dump "--repair" "--transaction-id=5" "--data-block-size=128" "--nr-data-blocks=4096000" md) + (assert-eof stderr) + (assert-equal expected-xml repaired-xml))))) + + (define-scenario (thin-dump repair-superblock missing-transaction-id) + "--transaction-id is mandatory if the superblock is damaged" + (with-damaged-superblock (md) + (run-fail-rcv (_ stderr) (thin-dump "--repair" "--data-block-size=128" "--nr-data-blocks=4096000" md) + (assert-matches ".*transaction id.*" stderr)))) + + (define-scenario (thin-dump repair-superblock missing-data-block-size) + "--data-block-size is mandatory if the superblock is damaged" + (with-damaged-superblock (md) + (run-fail-rcv (_ stderr) (thin-dump "--repair" "--transaction-id=5" "--nr-data-blocks=4096000" md) + (assert-matches ".*data block size.*" stderr)))) + + (define-scenario (thin-dump repair-superblock missing-nr-data-blocks) + "--nr-data-blocks is mandatory if the superblock is damaged" + (with-damaged-superblock (md) + (run-fail-rcv (_ stderr) (thin-dump "--repair" "--transaction-id=5" "--data-block-size=128" md) + (assert-matches ".*nr data blocks.*" stderr)))) + ;;;----------------------------------------------------------- ;;; thin_rmap scenarios ;;;----------------------------------------------------------- @@ -356,4 +414,63 @@ (run-fail-rcv (_ stderr) (thin-repair "-i " xml) (assert-starts-with "No output file provided." stderr)))) + (define-scenario (thin-repair override transaction-id) + "thin_repair obeys the --transaction-id override" + (with-valid-metadata (md1) + (with-empty-metadata (md2) + (run-ok-rcv (stdout stderr) (thin-repair "--transaction-id 2345" "-i" md1 "-o" md2) + (assert-eof stderr)) + (run-ok-rcv (stdout stderr) (thin-dump md2) + (assert-matches ".*transaction=\"2345\"" stdout))))) + + (define-scenario (thin-repair override data-block-size) + "thin_repair obeys the --data-block-size override" + (with-valid-metadata (md1) + (with-empty-metadata (md2) + (run-ok-rcv (stdout stderr) (thin-repair "--data-block-size 8192" "-i" md1 "-o" md2) + (assert-eof stderr)) + (run-ok-rcv (stdout stderr) (thin-dump md2) + (assert-matches ".*data_block_size=\"8192\"" stdout))))) + + (define-scenario (thin-repair override nr-data-blocks) + "thin_repair obeys the --nr-data-blocks override" + (with-valid-metadata (md1) + (with-empty-metadata (md2) + (run-ok-rcv (stdout stderr) (thin-repair "--nr-data-blocks 234500" "-i" md1 "-o" md2) + (assert-eof stderr)) + (run-ok-rcv (stdout stderr) (thin-dump md2) + (assert-matches ".*nr_data_blocks=\"234500\"" stdout))))) + + (define-scenario (thin-repair superblock succeeds) + "thin_repair can restore a missing superblock" + (with-valid-metadata (md1) + (run-ok-rcv (expected-xml stderr) (thin-dump "--transaction-id=5" "--data-block-size=128" "--nr-data-blocks=4096000" md1) + (damage-superblock md1) + (with-empty-metadata (md2) + (run-ok-rcv (_ stderr) (thin-repair "--transaction-id=5" "--data-block-size=128" "--nr-data-blocks=4096000" "-i" md1 "-o" md2) + (assert-eof stderr)) + (run-ok-rcv (repaired-xml stderr) (thin-dump md2) + (assert-eof stderr) + (assert-equal expected-xml repaired-xml)))))) + + (define-scenario (thin-repair superblock missing-transaction-id) + "--transaction-id is mandatory if the superblock is damaged" + (with-damaged-superblock (md1) + (with-empty-metadata (md2) + (run-fail-rcv (_ stderr) (thin-repair "--data-block-size=128" "--nr-data-blocks=4096000" "-i" md1 "-o" md2) + (assert-matches ".*transaction id.*" stderr))))) + + (define-scenario (thin-repair superblock missing-data-block-size) + "--data-block-size is mandatory if the superblock is damaged" + (with-damaged-superblock (md1) + (with-empty-metadata (md2) + (run-fail-rcv (_ stderr) (thin-repair "--transaction-id=5" "--nr-data-blocks=4096000" "-i" md1 "-o" md2) + (assert-matches ".*data block size.*" stderr))))) + + (define-scenario (thin-repair superblock missing-nr-data-blocks) + "--nr-data-blocks is mandatory if the superblock is damaged" + (with-damaged-superblock (md1) + (with-empty-metadata (md2) + (run-fail-rcv (_ stderr) (thin-repair "--transaction-id=5" "--data-block-size=128" "-i" md1 "-o" md2) + (assert-matches ".*nr data blocks.*" stderr))))) ) diff --git a/thin-provisioning/metadata.cc b/thin-provisioning/metadata.cc index fa26cfa..58f0495 100644 --- a/thin-provisioning/metadata.cc +++ b/thin-provisioning/metadata.cc @@ -33,7 +33,6 @@ using namespace base; using namespace thin_provisioning; -#define METADATA_VERSION 2 //---------------------------------------------------------------- diff --git a/thin-provisioning/metadata_dumper.cc b/thin-provisioning/metadata_dumper.cc index ce28d2e..c56b94e 100644 --- a/thin-provisioning/metadata_dumper.cc +++ b/thin-provisioning/metadata_dumper.cc @@ -23,6 +23,7 @@ #include "thin-provisioning/mapping_tree.h" #include "thin-provisioning/metadata_dumper.h" +#include #include #include @@ -275,13 +276,20 @@ namespace { class gatherer { public: - gatherer(block_manager<> &bm) - : bm_(bm), - referenced_(bm.get_nr_blocks(), false), - examined_(bm.get_nr_blocks(), false) { - } + gatherer(block_manager<> &bm) + : bm_(bm), + referenced_(bm.get_nr_blocks(), false), + examined_(bm.get_nr_blocks(), false) { + } + + struct roots { + block_address mapping_root; + block_address detail_root; + uint32_t time; + }; + + optional - optional> find_best_roots(transaction_manager &tm) { vector mapping_roots; vector device_roots; @@ -296,14 +304,16 @@ namespace { auto info = get_info(b); - if (info.valid) { - if (info.type == TOP_LEVEL) - mapping_roots.push_back(info); + if (info.valid) { + if (info.type == TOP_LEVEL) { + mapping_roots.push_back(info); + } - else if (info.type == DEVICE_DETAILS) - device_roots.push_back(info); - } - } + else if (info.type == DEVICE_DETAILS) { + device_roots.push_back(info); + } + } + } #if SHOW_WORKING cerr << "mapping candidates (" << mapping_roots.size() << "):\n"; @@ -322,13 +332,28 @@ namespace { cerr << "(" << p.first << ", " << p.second << ")\n"; #endif - if (pairs.size()) - return pairs[0]; - else - return optional>(); - } + if (pairs.size()) + return mk_roots(pairs[0]); + else + return optional(); + } private: + uint32_t get_time(block_address b) const { + auto i = lookup_info(b); + return i ? i->age : 0; + } + + roots mk_roots(pair const &p) { + roots r; + + r.mapping_root = p.second; + r.detail_root = p.first; + r.time = max(get_time(p.first), get_time(p.second)); + + return r; + } + bool set_eq(set const &lhs, set const &rhs) { for (auto v : lhs) if (!rhs.count(v)) @@ -599,6 +624,15 @@ namespace { } } + optional lookup_info(block_address b) const { + auto it = infos_.find(b); + if (it == infos_.end()) + return optional(); + + return optional(it->second); + } + + block_manager<> &bm_; vector referenced_; vector examined_; @@ -745,53 +779,102 @@ namespace { return 0ull; } - void - do_repair_(block_manager<>::ptr bm, superblock_detail::superblock const &sb, emitter::ptr e) - { - metadata md(bm, sb); - dump_options opts; - details_extractor de(opts); - device_tree_detail::damage_visitor::ptr dd_policy(details_damage_policy(true)); - walk_device_tree(*md.details_, de, *dd_policy); - e->begin_superblock("", sb.time_, - sb.trans_id_, - sb.flags_, - sb.version_, - sb.data_block_size_, - get_nr_blocks(md), - boost::optional()); + void + emit_trees_(block_manager<>::ptr bm, superblock_detail::superblock const &sb, + emitter::ptr e, override_options const &ropts) + { + metadata md(bm, sb); + dump_options opts; + details_extractor de(opts); + device_tree_detail::damage_visitor::ptr dd_policy(details_damage_policy(true)); + walk_device_tree(*md.details_, de, *dd_policy); - { - mapping_tree_detail::damage_visitor::ptr md_policy(mapping_damage_policy(true)); - mapping_tree_emit_visitor mte(opts, *md.tm_, e, de.get_details(), mapping_damage_policy(true)); - walk_mapping_tree(*md.mappings_top_level_, mte, *md_policy); - } + e->begin_superblock("", sb.time_, + sb.trans_id_, + sb.flags_, + sb.version_, + sb.data_block_size_, + get_nr_blocks(md), + boost::optional()); - e->end_superblock(); - } + { + mapping_tree_detail::damage_visitor::ptr md_policy(mapping_damage_policy(true)); + mapping_tree_emit_visitor mte(opts, *md.tm_, e, de.get_details(), mapping_damage_policy(true)); + walk_mapping_tree(*md.mappings_top_level_, mte, *md_policy); + } - void - metadata_repair_(block_manager<>::ptr bm, emitter::ptr e) - { - // We assume the superblock is wrong, and find the best roots - // for ourselves. We've had a few cases where people have - // activated a pool on multiple hosts at once, which results in - // the superblock being over written. - gatherer g(*bm); - auto tm = open_tm(bm, superblock_detail::SUPERBLOCK_LOCATION); - auto p = g.find_best_roots(*tm); - auto sb = read_superblock(*bm); + e->end_superblock(); + } - if (p) { - sb.metadata_snap_ = 0; - sb.device_details_root_ = p->first; - sb.data_mapping_root_ = p->second; - sb.metadata_nr_blocks_ = bm->get_nr_blocks(); - } + void + find_better_roots_(block_manager<>::ptr bm, superblock_detail::superblock &sb) + { + // We assume the superblock is wrong, and find the best roots + // for ourselves. We've had a few cases where people have + // activated a pool on multiple hosts at once, which results in + // the superblock being over written. + gatherer g(*bm); + auto tm = open_tm(bm, superblock_detail::SUPERBLOCK_LOCATION); + auto p = g.find_best_roots(*tm); + + if (p) { + sb.metadata_snap_ = 0; + sb.time_ = p->time; + sb.device_details_root_ = p->detail_root; + sb.data_mapping_root_ = p->mapping_root; + sb.metadata_nr_blocks_ = bm->get_nr_blocks(); + } + } + + superblock_detail::superblock + recreate_superblock(override_options const &opts) + { + superblock_detail::superblock sb; + memset(&sb, 0, sizeof(sb)); + + // FIXME: we need to get this by walking both the mapping and device trees. + sb.time_ = 100000; + + + sb.trans_id_ = opts.get_transaction_id(); + sb.version_ = superblock_detail::METADATA_VERSION; + sb.data_block_size_ = opts.get_data_block_size(); + + // Check that this has been overridden. + opts.get_nr_data_blocks(); + + return sb; + } + + optional + maybe_read_superblock(block_manager<>::ptr bm) + { + try { + auto sb = read_superblock(bm); + return optional(sb); + } catch (...) { + } + + return optional(); + } + + void + metadata_repair_(block_manager<>::ptr bm, emitter::ptr e, override_options const &opts) + { + auto msb = maybe_read_superblock(bm); + if (!msb) + msb = recreate_superblock(opts); + + auto tm = open_tm(bm, superblock_detail::SUPERBLOCK_LOCATION); + + if (!get_dev_ids(*tm, msb->device_details_root_) || + !get_map_ids(*tm, msb->data_mapping_root_)) + find_better_roots_(bm, *msb); + + emit_trees_(bm, *msb, e, opts); + } - do_repair_(bm, sb, e); - } } //---------------------------------------------------------------- @@ -821,18 +904,17 @@ thin_provisioning::metadata_dump(metadata::ptr md, emitter::ptr e, dump_options } void -thin_provisioning::metadata_repair(block_manager<>::ptr bm, emitter::ptr e) +thin_provisioning::metadata_repair(block_manager<>::ptr bm, emitter::ptr e, override_options const &opts) { - auto sb = read_superblock(*bm); - auto tm = open_tm(bm, superblock_detail::SUPERBLOCK_LOCATION); + try { + metadata_repair_(bm, e, opts); - if (get_dev_ids(*tm, sb.device_details_root_) && get_map_ids(*tm, sb.data_mapping_root_)) { - // The roots in the superblock look ok (perhaps the corruption was in the - // space maps). - auto sb = read_superblock(bm); - do_repair_(bm, sb, e); - } else - metadata_repair_(bm, e); + } catch (override_error const &e) { + ostringstream out; + out << "The following field needs to be provided on the command line due to corruption in the superblock: " + << e.what(); + throw runtime_error(out.str()); + } } //---------------------------------------------------------------- diff --git a/thin-provisioning/metadata_dumper.h b/thin-provisioning/metadata_dumper.h index a738dd4..2de756f 100644 --- a/thin-provisioning/metadata_dumper.h +++ b/thin-provisioning/metadata_dumper.h @@ -19,8 +19,9 @@ #ifndef METADATA_DUMPER_H #define METADATA_DUMPER_H -#include "emitter.h" -#include "metadata.h" +#include "thin-provisioning/emitter.h" +#include "thin-provisioning/metadata.h" +#include "thin-provisioning/override_emitter.h" #include #include @@ -46,6 +47,8 @@ namespace thin_provisioning { } bool skip_mappings_; + override_options overrides_; + using dev_set = std::set; using maybe_dev_set = boost::optional; @@ -58,10 +61,10 @@ namespace thin_provisioning { // corruption encountered will cause an exception to be thrown. void metadata_dump(metadata::ptr md, emitter::ptr e, dump_options const &opts); - // We have to provide a different interface for repairing, since - // the superblock itself may be corrupt, so we wont be able - // to create the metadata object. - void metadata_repair(block_manager<>::ptr bm, emitter::ptr e); + // We have to provide a different interface for repairing, since + // the superblock itself may be corrupt, so we wont be able + // to create the metadata object. + void metadata_repair(block_manager<>::ptr bm, emitter::ptr e, override_options const &opts); // Only used by ll_restore, so we leave the repair arg void metadata_dump_subtree(metadata::ptr md, emitter::ptr e, bool repair, uint64_t subtree_root); diff --git a/thin-provisioning/override_emitter.cc b/thin-provisioning/override_emitter.cc new file mode 100644 index 0000000..554e435 --- /dev/null +++ b/thin-provisioning/override_emitter.cc @@ -0,0 +1,95 @@ +// Copyright (C) 2019 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 "thin-provisioning/override_emitter.h" + +using namespace thin_provisioning; + +//---------------------------------------------------------------- + +namespace { + class override_emitter : public emitter { + public: + override_emitter(emitter::ptr inner, override_options const &opts) + : inner_(inner), + opts_(opts) { + } + + virtual void begin_superblock(std::string const &uuid, + uint64_t time, + uint64_t trans_id, + boost::optional flags, + boost::optional version, + uint32_t data_block_size, + uint64_t nr_data_blocks, + boost::optional metadata_snap) { + inner_->begin_superblock(uuid, time, opts_.get_transaction_id(trans_id), + flags, version, opts_.get_data_block_size(data_block_size), + opts_.get_nr_data_blocks(nr_data_blocks), + metadata_snap); + } + + virtual void end_superblock() { + inner_->end_superblock(); + } + + virtual void begin_device(uint32_t dev, + uint64_t mapped_blocks, + uint64_t trans_id, + uint64_t creation_time, + uint64_t snap_time) { + inner_->begin_device(dev, mapped_blocks, trans_id, creation_time, snap_time); + } + + virtual void end_device() { + inner_->end_device(); + } + + virtual void begin_named_mapping(std::string const &name) { + inner_->begin_named_mapping(name); + } + + virtual void end_named_mapping() { + inner_->end_named_mapping(); + } + + virtual void identifier(std::string const &name) { + inner_->identifier(name); + } + + virtual void range_map(uint64_t origin_begin, uint64_t data_begin, uint32_t time, uint64_t len) { + inner_->range_map(origin_begin, data_begin, time, len); + } + + virtual void single_map(uint64_t origin_block, uint64_t data_block, uint32_t time) { + inner_->single_map(origin_block, data_block, time); + } + + private: + emitter::ptr inner_; + override_options opts_; + }; +} + +emitter::ptr thin_provisioning::create_override_emitter(emitter::ptr inner, override_options const &opts) +{ + return emitter::ptr(new override_emitter(inner, opts)); +} + +//---------------------------------------------------------------- + diff --git a/thin-provisioning/override_emitter.h b/thin-provisioning/override_emitter.h new file mode 100644 index 0000000..0af316b --- /dev/null +++ b/thin-provisioning/override_emitter.h @@ -0,0 +1,85 @@ +// Copyright (C) 2019 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 TP_OVERRIDE_EMITTER_H +#define TP_OVERRIDE_EMITTER_H + +#include "emitter.h" +#include "metadata.h" + +#include +#include + +//---------------------------------------------------------------- + +namespace thin_provisioning { + struct override_error : public std::runtime_error { + override_error(std::string const &str) + : std::runtime_error(str) { + } + }; + + struct override_options { + uint64_t get_transaction_id() const { + if (!transaction_id_) + bad_override_("transaction id"); + + return *transaction_id_; + } + + uint64_t get_transaction_id(uint64_t dflt) const { + return transaction_id_ ? *transaction_id_ : dflt; + } + + uint32_t get_data_block_size() const { + if (!data_block_size_) + bad_override_("data block size"); + + return *data_block_size_; + } + uint32_t get_data_block_size(uint32_t dflt) const { + return data_block_size_ ? *data_block_size_ : dflt; + } + + uint64_t get_nr_data_blocks() const { + if (!nr_data_blocks_) + bad_override_("nr data blocks"); + + return *nr_data_blocks_; + } + + uint64_t get_nr_data_blocks(uint64_t dflt) const { + return nr_data_blocks_ ? *nr_data_blocks_ : dflt; + } + + boost::optional transaction_id_; + boost::optional data_block_size_; + boost::optional nr_data_blocks_; + + private: + void bad_override_(std::string const &field) const { + throw override_error(field); + } + }; + + emitter::ptr create_override_emitter(emitter::ptr inner, override_options const &opts); +} + +//---------------------------------------------------------------- + +#endif diff --git a/thin-provisioning/restore_emitter.cc b/thin-provisioning/restore_emitter.cc index 46b7f92..9c76b1f 100644 --- a/thin-provisioning/restore_emitter.cc +++ b/thin-provisioning/restore_emitter.cc @@ -16,6 +16,7 @@ // with thin-provisioning-tools. If not, see // . +#include "thin-provisioning/override_emitter.h" #include "thin-provisioning/restore_emitter.h" #include "thin-provisioning/superblock.h" @@ -155,6 +156,8 @@ namespace { } metadata::ptr md_; + override_options opts_; + bool in_superblock_; block_address nr_data_blocks_; boost::optional current_device_; diff --git a/thin-provisioning/restore_emitter.h b/thin-provisioning/restore_emitter.h index c77b536..e6588fb 100644 --- a/thin-provisioning/restore_emitter.h +++ b/thin-provisioning/restore_emitter.h @@ -22,6 +22,8 @@ #include "emitter.h" #include "metadata.h" +#include + //---------------------------------------------------------------- namespace thin_provisioning { diff --git a/thin-provisioning/superblock.h b/thin-provisioning/superblock.h index 5e1ba2c..7f6a569 100644 --- a/thin-provisioning/superblock.h +++ b/thin-provisioning/superblock.h @@ -97,6 +97,8 @@ namespace thin_provisioning { block_address const SUPERBLOCK_LOCATION = 0; uint32_t const SUPERBLOCK_MAGIC = 27022010; + uint32_t const METADATA_VERSION = 2; + //-------------------------------- diff --git a/thin-provisioning/thin_dump.cc b/thin-provisioning/thin_dump.cc index 7ea968b..4635fe6 100644 --- a/thin-provisioning/thin_dump.cc +++ b/thin-provisioning/thin_dump.cc @@ -84,33 +84,35 @@ namespace { return e; } - int dump_(string const &path, ostream &out, struct flags &flags) { - try { - emitter::ptr e = create_emitter(flags.format, out); + int dump_(string const &path, ostream &out, struct flags &flags) { + try { + emitter::ptr inner = create_emitter(flags.format, out); + emitter::ptr e = create_override_emitter(inner, flags.opts.overrides_); - if (flags.repair) { - auto bm = open_bm(path, block_manager<>::READ_ONLY, true); - metadata_repair(bm, e); - } else { - metadata::ptr md = open_metadata(path, flags); - metadata_dump(md, e, flags.opts); - } + if (flags.repair) { + auto bm = open_bm(path, block_manager<>::READ_ONLY, true); + metadata_repair(bm, e, flags.opts.overrides_); + } else { + metadata::ptr md = open_metadata(path, flags); + metadata_dump(md, e, flags.opts); + } - } catch (std::exception &e) { - cerr << e.what() << endl; - return 1; - } + } catch (std::exception &e) { + cerr << e.what() << endl; + return 1; + } - return 0; - } + return 0; + } + + int dump(string const &path, char const *output, struct flags &flags) { + if (output) { + ofstream out(output); + return dump_(path, out, flags); + } else + return dump_(path, cout, flags); + } - int dump(string const &path, char const *output, struct flags &flags) { - if (output) { - ofstream out(output); - return dump_(path, out, flags); - } else - return dump_(path, cout, flags); - } } //---------------------------------------------------------------- @@ -205,6 +207,18 @@ thin_dump_cmd::run(int argc, char **argv) flags.opts.skip_mappings_ = true; break; + case 3: + flags.opts.overrides_.transaction_id_ = parse_uint64(optarg, "transaction id"); + break; + + case 4: + flags.opts.overrides_.data_block_size_ = static_cast(parse_uint64(optarg, "data block size")); + break; + + case 5: + flags.opts.overrides_.nr_data_blocks_ = parse_uint64(optarg, "nr data blocks"); + break; + case 'V': cout << THIN_PROVISIONING_TOOLS_VERSION << endl; return 0; diff --git a/thin-provisioning/thin_repair.cc b/thin-provisioning/thin_repair.cc index 0673753..31e4ff6 100644 --- a/thin-provisioning/thin_repair.cc +++ b/thin-provisioning/thin_repair.cc @@ -17,27 +17,28 @@ using namespace std; using namespace thin_provisioning; namespace { - int repair(string const &old_path, string const &new_path) { - bool metadata_touched = false; - try { - // block size gets updated by the restorer - block_manager<>::ptr new_bm = open_bm(new_path, block_manager<>::READ_WRITE); - file_utils::check_file_exists(old_path, false); - metadata_touched = true; - metadata::ptr new_md(new metadata(new_bm, metadata::CREATE, 128, 0)); - emitter::ptr e = create_restore_emitter(new_md); - block_manager<>::ptr old_bm = open_bm(old_path, block_manager<>::READ_ONLY); - metadata_repair(old_bm, e); + int repair(string const &old_path, string const &new_path, override_options const &opts) { + bool metadata_touched = false; + try { + // block size gets updated by the restorer + block_manager<>::ptr new_bm = open_bm(new_path, block_manager<>::READ_WRITE); + file_utils::check_file_exists(old_path, false); + metadata_touched = true; + metadata::ptr new_md(new metadata(new_bm, metadata::CREATE, 128, 0)); + emitter::ptr inner = create_restore_emitter(new_md); + emitter::ptr e = create_override_emitter(inner, opts); + block_manager<>::ptr old_bm = open_bm(old_path, block_manager<>::READ_ONLY); + metadata_repair(old_bm, e, opts); - } catch (std::exception &e) { - if (metadata_touched) - file_utils::zero_superblock(new_path); - cerr << e.what() << endl; - return 1; - } + } catch (std::exception &e) { + if (metadata_touched) + file_utils::zero_superblock(new_path); + cerr << e.what() << endl; + return 1; + } - return 0; - } + return 0; + } } //---------------------------------------------------------------- @@ -50,12 +51,15 @@ thin_repair_cmd::thin_repair_cmd() void thin_repair_cmd::usage(std::ostream &out) const { - out << "Usage: " << get_name() << " [options] {device|file}" << endl - << "Options:" << endl - << " {-h|--help}" << endl - << " {-i|--input} " << endl - << " {-o|--output} " << endl - << " {-V|--version}" << endl; + out << "Usage: " << get_name() << " [options] {device|file}" << endl + << "Options:" << endl + << " {-h|--help}" << endl + << " {-i|--input} " << endl + << " {-o|--output} " << endl + << " {--transaction-id} " << endl + << " {--data-block-size} " << endl + << " {--nr-data-blocks} " << endl + << " {-V|--version}" << endl; } int @@ -63,39 +67,56 @@ thin_repair_cmd::run(int argc, char **argv) { int c; boost::optional input_path, output_path; + override_options opts; + const char shortopts[] = "hi:o:V"; - const struct option longopts[] = { - { "help", no_argument, NULL, 'h'}, - { "input", required_argument, NULL, 'i'}, - { "output", required_argument, NULL, 'o'}, - { "version", no_argument, NULL, 'V'}, - { NULL, no_argument, NULL, 0 } - }; + const struct option longopts[] = { + { "help", no_argument, NULL, 'h'}, + { "input", required_argument, NULL, 'i'}, + { "output", required_argument, NULL, 'o'}, + { "transaction-id", required_argument, NULL, 1}, + { "data-block-size", required_argument, NULL, 2}, + { "nr-data-blocks", required_argument, NULL, 3}, + { "version", no_argument, NULL, 'V'}, + { NULL, no_argument, NULL, 0 } + }; - while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { - switch(c) { - case 'h': - usage(cout); - return 0; + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch(c) { + case 'h': + usage(cout); + return 0; - case 'i': - input_path = optarg; - break; + case 'i': + input_path = optarg; + break; - case 'o': - output_path = optarg; - break; + case 'o': + output_path = optarg; + break; - case 'V': - cout << THIN_PROVISIONING_TOOLS_VERSION << endl; - return 0; + case 1: + opts.transaction_id_ = parse_uint64(optarg, "transaction id"); + break; - default: - usage(cerr); - return 1; - } - } + case 2: + opts.data_block_size_ = static_cast(parse_uint64(optarg, "data block size")); + break; + + case 3: + opts.nr_data_blocks_ = parse_uint64(optarg, "nr data blocks"); + break; + + case 'V': + cout << THIN_PROVISIONING_TOOLS_VERSION << endl; + return 0; + + default: + usage(cerr); + return 1; + } + } if (!input_path) { cerr << "no input file provided" << endl; @@ -112,7 +133,8 @@ thin_repair_cmd::run(int argc, char **argv) return 1; } - return repair(*input_path, *output_path); + return repair(*input_path, *output_path, opts); + } //---------------------------------------------------------------- diff --git a/thin-provisioning/thin_restore.cc b/thin-provisioning/thin_restore.cc index fb052fa..3441eab 100644 --- a/thin-provisioning/thin_restore.cc +++ b/thin-provisioning/thin_restore.cc @@ -23,6 +23,7 @@ #include "thin-provisioning/emitter.h" #include "thin-provisioning/human_readable_format.h" #include "thin-provisioning/metadata.h" +#include "thin-provisioning/override_emitter.h" #include "thin-provisioning/restore_emitter.h" #include "thin-provisioning/xml_format.h" #include "version.h" @@ -44,27 +45,28 @@ using namespace thin_provisioning; //---------------------------------------------------------------- namespace { - int restore(string const &backup_file, string const &dev, bool quiet) { - bool metadata_touched = false; - try { - // The block size gets updated by the restorer. - block_manager<>::ptr bm(open_bm(dev, block_manager<>::READ_WRITE)); - file_utils::check_file_exists(backup_file); - metadata_touched = true; - metadata::ptr md(new metadata(bm, metadata::CREATE, 128, 0)); - emitter::ptr restorer = create_restore_emitter(md); + int restore(string const &backup_file, string const &dev, bool quiet, override_options const &opts) { + bool metadata_touched = false; + try { + // The block size gets updated by the restorer. + block_manager<>::ptr bm(open_bm(dev, block_manager<>::READ_WRITE)); + file_utils::check_file_exists(backup_file); + metadata_touched = true; + metadata::ptr md(new metadata(bm, metadata::CREATE, 128, 0)); + emitter::ptr inner = create_restore_emitter(md); + emitter::ptr restorer = create_override_emitter(inner, opts); - parse_xml(backup_file, restorer, quiet); + parse_xml(backup_file, restorer, quiet); - } catch (std::exception &e) { - if (metadata_touched) - file_utils::zero_superblock(dev); - cerr << e.what() << endl; - return 1; - } + } catch (std::exception &e) { + if (metadata_touched) + file_utils::zero_superblock(dev); + cerr << e.what() << endl; + return 1; + } - return 0; - } + return 0; + } } //---------------------------------------------------------------- @@ -77,78 +79,99 @@ thin_restore_cmd::thin_restore_cmd() void thin_restore_cmd::usage(std::ostream &out) const { - out << "Usage: " << get_name() << " [options]" << endl - << "Options:" << endl - << " {-h|--help}" << endl - << " {-i|--input} " << endl - << " {-o|--output} " << endl - << " {-q|--quiet}" << endl - << " {-V|--version}" << endl; + out << "Usage: " << get_name() << " [options]" << endl + << "Options:" << endl + << " {-h|--help}" << endl + << " {-i|--input} " << endl + << " {-o|--output} " << endl + << " {--transaction-id} " << endl + << " {--data-block-size} " << endl + << " {--nr-data-blocks} " << endl + << " {-q|--quiet}" << endl + << " {-V|--version}" << endl; } int thin_restore_cmd::run(int argc, char **argv) { - int c; - const char *shortopts = "hi:o:qV"; - string input, output; - bool quiet = false; - const struct option longopts[] = { - { "help", no_argument, NULL, 'h'}, - { "input", required_argument, NULL, 'i' }, - { "output", required_argument, NULL, 'o'}, - { "quiet", no_argument, NULL, 'q'}, - { "version", no_argument, NULL, 'V'}, - { NULL, no_argument, NULL, 0 } - }; + int c; + const char *shortopts = "hi:o:qV"; + string input, output; + bool quiet = false; + override_options opts; - while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { - switch(c) { - case 'h': - usage(cout); - return 0; + const struct option longopts[] = { + { "help", no_argument, NULL, 'h'}, + { "input", required_argument, NULL, 'i' }, + { "output", required_argument, NULL, 'o'}, + { "transaction-id", required_argument, NULL, 1}, + { "data-block-size", required_argument, NULL, 2}, + { "nr-data-blocks", required_argument, NULL, 3}, + { "quiet", no_argument, NULL, 'q'}, + { "version", no_argument, NULL, 'V'}, + { NULL, no_argument, NULL, 0 } + }; - case 'i': - input = optarg; - break; + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch(c) { + case 'h': + usage(cout); + return 0; - case 'o': - output = optarg; - break; + case 'i': + input = optarg; + break; - case 'q': - quiet = true; - break; + case 'o': + output = optarg; + break; - case 'V': - cout << THIN_PROVISIONING_TOOLS_VERSION << endl; - return 0; + case 1: + opts.transaction_id_ = parse_uint64(optarg, "transaction id"); + break; - default: - usage(cerr); - return 1; - } - } + case 2: + opts.data_block_size_ = static_cast(parse_uint64(optarg, "data block size")); + break; - if (argc != optind) { - usage(cerr); - return 1; - } + case 3: + opts.nr_data_blocks_ = parse_uint64(optarg, "nr data blocks"); + break; + + case 'q': + quiet = true; + break; + + case 'V': + cout << THIN_PROVISIONING_TOOLS_VERSION << endl; + return 0; + + default: + usage(cerr); + return 1; + } + } + + if (argc != optind) { + usage(cerr); + return 1; + } if (input.empty()) { - cerr << "No input file provided." << endl << endl; - usage(cerr); - return 1; - } + cerr << "No input file provided." << endl << endl; + usage(cerr); + return 1; + } - if (output.empty()) { - cerr << "No output file provided." << endl << endl; - usage(cerr); - return 1; - } else - check_output_file_requirements(output); + if (output.empty()) { + cerr << "No output file provided." << endl << endl; + usage(cerr); + return 1; + } else + check_output_file_requirements(output); - return restore(input, output, quiet); + return restore(input, output, quiet, opts); } //---------------------------------------------------------------- + diff --git a/thin-provisioning/thin_show_duplicates.cc b/thin-provisioning/thin_show_duplicates.cc index f9faaaf..7c6c6f2 100644 --- a/thin-provisioning/thin_show_duplicates.cc +++ b/thin-provisioning/thin_show_duplicates.cc @@ -40,7 +40,7 @@ #include "thin-provisioning/superblock.h" #include "thin-provisioning/variable_chunk_stream.h" -#include +#include #include #include #include