Corrupt superblock repair

This commit is contained in:
Nikhil Kshirsagar 2019-10-25 15:02:56 +05:30
parent 0fc7529c01
commit 91b4714e38
14 changed files with 674 additions and 226 deletions

View File

@ -101,6 +101,7 @@ SOURCE=\
thin-provisioning/metadata_checker.cc \ thin-provisioning/metadata_checker.cc \
thin-provisioning/metadata_counter.cc \ thin-provisioning/metadata_counter.cc \
thin-provisioning/metadata_dumper.cc \ thin-provisioning/metadata_dumper.cc \
thin-provisioning/override_emitter.cc \
thin-provisioning/pool_stream.cc \ thin-provisioning/pool_stream.cc \
thin-provisioning/restore_emitter.cc \ thin-provisioning/restore_emitter.cc \
thin-provisioning/rmap_visitor.cc \ thin-provisioning/rmap_visitor.cc \

View File

@ -51,6 +51,16 @@
(with-temp-file-sized ((md "thin.bin" (meg 4))) (with-temp-file-sized ((md "thin.bin" (meg 4)))
b1 b2 ...)))) 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 ;; We have to export something that forces all the initialisation expressions
;; to run. ;; to run.
(define (register-thin-tests) #t) (define (register-thin-tests) #t)
@ -230,6 +240,54 @@
(run-ok-rcv (stdout stderr) (thin-dump md) (run-ok-rcv (stdout stderr) (thin-dump md)
(assert-eof stderr)))) (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 ;;; thin_rmap scenarios
;;;----------------------------------------------------------- ;;;-----------------------------------------------------------
@ -356,4 +414,63 @@
(run-fail-rcv (_ stderr) (thin-repair "-i " xml) (run-fail-rcv (_ stderr) (thin-repair "-i " xml)
(assert-starts-with "No output file provided." stderr)))) (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)))))
) )

View File

@ -33,7 +33,6 @@
using namespace base; using namespace base;
using namespace thin_provisioning; using namespace thin_provisioning;
#define METADATA_VERSION 2
//---------------------------------------------------------------- //----------------------------------------------------------------

View File

@ -23,6 +23,7 @@
#include "thin-provisioning/mapping_tree.h" #include "thin-provisioning/mapping_tree.h"
#include "thin-provisioning/metadata_dumper.h" #include "thin-provisioning/metadata_dumper.h"
#include <algorithm>
#include <map> #include <map>
#include <vector> #include <vector>
@ -275,13 +276,20 @@ namespace {
class gatherer { class gatherer {
public: public:
gatherer(block_manager<> &bm) gatherer(block_manager<> &bm)
: bm_(bm), : bm_(bm),
referenced_(bm.get_nr_blocks(), false), referenced_(bm.get_nr_blocks(), false),
examined_(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<roots>
optional<pair<block_address, block_address>>
find_best_roots(transaction_manager &tm) { find_best_roots(transaction_manager &tm) {
vector<node_info> mapping_roots; vector<node_info> mapping_roots;
vector<node_info> device_roots; vector<node_info> device_roots;
@ -296,14 +304,16 @@ namespace {
auto info = get_info(b); auto info = get_info(b);
if (info.valid) { if (info.valid) {
if (info.type == TOP_LEVEL) if (info.type == TOP_LEVEL) {
mapping_roots.push_back(info); mapping_roots.push_back(info);
}
else if (info.type == DEVICE_DETAILS) else if (info.type == DEVICE_DETAILS) {
device_roots.push_back(info); device_roots.push_back(info);
} }
} }
}
#if SHOW_WORKING #if SHOW_WORKING
cerr << "mapping candidates (" << mapping_roots.size() << "):\n"; cerr << "mapping candidates (" << mapping_roots.size() << "):\n";
@ -322,13 +332,28 @@ namespace {
cerr << "(" << p.first << ", " << p.second << ")\n"; cerr << "(" << p.first << ", " << p.second << ")\n";
#endif #endif
if (pairs.size()) if (pairs.size())
return pairs[0]; return mk_roots(pairs[0]);
else else
return optional<pair<block_address, block_address>>(); return optional<roots>();
} }
private: private:
uint32_t get_time(block_address b) const {
auto i = lookup_info(b);
return i ? i->age : 0;
}
roots mk_roots(pair<block_address, block_address> const &p) {
roots r;
r.mapping_root = p.second;
r.detail_root = p.first;
r.time = max<block_address>(get_time(p.first), get_time(p.second));
return r;
}
bool set_eq(set<uint32_t> const &lhs, set<uint32_t> const &rhs) { bool set_eq(set<uint32_t> const &lhs, set<uint32_t> const &rhs) {
for (auto v : lhs) for (auto v : lhs)
if (!rhs.count(v)) if (!rhs.count(v))
@ -599,6 +624,15 @@ namespace {
} }
} }
optional<node_info> lookup_info(block_address b) const {
auto it = infos_.find(b);
if (it == infos_.end())
return optional<node_info>();
return optional<node_info>(it->second);
}
block_manager<> &bm_; block_manager<> &bm_;
vector<bool> referenced_; vector<bool> referenced_;
vector<bool> examined_; vector<bool> examined_;
@ -745,53 +779,102 @@ namespace {
return 0ull; 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_, void
sb.trans_id_, emit_trees_(block_manager<>::ptr bm, superblock_detail::superblock const &sb,
sb.flags_, emitter::ptr e, override_options const &ropts)
sb.version_, {
sb.data_block_size_, metadata md(bm, sb);
get_nr_blocks(md), dump_options opts;
boost::optional<block_address>()); 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_,
mapping_tree_detail::damage_visitor::ptr md_policy(mapping_damage_policy(true)); sb.trans_id_,
mapping_tree_emit_visitor mte(opts, *md.tm_, e, de.get_details(), mapping_damage_policy(true)); sb.flags_,
walk_mapping_tree(*md.mappings_top_level_, mte, *md_policy); sb.version_,
} sb.data_block_size_,
get_nr_blocks(md),
boost::optional<block_address>());
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 e->end_superblock();
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);
if (p) { void
sb.metadata_snap_ = 0; find_better_roots_(block_manager<>::ptr bm, superblock_detail::superblock &sb)
sb.device_details_root_ = p->first; {
sb.data_mapping_root_ = p->second; // We assume the superblock is wrong, and find the best roots
sb.metadata_nr_blocks_ = bm->get_nr_blocks(); // 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<superblock_detail::superblock>
maybe_read_superblock(block_manager<>::ptr bm)
{
try {
auto sb = read_superblock(bm);
return optional<superblock_detail::superblock>(sb);
} catch (...) {
}
return optional<superblock_detail::superblock>();
}
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 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); try {
auto tm = open_tm(bm, superblock_detail::SUPERBLOCK_LOCATION); metadata_repair_(bm, e, opts);
if (get_dev_ids(*tm, sb.device_details_root_) && get_map_ids(*tm, sb.data_mapping_root_)) { } catch (override_error const &e) {
// The roots in the superblock look ok (perhaps the corruption was in the ostringstream out;
// space maps). out << "The following field needs to be provided on the command line due to corruption in the superblock: "
auto sb = read_superblock(bm); << e.what();
do_repair_(bm, sb, e); throw runtime_error(out.str());
} else }
metadata_repair_(bm, e);
} }
//---------------------------------------------------------------- //----------------------------------------------------------------

View File

@ -19,8 +19,9 @@
#ifndef METADATA_DUMPER_H #ifndef METADATA_DUMPER_H
#define METADATA_DUMPER_H #define METADATA_DUMPER_H
#include "emitter.h" #include "thin-provisioning/emitter.h"
#include "metadata.h" #include "thin-provisioning/metadata.h"
#include "thin-provisioning/override_emitter.h"
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <set> #include <set>
@ -46,6 +47,8 @@ namespace thin_provisioning {
} }
bool skip_mappings_; bool skip_mappings_;
override_options overrides_;
using dev_set = std::set<uint64_t>; using dev_set = std::set<uint64_t>;
using maybe_dev_set = boost::optional<dev_set>; using maybe_dev_set = boost::optional<dev_set>;
@ -58,10 +61,10 @@ namespace thin_provisioning {
// corruption encountered will cause an exception to be thrown. // corruption encountered will cause an exception to be thrown.
void metadata_dump(metadata::ptr md, emitter::ptr e, dump_options const &opts); void metadata_dump(metadata::ptr md, emitter::ptr e, dump_options const &opts);
// We have to provide a different interface for repairing, since // We have to provide a different interface for repairing, since
// the superblock itself may be corrupt, so we wont be able // the superblock itself may be corrupt, so we wont be able
// to create the metadata object. // to create the metadata object.
void metadata_repair(block_manager<>::ptr bm, emitter::ptr e); 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 // 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); void metadata_dump_subtree(metadata::ptr md, emitter::ptr e, bool repair, uint64_t subtree_root);

View File

@ -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
// <http://www.gnu.org/licenses/>.
#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<uint32_t> flags,
boost::optional<uint32_t> version,
uint32_t data_block_size,
uint64_t nr_data_blocks,
boost::optional<uint64_t> 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));
}
//----------------------------------------------------------------

View File

@ -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
// <http://www.gnu.org/licenses/>.
#ifndef TP_OVERRIDE_EMITTER_H
#define TP_OVERRIDE_EMITTER_H
#include "emitter.h"
#include "metadata.h"
#include <boost/optional.hpp>
#include <sstream>
//----------------------------------------------------------------
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<uint64_t> transaction_id_;
boost::optional<uint32_t> data_block_size_;
boost::optional<uint64_t> 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

View File

@ -16,6 +16,7 @@
// with thin-provisioning-tools. If not, see // with thin-provisioning-tools. If not, see
// <http://www.gnu.org/licenses/>. // <http://www.gnu.org/licenses/>.
#include "thin-provisioning/override_emitter.h"
#include "thin-provisioning/restore_emitter.h" #include "thin-provisioning/restore_emitter.h"
#include "thin-provisioning/superblock.h" #include "thin-provisioning/superblock.h"
@ -155,6 +156,8 @@ namespace {
} }
metadata::ptr md_; metadata::ptr md_;
override_options opts_;
bool in_superblock_; bool in_superblock_;
block_address nr_data_blocks_; block_address nr_data_blocks_;
boost::optional<uint32_t> current_device_; boost::optional<uint32_t> current_device_;

View File

@ -22,6 +22,8 @@
#include "emitter.h" #include "emitter.h"
#include "metadata.h" #include "metadata.h"
#include <boost/optional.hpp>
//---------------------------------------------------------------- //----------------------------------------------------------------
namespace thin_provisioning { namespace thin_provisioning {

View File

@ -97,6 +97,8 @@ namespace thin_provisioning {
block_address const SUPERBLOCK_LOCATION = 0; block_address const SUPERBLOCK_LOCATION = 0;
uint32_t const SUPERBLOCK_MAGIC = 27022010; uint32_t const SUPERBLOCK_MAGIC = 27022010;
uint32_t const METADATA_VERSION = 2;
//-------------------------------- //--------------------------------

View File

@ -84,33 +84,35 @@ namespace {
return e; return e;
} }
int dump_(string const &path, ostream &out, struct flags &flags) { int dump_(string const &path, ostream &out, struct flags &flags) {
try { try {
emitter::ptr e = create_emitter(flags.format, out); emitter::ptr inner = create_emitter(flags.format, out);
emitter::ptr e = create_override_emitter(inner, flags.opts.overrides_);
if (flags.repair) { if (flags.repair) {
auto bm = open_bm(path, block_manager<>::READ_ONLY, true); auto bm = open_bm(path, block_manager<>::READ_ONLY, true);
metadata_repair(bm, e); metadata_repair(bm, e, flags.opts.overrides_);
} else { } else {
metadata::ptr md = open_metadata(path, flags); metadata::ptr md = open_metadata(path, flags);
metadata_dump(md, e, flags.opts); metadata_dump(md, e, flags.opts);
} }
} catch (std::exception &e) { } catch (std::exception &e) {
cerr << e.what() << endl; cerr << e.what() << endl;
return 1; 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; flags.opts.skip_mappings_ = true;
break; break;
case 3:
flags.opts.overrides_.transaction_id_ = parse_uint64(optarg, "transaction id");
break;
case 4:
flags.opts.overrides_.data_block_size_ = static_cast<uint32_t>(parse_uint64(optarg, "data block size"));
break;
case 5:
flags.opts.overrides_.nr_data_blocks_ = parse_uint64(optarg, "nr data blocks");
break;
case 'V': case 'V':
cout << THIN_PROVISIONING_TOOLS_VERSION << endl; cout << THIN_PROVISIONING_TOOLS_VERSION << endl;
return 0; return 0;

View File

@ -17,27 +17,28 @@ using namespace std;
using namespace thin_provisioning; using namespace thin_provisioning;
namespace { namespace {
int repair(string const &old_path, string const &new_path) { int repair(string const &old_path, string const &new_path, override_options const &opts) {
bool metadata_touched = false; bool metadata_touched = false;
try { try {
// block size gets updated by the restorer // block size gets updated by the restorer
block_manager<>::ptr new_bm = open_bm(new_path, block_manager<>::READ_WRITE); block_manager<>::ptr new_bm = open_bm(new_path, block_manager<>::READ_WRITE);
file_utils::check_file_exists(old_path, false); file_utils::check_file_exists(old_path, false);
metadata_touched = true; metadata_touched = true;
metadata::ptr new_md(new metadata(new_bm, metadata::CREATE, 128, 0)); metadata::ptr new_md(new metadata(new_bm, metadata::CREATE, 128, 0));
emitter::ptr e = create_restore_emitter(new_md); emitter::ptr inner = create_restore_emitter(new_md);
block_manager<>::ptr old_bm = open_bm(old_path, block_manager<>::READ_ONLY); emitter::ptr e = create_override_emitter(inner, opts);
metadata_repair(old_bm, e); block_manager<>::ptr old_bm = open_bm(old_path, block_manager<>::READ_ONLY);
metadata_repair(old_bm, e, opts);
} catch (std::exception &e) { } catch (std::exception &e) {
if (metadata_touched) if (metadata_touched)
file_utils::zero_superblock(new_path); file_utils::zero_superblock(new_path);
cerr << e.what() << endl; cerr << e.what() << endl;
return 1; return 1;
} }
return 0; return 0;
} }
} }
//---------------------------------------------------------------- //----------------------------------------------------------------
@ -50,12 +51,15 @@ thin_repair_cmd::thin_repair_cmd()
void void
thin_repair_cmd::usage(std::ostream &out) const thin_repair_cmd::usage(std::ostream &out) const
{ {
out << "Usage: " << get_name() << " [options] {device|file}" << endl out << "Usage: " << get_name() << " [options] {device|file}" << endl
<< "Options:" << endl << "Options:" << endl
<< " {-h|--help}" << endl << " {-h|--help}" << endl
<< " {-i|--input} <input metadata (binary format)>" << endl << " {-i|--input} <input metadata (binary format)>" << endl
<< " {-o|--output} <output metadata (binary format)>" << endl << " {-o|--output} <output metadata (binary format)>" << endl
<< " {-V|--version}" << endl; << " {--transaction-id} <natural>" << endl
<< " {--data-block-size} <natural>" << endl
<< " {--nr-data-blocks} <natural>" << endl
<< " {-V|--version}" << endl;
} }
int int
@ -63,39 +67,56 @@ thin_repair_cmd::run(int argc, char **argv)
{ {
int c; int c;
boost::optional<string> input_path, output_path; boost::optional<string> input_path, output_path;
override_options opts;
const char shortopts[] = "hi:o:V"; const char shortopts[] = "hi:o:V";
const struct option longopts[] = { const struct option longopts[] = {
{ "help", no_argument, NULL, 'h'}, { "help", no_argument, NULL, 'h'},
{ "input", required_argument, NULL, 'i'}, { "input", required_argument, NULL, 'i'},
{ "output", required_argument, NULL, 'o'}, { "output", required_argument, NULL, 'o'},
{ "version", no_argument, NULL, 'V'}, { "transaction-id", required_argument, NULL, 1},
{ NULL, no_argument, NULL, 0 } { "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) { while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
switch(c) { switch(c) {
case 'h': case 'h':
usage(cout); usage(cout);
return 0; return 0;
case 'i': case 'i':
input_path = optarg; input_path = optarg;
break; break;
case 'o': case 'o':
output_path = optarg; output_path = optarg;
break; break;
case 'V': case 1:
cout << THIN_PROVISIONING_TOOLS_VERSION << endl; opts.transaction_id_ = parse_uint64(optarg, "transaction id");
return 0; break;
default: case 2:
usage(cerr); opts.data_block_size_ = static_cast<uint32_t>(parse_uint64(optarg, "data block size"));
return 1; 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) { if (!input_path) {
cerr << "no input file provided" << endl; cerr << "no input file provided" << endl;
@ -112,7 +133,8 @@ thin_repair_cmd::run(int argc, char **argv)
return 1; return 1;
} }
return repair(*input_path, *output_path); return repair(*input_path, *output_path, opts);
} }
//---------------------------------------------------------------- //----------------------------------------------------------------

View File

@ -23,6 +23,7 @@
#include "thin-provisioning/emitter.h" #include "thin-provisioning/emitter.h"
#include "thin-provisioning/human_readable_format.h" #include "thin-provisioning/human_readable_format.h"
#include "thin-provisioning/metadata.h" #include "thin-provisioning/metadata.h"
#include "thin-provisioning/override_emitter.h"
#include "thin-provisioning/restore_emitter.h" #include "thin-provisioning/restore_emitter.h"
#include "thin-provisioning/xml_format.h" #include "thin-provisioning/xml_format.h"
#include "version.h" #include "version.h"
@ -44,27 +45,28 @@ using namespace thin_provisioning;
//---------------------------------------------------------------- //----------------------------------------------------------------
namespace { namespace {
int restore(string const &backup_file, string const &dev, bool quiet) { int restore(string const &backup_file, string const &dev, bool quiet, override_options const &opts) {
bool metadata_touched = false; bool metadata_touched = false;
try { try {
// The block size gets updated by the restorer. // The block size gets updated by the restorer.
block_manager<>::ptr bm(open_bm(dev, block_manager<>::READ_WRITE)); block_manager<>::ptr bm(open_bm(dev, block_manager<>::READ_WRITE));
file_utils::check_file_exists(backup_file); file_utils::check_file_exists(backup_file);
metadata_touched = true; metadata_touched = true;
metadata::ptr md(new metadata(bm, metadata::CREATE, 128, 0)); metadata::ptr md(new metadata(bm, metadata::CREATE, 128, 0));
emitter::ptr restorer = create_restore_emitter(md); 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) { } catch (std::exception &e) {
if (metadata_touched) if (metadata_touched)
file_utils::zero_superblock(dev); file_utils::zero_superblock(dev);
cerr << e.what() << endl; cerr << e.what() << endl;
return 1; return 1;
} }
return 0; return 0;
} }
} }
//---------------------------------------------------------------- //----------------------------------------------------------------
@ -77,78 +79,99 @@ thin_restore_cmd::thin_restore_cmd()
void void
thin_restore_cmd::usage(std::ostream &out) const thin_restore_cmd::usage(std::ostream &out) const
{ {
out << "Usage: " << get_name() << " [options]" << endl out << "Usage: " << get_name() << " [options]" << endl
<< "Options:" << endl << "Options:" << endl
<< " {-h|--help}" << endl << " {-h|--help}" << endl
<< " {-i|--input} <input xml file>" << endl << " {-i|--input} <input xml file>" << endl
<< " {-o|--output} <output device or file>" << endl << " {-o|--output} <output device or file>" << endl
<< " {-q|--quiet}" << endl << " {--transaction-id} <natural>" << endl
<< " {-V|--version}" << endl; << " {--data-block-size} <natural>" << endl
<< " {--nr-data-blocks} <natural>" << endl
<< " {-q|--quiet}" << endl
<< " {-V|--version}" << endl;
} }
int int
thin_restore_cmd::run(int argc, char **argv) thin_restore_cmd::run(int argc, char **argv)
{ {
int c; int c;
const char *shortopts = "hi:o:qV"; const char *shortopts = "hi:o:qV";
string input, output; string input, output;
bool quiet = false; bool quiet = false;
const struct option longopts[] = { override_options opts;
{ "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 }
};
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { const struct option longopts[] = {
switch(c) { { "help", no_argument, NULL, 'h'},
case 'h': { "input", required_argument, NULL, 'i' },
usage(cout); { "output", required_argument, NULL, 'o'},
return 0; { "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': while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
input = optarg; switch(c) {
break; case 'h':
usage(cout);
return 0;
case 'o': case 'i':
output = optarg; input = optarg;
break; break;
case 'q': case 'o':
quiet = true; output = optarg;
break; break;
case 'V': case 1:
cout << THIN_PROVISIONING_TOOLS_VERSION << endl; opts.transaction_id_ = parse_uint64(optarg, "transaction id");
return 0; break;
default: case 2:
usage(cerr); opts.data_block_size_ = static_cast<uint32_t>(parse_uint64(optarg, "data block size"));
return 1; break;
}
}
if (argc != optind) { case 3:
usage(cerr); opts.nr_data_blocks_ = parse_uint64(optarg, "nr data blocks");
return 1; 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()) { if (input.empty()) {
cerr << "No input file provided." << endl << endl; cerr << "No input file provided." << endl << endl;
usage(cerr); usage(cerr);
return 1; return 1;
} }
if (output.empty()) { if (output.empty()) {
cerr << "No output file provided." << endl << endl; cerr << "No output file provided." << endl << endl;
usage(cerr); usage(cerr);
return 1; return 1;
} else } else
check_output_file_requirements(output); check_output_file_requirements(output);
return restore(input, output, quiet); return restore(input, output, quiet, opts);
} }
//---------------------------------------------------------------- //----------------------------------------------------------------

View File

@ -40,7 +40,7 @@
#include "thin-provisioning/superblock.h" #include "thin-provisioning/superblock.h"
#include "thin-provisioning/variable_chunk_stream.h" #include "thin-provisioning/variable_chunk_stream.h"
#include <boost/uuid/sha1.hpp> #include <boost/compute/detail/sha1.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <deque> #include <deque>