Corrupt superblock repair
This commit is contained in:
parent
0fc7529c01
commit
91b4714e38
@ -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 \
|
||||
|
@ -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)))))
|
||||
)
|
||||
|
@ -33,7 +33,6 @@
|
||||
using namespace base;
|
||||
using namespace thin_provisioning;
|
||||
|
||||
#define METADATA_VERSION 2
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "thin-provisioning/mapping_tree.h"
|
||||
#include "thin-provisioning/metadata_dumper.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
@ -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<roots>
|
||||
|
||||
optional<pair<block_address, block_address>>
|
||||
find_best_roots(transaction_manager &tm) {
|
||||
vector<node_info> mapping_roots;
|
||||
vector<node_info> 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<pair<block_address, block_address>>();
|
||||
}
|
||||
if (pairs.size())
|
||||
return mk_roots(pairs[0]);
|
||||
else
|
||||
return optional<roots>();
|
||||
}
|
||||
|
||||
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) {
|
||||
for (auto v : lhs)
|
||||
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_;
|
||||
vector<bool> referenced_;
|
||||
vector<bool> 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<block_address>());
|
||||
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<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
|
||||
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<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
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
@ -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 <boost/optional.hpp>
|
||||
#include <set>
|
||||
@ -46,6 +47,8 @@ namespace thin_provisioning {
|
||||
}
|
||||
|
||||
bool skip_mappings_;
|
||||
override_options overrides_;
|
||||
|
||||
|
||||
using dev_set = std::set<uint64_t>;
|
||||
using maybe_dev_set = boost::optional<dev_set>;
|
||||
@ -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);
|
||||
|
95
thin-provisioning/override_emitter.cc
Normal file
95
thin-provisioning/override_emitter.cc
Normal 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));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
85
thin-provisioning/override_emitter.h
Normal file
85
thin-provisioning/override_emitter.h
Normal 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
|
@ -16,6 +16,7 @@
|
||||
// with thin-provisioning-tools. If not, see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
#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<uint32_t> current_device_;
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include "emitter.h"
|
||||
#include "metadata.h"
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace thin_provisioning {
|
||||
|
@ -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;
|
||||
|
||||
|
||||
//--------------------------------
|
||||
|
||||
|
@ -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<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':
|
||||
cout << THIN_PROVISIONING_TOOLS_VERSION << endl;
|
||||
return 0;
|
||||
|
@ -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} <input metadata (binary format)>" << endl
|
||||
<< " {-o|--output} <output metadata (binary format)>" << endl
|
||||
<< " {-V|--version}" << endl;
|
||||
out << "Usage: " << get_name() << " [options] {device|file}" << endl
|
||||
<< "Options:" << endl
|
||||
<< " {-h|--help}" << endl
|
||||
<< " {-i|--input} <input metadata (binary format)>" << endl
|
||||
<< " {-o|--output} <output metadata (binary format)>" << endl
|
||||
<< " {--transaction-id} <natural>" << endl
|
||||
<< " {--data-block-size} <natural>" << endl
|
||||
<< " {--nr-data-blocks} <natural>" << endl
|
||||
<< " {-V|--version}" << endl;
|
||||
}
|
||||
|
||||
int
|
||||
@ -63,39 +67,56 @@ thin_repair_cmd::run(int argc, char **argv)
|
||||
{
|
||||
int c;
|
||||
boost::optional<string> 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<uint32_t>(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);
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
@ -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} <input xml file>" << endl
|
||||
<< " {-o|--output} <output device or file>" << endl
|
||||
<< " {-q|--quiet}" << endl
|
||||
<< " {-V|--version}" << endl;
|
||||
out << "Usage: " << get_name() << " [options]" << endl
|
||||
<< "Options:" << endl
|
||||
<< " {-h|--help}" << endl
|
||||
<< " {-i|--input} <input xml file>" << endl
|
||||
<< " {-o|--output} <output device or file>" << endl
|
||||
<< " {--transaction-id} <natural>" << endl
|
||||
<< " {--data-block-size} <natural>" << endl
|
||||
<< " {--nr-data-blocks} <natural>" << 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<uint32_t>(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);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
|
@ -40,7 +40,7 @@
|
||||
#include "thin-provisioning/superblock.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/optional.hpp>
|
||||
#include <deque>
|
||||
|
Loading…
Reference in New Issue
Block a user