Merge branch 'master' into held-root

This commit is contained in:
Joe Thornber 2012-05-15 10:02:49 +01:00
commit ae220c977d
27 changed files with 876 additions and 311 deletions

View File

@ -19,7 +19,8 @@
.PHONY: all .PHONY: all
PROGRAMS=\ PROGRAMS=\
thin_repair \ thin_debug \
thin_check \
thin_dump \ thin_dump \
thin_restore thin_restore
@ -45,15 +46,15 @@ SOURCE=\
xml_format.cc xml_format.cc
PROGRAM_SOURCE=\ PROGRAM_SOURCE=\
thin_check.cc \
thin_dump.cc \ thin_dump.cc \
thin_repair.cc \
thin_restore.cc thin_restore.cc
CXX:=@CXX@ CXX:=@CXX@
OBJECTS:=$(subst .cc,.o,$(SOURCE)) OBJECTS:=$(subst .cc,.o,$(SOURCE))
TOP_DIR:=@top_srcdir@ TOP_DIR:=@top_srcdir@
TOP_BUILDDIR:=@top_builddir@ TOP_BUILDDIR:=@top_builddir@
CXXFLAGS+=-Wall CXXFLAGS+=-Wall -fno-strict-aliasing
CXXFLAGS+=@CXXOPTIMISE_FLAG@ CXXFLAGS+=@CXXOPTIMISE_FLAG@
CXXFLAGS+=@CXXDEBUG_FLAG@ CXXFLAGS+=@CXXDEBUG_FLAG@
INCLUDES+=-I$(TOP_BUILDDIR) -I$(TOP_DIR) INCLUDES+=-I$(TOP_BUILDDIR) -I$(TOP_DIR)
@ -61,8 +62,8 @@ LIBS:=-lstdc++
LIBEXPAT:=-lexpat LIBEXPAT:=-lexpat
INSTALL:=@INSTALL@ INSTALL:=@INSTALL@
STRIP= STRIP=
DESTDIR:=@prefix@ PREFIX:=@prefix@
BINDIR:=$(DESTDIR)/sbin BINDIR:=$(DESTDIR)$(PREFIX)/sbin
MANPATH:=$(DESTDIR)$(MANDIR) MANPATH:=$(DESTDIR)$(MANDIR)
vpath %.cc $(TOP_DIR) vpath %.cc $(TOP_DIR)
@ -85,9 +86,10 @@ test-programs: $(TEST_PROGRAMS)
%.o: %.cc %.o: %.cc
$(CXX) -c $(INCLUDES) $(CXXFLAGS) -o $@ $< $(CXX) -c $(INCLUDES) $(CXXFLAGS) -o $@ $<
THIN_DEBUG_SOURCE=$(SOURCE)
THIN_DUMP_SOURCE=$(SOURCE) THIN_DUMP_SOURCE=$(SOURCE)
THIN_RESTORE_SOURCE=$(SOURCE) THIN_RESTORE_SOURCE=$(SOURCE)
THIN_REPAIR_SOURCE=\ THIN_CHECK_SOURCE=\
checksum.cc \ checksum.cc \
endian_utils.cc \ endian_utils.cc \
error_set.cc \ error_set.cc \
@ -101,9 +103,13 @@ THIN_REPAIR_SOURCE=\
space_map_transactional.cc \ space_map_transactional.cc \
transaction_manager.cc transaction_manager.cc
THIN_DEBUG_OBJECTS=$(subst .cc,.o,$(THIN_DEBUG_SOURCE))
THIN_DUMP_OBJECTS=$(subst .cc,.o,$(THIN_DUMP_SOURCE)) THIN_DUMP_OBJECTS=$(subst .cc,.o,$(THIN_DUMP_SOURCE))
THIN_RESTORE_OBJECTS=$(subst .cc,.o,$(THIN_RESTORE_SOURCE)) THIN_RESTORE_OBJECTS=$(subst .cc,.o,$(THIN_RESTORE_SOURCE))
THIN_REPAIR_OBJECTS=$(subst .cc,.o,$(THIN_REPAIR_SOURCE)) THIN_CHECK_OBJECTS=$(subst .cc,.o,$(THIN_CHECK_SOURCE))
thin_debug: $(THIN_DEBUG_OBJECTS) thin_debug.o
$(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT)
thin_dump: $(THIN_DUMP_OBJECTS) thin_dump.o thin_dump: $(THIN_DUMP_OBJECTS) thin_dump.o
$(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) $(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT)
@ -111,7 +117,7 @@ thin_dump: $(THIN_DUMP_OBJECTS) thin_dump.o
thin_restore: $(THIN_RESTORE_OBJECTS) thin_restore.o thin_restore: $(THIN_RESTORE_OBJECTS) thin_restore.o
$(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT) $(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS) $(LIBEXPAT)
thin_repair: $(THIN_REPAIR_OBJECTS) thin_repair.o thin_check: $(THIN_CHECK_OBJECTS) thin_check.o
$(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS) $(CXX) $(CXXFLAGS) -o $@ $+ $(LIBS)
clean: clean:
@ -122,11 +128,11 @@ distclean: clean
install: $(PROGRAMS) install: $(PROGRAMS)
$(INSTALL_DIR) $(BINDIR) $(INSTALL_DIR) $(BINDIR)
$(INSTALL_PROGRAM) thin_repair $(BINDIR)/thin_repair $(INSTALL_PROGRAM) thin_check $(BINDIR)/thin_check
$(INSTALL_PROGRAM) thin_dump $(BINDIR)/thin_dump $(INSTALL_PROGRAM) thin_dump $(BINDIR)/thin_dump
$(INSTALL_PROGRAM) thin_restore $(BINDIR)/thin_restore $(INSTALL_PROGRAM) thin_restore $(BINDIR)/thin_restore
$(INSTALL_DIR) $(MANPATH)/man8 $(INSTALL_DIR) $(MANPATH)/man8
$(INSTALL_DATA) man8/thin_repair.8 $(MANPATH)/man8/thin_repair.8 $(INSTALL_DATA) man8/thin_check.8 $(MANPATH)/man8/thin_check.8
$(INSTALL_DATA) man8/thin_dump.8 $(MANPATH)/man8/thin_dump.8 $(INSTALL_DATA) man8/thin_dump.8 $(MANPATH)/man8/thin_dump.8
$(INSTALL_DATA) man8/thin_restore.8 $(MANPATH)/man8/thin_restore.8 $(INSTALL_DATA) man8/thin_restore.8 $(MANPATH)/man8/thin_restore.8
.PHONY: install .PHONY: install

View File

@ -1 +1 @@
0.0.1-github(0) (2011-12-06) 0.1.4+

View File

@ -18,11 +18,13 @@
#include "btree.h" #include "btree.h"
#include "errors.h"
#include "checksum.h" #include "checksum.h"
#include "transaction_manager.h" #include "transaction_manager.h"
#include <iostream> #include <iostream>
using namespace base;
using namespace btree_detail; using namespace btree_detail;
using namespace persistent_data; using namespace persistent_data;
using namespace std; using namespace std;
@ -37,10 +39,10 @@ namespace {
crc32c sum(BTREE_CSUM_XOR); crc32c sum(BTREE_CSUM_XOR);
sum.append(&n->flags, MD_BLOCK_SIZE - sizeof(uint32_t)); sum.append(&n->flags, MD_BLOCK_SIZE - sizeof(uint32_t));
if (sum.get_sum() != to_cpu<uint32_t>(n->csum)) if (sum.get_sum() != to_cpu<uint32_t>(n->csum))
throw runtime_error("bad checksum in btree node"); throw checksum_error("bad checksum in btree node");
if (to_cpu<uint64_t>(n->blocknr) != location) if (to_cpu<uint64_t>(n->blocknr) != location)
throw runtime_error("bad block nr in btree node"); throw checksum_error("bad block nr in btree node");
} }
virtual void prepare(block_manager<>::buffer &b, block_address location) const { virtual void prepare(block_manager<>::buffer &b, block_address location) const {

View File

@ -51,81 +51,39 @@ namespace persistent_data {
// Not implemented // Not implemented
// --------------- // ---------------
// //
// - checksum
// - leaf | internal flags (this can be inferred from siblings) // - leaf | internal flags (this can be inferred from siblings)
//---------------------------------------------------------------- //----------------------------------------------------------------
template <uint32_t Levels, typename ValueTraits> template <uint32_t Levels, typename ValueTraits>
class btree_checker : public btree<Levels, ValueTraits>::visitor { class btree_checker : public btree<Levels, ValueTraits>::visitor {
public: public:
btree_checker(block_counter &counter) btree_checker(block_counter &counter, bool avoid_repeated_visits = true)
: counter_(counter), : counter_(counter),
errs_(new error_set("btree errors")) { errs_(new error_set("btree errors")),
avoid_repeated_visits_(avoid_repeated_visits) {
} }
bool visit_internal(unsigned level, bool visit_internal(unsigned level,
bool sub_root, bool sub_root,
optional<uint64_t> key, optional<uint64_t> key,
btree_detail::node_ref<uint64_traits> const &n) { btree_detail::node_ref<uint64_traits> const &n) {
if (already_visited(n)) return check_internal(level, sub_root, key, n);
return false;
check_sum(n);
if (sub_root)
new_root(level);
check_block_nr(n);
check_max_entries(n);
check_nr_entries(n, sub_root);
check_ordered_keys(n);
check_parent_key(sub_root ? optional<uint64_t>() : key, n);
return true;
} }
bool visit_internal_leaf(unsigned level, bool visit_internal_leaf(unsigned level,
bool sub_root, bool sub_root,
optional<uint64_t> key, optional<uint64_t> key,
btree_detail::node_ref<uint64_traits> const &n) { btree_detail::node_ref<uint64_traits> const &n) {
if (already_visited(n)) return check_leaf(level, sub_root, key, n);
return false;
check_sum(n);
if (sub_root)
new_root(level);
check_block_nr(n);
check_max_entries(n);
check_nr_entries(n, sub_root);
check_ordered_keys(n);
check_parent_key(sub_root ? optional<uint64_t>() : key, n);
check_leaf_key(level, n);
return true;
} }
bool visit_leaf(unsigned level, bool visit_leaf(unsigned level,
bool sub_root, bool sub_root,
optional<uint64_t> key, optional<uint64_t> key,
btree_detail::node_ref<ValueTraits> const &n) { btree_detail::node_ref<ValueTraits> const &n) {
if (already_visited(n)) return check_leaf(level, sub_root, key, n);
return false;
check_sum(n);
if (sub_root)
new_root(level);
check_block_nr(n);
check_max_entries(n);
check_nr_entries(n, sub_root);
check_ordered_keys(n);
check_parent_key(sub_root ? optional<uint64_t>() : key, n);
check_leaf_key(level, n);
return true;
} }
boost::optional<error_set::ptr> get_errors() const { error_set::ptr get_errors() const {
return errs_; return errs_;
} }
@ -135,21 +93,66 @@ namespace persistent_data {
} }
private: private:
bool check_internal(unsigned level,
bool sub_root,
optional<uint64_t> key,
btree_detail::node_ref<uint64_traits> const &n) {
if (!already_visited(n) &&
check_sum(n) &&
check_block_nr(n) &&
check_max_entries(n) &&
check_nr_entries(n, sub_root) &&
check_ordered_keys(n) &&
check_parent_key(sub_root ? optional<uint64_t>() : key, n)) {
if (sub_root)
new_root(level);
return true;
}
return false;
}
template <typename ValueTraits2>
bool check_leaf(unsigned level,
bool sub_root,
optional<uint64_t> key,
btree_detail::node_ref<ValueTraits2> const &n) {
if (!already_visited(n) &&
check_sum(n) &&
check_block_nr(n) &&
check_max_entries(n) &&
check_nr_entries(n, sub_root) &&
check_ordered_keys(n) &&
check_parent_key(sub_root ? optional<uint64_t>() : key, n)) {
if (sub_root)
new_root(level);
return check_leaf_key(level, n);
}
return false;
}
template <typename node> template <typename node>
bool already_visited(node const &n) { bool already_visited(node const &n) {
block_address b = n.get_location(); block_address b = n.get_location();
counter_.inc(b); counter_.inc(b);
if (seen_.count(b) > 0) if (avoid_repeated_visits_) {
return true; if (seen_.count(b) > 0)
return true;
seen_.insert(b);
}
seen_.insert(b);
return false; return false;
} }
template <typename node> template <typename node>
void check_sum(node const &n) const { bool check_sum(node const &n) const {
crc32c sum(BTREE_CSUM_XOR); crc32c sum(BTREE_CSUM_XOR);
disk_node const *data = n.raw(); disk_node const *data = n.raw();
@ -160,48 +163,55 @@ namespace persistent_data {
<< ", sum was " << sum.get_sum() << ", sum was " << sum.get_sum()
<< ", on disk " << n.get_checksum(); << ", on disk " << n.get_checksum();
errs_->add_child(out.str()); errs_->add_child(out.str());
throw runtime_error(out.str()); return false;
} }
return true;
} }
template <typename node> template <typename node>
void check_block_nr(node const &n) const { bool check_block_nr(node const &n) const {
if (n.get_location() != n.get_block_nr()) { if (n.get_location() != n.get_block_nr()) {
std::ostringstream out; std::ostringstream out;
out << "block number mismatch: actually " out << "block number mismatch: actually "
<< n.get_location() << n.get_location()
<< ", claims " << n.get_block_nr(); << ", claims " << n.get_block_nr();
errs_->add_child(out.str()); errs_->add_child(out.str());
throw runtime_error(out.str()); return false;
} }
return true;
} }
template <typename node> template <typename node>
void check_max_entries(node const &n) const { bool check_max_entries(node const &n) const {
size_t elt_size = sizeof(uint64_t) + n.get_value_size(); size_t elt_size = sizeof(uint64_t) + n.get_value_size();
if (elt_size * n.get_max_entries() + sizeof(node_header) > MD_BLOCK_SIZE) { if (elt_size * n.get_max_entries() + sizeof(node_header) > MD_BLOCK_SIZE) {
std::ostringstream out; std::ostringstream out;
out << "max entries too large: " << n.get_max_entries(); out << "max entries too large: " << n.get_max_entries();
errs_->add_child(out.str()); errs_->add_child(out.str());
return false;
} }
if (n.get_max_entries() % 3) { if (n.get_max_entries() % 3) {
std::ostringstream out; std::ostringstream out;
out << "max entries is not divisible by 3: " << n.get_max_entries(); out << "max entries is not divisible by 3: " << n.get_max_entries();
errs_->add_child(out.str()); errs_->add_child(out.str());
throw runtime_error(out.str()); return false;
} }
return true;
} }
template <typename node> template <typename node>
void check_nr_entries(node const &n, bool is_root) const { bool check_nr_entries(node const &n, bool is_root) const {
if (n.get_nr_entries() > n.get_max_entries()) { if (n.get_nr_entries() > n.get_max_entries()) {
std::ostringstream out; std::ostringstream out;
out << "bad nr_entries: " out << "bad nr_entries: "
<< n.get_nr_entries() << " < " << n.get_nr_entries() << " < "
<< n.get_max_entries(); << n.get_max_entries();
errs_->add_child(out.str()); errs_->add_child(out.str());
throw std::runtime_error(out.str()); return false;
} }
block_address min = n.get_max_entries() / 3; block_address min = n.get_max_entries() / 3;
@ -213,16 +223,18 @@ namespace persistent_data {
<< min << min
<< "(max_entries = " << n.get_max_entries() << ")"; << "(max_entries = " << n.get_max_entries() << ")";
errs_->add_child(out.str()); errs_->add_child(out.str());
throw runtime_error(out.str()); return false;
} }
return true;
} }
template <typename node> template <typename node>
void check_ordered_keys(node const &n) const { bool check_ordered_keys(node const &n) const {
unsigned nr_entries = n.get_nr_entries(); unsigned nr_entries = n.get_nr_entries();
if (nr_entries == 0) if (nr_entries == 0)
return; // can only happen if a root node return true; // can only happen if a root node
uint64_t last_key = n.key_at(0); uint64_t last_key = n.key_at(0);
@ -231,50 +243,59 @@ namespace persistent_data {
if (k <= last_key) { if (k <= last_key) {
ostringstream out; ostringstream out;
out << "keys are out of order, " << k << " <= " << last_key; out << "keys are out of order, " << k << " <= " << last_key;
throw runtime_error(out.str()); errs_->add_child(out.str());
return false;
} }
last_key = k; last_key = k;
} }
return true;
} }
template <typename node> template <typename node>
void check_parent_key(boost::optional<uint64_t> key, node const &n) const { bool check_parent_key(boost::optional<uint64_t> key, node const &n) const {
if (!key) if (!key)
return; return true;
if (*key > n.key_at(0)) { if (*key > n.key_at(0)) {
ostringstream out; ostringstream out;
out << "parent key mismatch: parent was " << *key out << "parent key mismatch: parent was " << *key
<< ", but lowest in node was " << n.key_at(0); << ", but lowest in node was " << n.key_at(0);
throw runtime_error(out.str()); errs_->add_child(out.str());
return false;
} }
return true;
} }
template <typename node> template <typename node>
void check_leaf_key(unsigned level, node const &n) { bool check_leaf_key(unsigned level, node const &n) {
if (n.get_nr_entries() == 0) if (n.get_nr_entries() == 0)
return; // can only happen if a root node return true; // can only happen if a root node
if (last_leaf_key_[level] && *last_leaf_key_[level] >= n.key_at(0)) { if (last_leaf_key_[level] && *last_leaf_key_[level] >= n.key_at(0)) {
ostringstream out; ostringstream out;
out << "the last key of the previous leaf was " << *last_leaf_key_[level] out << "the last key of the previous leaf was " << *last_leaf_key_[level]
<< " and the first key of this leaf is " << n.key_at(0); << " and the first key of this leaf is " << n.key_at(0);
throw runtime_error(out.str()); errs_->add_child(out.str());
return false;
} }
last_leaf_key_[level] = n.key_at(n.get_nr_entries() - 1); last_leaf_key_[level] = n.key_at(n.get_nr_entries() - 1);
return true;
} }
void new_root(unsigned level) { void new_root(unsigned level) {
// we're starting a new subtree, so should // we're starting a new subtree, so should
// reset the last_leaf value. // reset the last_leaf value.
last_leaf_key_[level] = boost::optional<uint64_t>(); last_leaf_key_[level] = boost::optional<uint64_t>();
} }
block_counter &counter_; block_counter &counter_;
std::set<block_address> seen_; std::set<block_address> seen_;
error_set::ptr errs_; error_set::ptr errs_;
boost::optional<uint64_t> last_leaf_key_[Levels]; boost::optional<uint64_t> last_leaf_key_[Levels];
bool avoid_repeated_visits_;
}; };
} }

View File

@ -47,7 +47,8 @@ namespace thin_provisioning {
virtual void begin_superblock(std::string const &uuid, virtual void begin_superblock(std::string const &uuid,
uint64_t time, uint64_t time,
uint64_t trans_id, uint64_t trans_id,
uint32_t data_block_size) = 0; uint32_t data_block_size,
uint64_t nr_data_blocks) = 0;
virtual void end_superblock() = 0; virtual void end_superblock() = 0;
virtual void begin_device(uint32_t dev_id, virtual void begin_device(uint32_t dev_id,

View File

@ -19,6 +19,7 @@
#ifndef ENDIAN_H #ifndef ENDIAN_H
#define ENDIAN_H #define ENDIAN_H
#include <endian.h>
#include <stdint.h> #include <stdint.h>
#include <boost/static_assert.hpp> #include <boost/static_assert.hpp>
@ -69,32 +70,32 @@ namespace base {
template <> template <>
inline uint16_t to_cpu<uint16_t, __le16>(__le16 const &d) { inline uint16_t to_cpu<uint16_t, __le16>(__le16 const &d) {
return d.v_; return le16toh(d.v_);
} }
template <> template <>
inline __le16 to_disk<__le16, uint16_t>(uint16_t const &v) { inline __le16 to_disk<__le16, uint16_t>(uint16_t const &v) {
return __le16(v); return __le16(htole16(v));
} }
template <> template <>
inline uint32_t to_cpu<uint32_t, __le32>(__le32 const &d) { inline uint32_t to_cpu<uint32_t, __le32>(__le32 const &d) {
return d.v_; return le32toh(d.v_);
} }
template <> template <>
inline __le32 to_disk<__le32, uint32_t>(uint32_t const &v) { inline __le32 to_disk<__le32, uint32_t>(uint32_t const &v) {
return __le32(v); return __le32(htole32(v));
} }
template <> template <>
inline uint64_t to_cpu<uint64_t, __le64>(__le64 const &d) { inline uint64_t to_cpu<uint64_t, __le64>(__le64 const &d) {
return d.v_; return le64toh(d.v_);
} }
template <> template <>
inline __le64 to_disk<__le64, uint64_t>(uint64_t const &v) { inline __le64 to_disk<__le64, uint64_t>(uint64_t const &v) {
return __le64(v); return __le64(htole64(v));
} }
//-------------------------------- //--------------------------------

View File

@ -61,6 +61,12 @@ error_set::add_child(string const &err)
add_child(e); add_child(e);
} }
bool
error_set::empty() const
{
return !children_.size();
}
//-------------------------------- //--------------------------------
namespace { namespace {

View File

@ -43,6 +43,7 @@ namespace persistent_data {
void add_child(error_set::ptr err); void add_child(error_set::ptr err);
void add_child(boost::optional<error_set::ptr> maybe_errs); void add_child(boost::optional<error_set::ptr> maybe_errs);
void add_child(std::string const &err); void add_child(std::string const &err);
bool empty() const;
private: private:
std::string err_; std::string err_;

37
errors.h Normal file
View File

@ -0,0 +1,37 @@
// Copyright (C) 2012 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 THINP_EXCEPTION_H
#define THINP_EXCEPTION_H
#include <stdexcept>
//----------------------------------------------------------------
namespace base {
class checksum_error : public std::runtime_error {
public:
explicit checksum_error(std::string const &what)
: std::runtime_error(what) {
}
};
}
//----------------------------------------------------------------
#endif

View File

@ -35,11 +35,13 @@ namespace {
void begin_superblock(string const &uuid, void begin_superblock(string const &uuid,
uint64_t time, uint64_t time,
uint64_t trans_id, uint64_t trans_id,
uint32_t data_block_size) { uint32_t data_block_size,
uint64_t nr_data_blocks) {
out_ << "begin superblock: \"" << uuid << "\"" out_ << "begin superblock: \"" << uuid << "\""
<< ", " << time << ", " << time
<< ", " << trans_id << ", " << trans_id
<< ", " << data_block_size << ", " << data_block_size
<< ", " << nr_data_blocks
<< endl; << endl;
} }

51
man8/thin_check.8 Normal file
View File

@ -0,0 +1,51 @@
.TH THIN_REPAIR 8 "Thin Provisioning Tools" "Red Hat, Inc." \" -*- nroff -*-
.SH NAME
thin_check \- repair thin provisioning metadata on device or file
.SH SYNOPSIS
.B thin_check
.RB [ options ]
.I {device|file}
.SH DESCRIPTION
.B thin_check
checks thin provisioning metadata created by
the device-mapper thin provisioning target on a
.I device
or
.I file.
See
.B thin_dump(8)
how to dump metadata to a file.
.SH OPTIONS
.IP "\fB\-q, \-\-quiet\fP"
Suppress output messages, return only exit code.
.IP "\fB\-h, \-\-help\fP"
Print help and exit.
.IP "\fB\-V, \-\-version\fP"
Output version information and exit.
.SH EXAMPLE
Analyses and repairs thin provisioning metadata on logical volume
/dev/vg/metadata:
.sp
.B thin_check /dev/vg/metadata
The device may not be actively used by the target
when repairing!
.SH DIAGNOSTICS
.B thin_check
returns an exit code of 0 for success or 1 for error.
.SH SEE ALSO
.B thin_dump(8)
.B thin_restore(8)
.SH AUTHOR
Joe Thornber <ejt@redhat.com>
.br
Heinz Mauelshagen <HeinzM@RedHat.com>

View File

@ -3,41 +3,49 @@
thin_dump \- dump thin provisioning metadata from device or file to standard output thin_dump \- dump thin provisioning metadata from device or file to standard output
.SH SYNOPSIS .SH SYNOPSIS
.B thin_dump [options] {metadata device|file} .B thin_dump
.RB [options]
.I {metadata device|file}
.SH DESCRIPTION .SH DESCRIPTION
thin_dump dumps thin provisioning metadata created by the device-mapper .B thin_dump
dumps thin provisioning metadata created by the device-mapper
thin provisioning target on a device or file to standard output for thin provisioning target on a device or file to standard output for
analysis or postprocessing in either XML or human readable format. analysis or postprocessing in either XML or human readable format.
XML formated metadata can be fed into thin_restore (see XML formated metadata can be fed into thin_restore (see
.B thin_restore(8) .BR thin_restore(8) )
) in order to put it back onto a metadata device (to process by in order to put it back onto a metadata device (to process by
the device-mapper target) or file. the device-mapper target) or file.
.B thin_dump .IP "\fB\-f, \-\-format\fP \fI{xml|human_readable}\fP".
{-i|--input} {device|file} Print output in XML or human readable format.
[{-f|--format} {xml|human_readable}]
.B thin_dump .IP "\fB\-r, \-\-repair\fP".
{-h|--help}
.B thin_dump .IP "\fB\-h, \-\-help\fP".
{-V|--version} Print help and exit.
.IP "\fB\-V, \-\-version\fP".
Output version information and exit.
.SH EXAMPLES .SH EXAMPLES
"thin_dump -f human_redable -i /dev/vg/metadata" Dumps the thin provisioning metadata on logical volume /dev/vg/metadata
dumps the thin provisioning metadata on logical volume /dev/vg/metadata to standard output in human readable format:
to standard output in human readable format. .sp
.B thin_dump -f human_redable /dev/vg/metadata
"thin_dump /dev/vg/metadata" Dumps the thin provisioning metadata on logical volume /dev/vg/metadata
dumps the thin provisioning metadata on logical volume /dev/vg/metadata to standard output in XML format:
to standard output in XML format. .sp
.B thin_dump /dev/vg/metadata
.SH DIAGNOSTICS .SH DIAGNOSTICS
thin_dump returns an exit code of 0 for success or 1 for error. .B thin_dump
returns an exit code of 0 for success or 1 for error.
.SH SEE ALSO .SH SEE ALSO
.B thin_repair(8), thin_restore(8) .B thin_check(8)
.B thin_restore(8)
.SH AUTHOR .SH AUTHOR
Joe Thornber <ejt@redhat.com> Joe Thornber <ejt@redhat.com>

View File

@ -1,38 +0,0 @@
.TH THIN_REPAIR 8 "Thin Provisioning Tools" "Red Hat, Inc." \" -*- nroff -*-
.SH NAME
thin_repair \- repair thin provisioning metadata on device or file
.SH SYNOPSIS
.B thin_repair {device|file}
.SH DESCRIPTION
thin_repair checks thin provisioning metadata created by
the device-mapper thin provisioning target on a device or file (see
.B thin_dump(8)
) on how to dump metadata to a file.
A future release will be able to fix faulty metadata.
.B thin_repair (device|file}
.B thin_repair
{-h|--help}
.B thin_dump
{-V|--version}
.SH EXAMPLE
"thin_repair /dev/vg/metadata"
analyses and repairs thin provisioning metadata on logical volume
/dev/vg/metadata. The device may not be actively used by the target
when repairing!
.SH DIAGNOSTICS
thin_repair returns an exit code of 0 for success or 1 for error.
.SH SEE ALSO
.B thin_dump(8), thin_restore(8)
.SH AUTHOR
Joe Thornber <ejt@redhat.com>
.br
Heinz Mauelshagen <HeinzM@RedHat.com>

View File

@ -3,37 +3,49 @@
thin_restore \- restore thin provisioning metadata file to device or file thin_restore \- restore thin provisioning metadata file to device or file
.SH SYNOPSIS .SH SYNOPSIS
.B thin_restore [options] file {metadata device|file} .B thin_restore
.RB [ options ]
.RB -i
.I {device|file}
.RB -o
.I {device|file}
.SH DESCRIPTION .SH DESCRIPTION
thin_restore restores thin provisioning metadata created by the .B thin_restore
respective device-mapper target dumped into an XML formated (see restores thin provisioning metadata created by the
.B thin_dump(8) respective device-mapper target dumped into an XML formatted (see
) file, which optionally can be preproccessed to another device or file. .BR thin_dump(8) )
file, which optionally can be preprocessed to another device or file.
If restored to a metadata device, the metadata can be processed If restored to a metadata device, the metadata can be processed
by the device-mapper target. by the device-mapper target.
.B thin_restore .IP "\fB\-i, \-\-input\fP \fI{device|file}\fP"
{-i|--input} {device|file} Input file or device with metadata.
{-o|--output} {device|file}
.B thin_restore .IP "\fB\-o, \-\-output\fP \fI{device|file}\fP"
-h|--help Output file or device.
.B thin_dump .IP "\fB\-h, \-\-help\fP"
{-V|--version} Print help and exit.
.IP "\fB\-V, \-\-version\fP"
Output version information and exit.
.SH EXAMPLE .SH EXAMPLE
"thin_restore -i metadata -o /dev/vg/metadata" Restores the XML formatted thin provisioning metadata on file
restores the XML formatted thin provisioning metadata on file "metadata" .B metadata
to logical volume /dev/vg/metadata for further processing by the to logical volume /dev/vg/metadata for further processing by the
respective device-mapper target. respective device-mapper target:
.sp
.B thin_restore -i metadata -o /dev/vg/metadata
.SH DIAGNOSTICS .SH DIAGNOSTICS
thin_dump returns an exit code of 0 for success or 1 for error. .B thin_dump
returns an exit code of 0 for success or 1 for error.
.SH SEE ALSO .SH SEE ALSO
.B thin_dump(8), thin_repair(8) .B thin_dump(8)
.B thin_check(8)
.SH AUTHOR .SH AUTHOR
Joe Thornber <ejt@redhat.com> Joe Thornber <ejt@redhat.com>

View File

@ -45,7 +45,7 @@ namespace {
crc32c sum(SUPERBLOCK_CSUM_SEED); crc32c sum(SUPERBLOCK_CSUM_SEED);
sum.append(&sbd->flags_, MD_BLOCK_SIZE - sizeof(uint32_t)); sum.append(&sbd->flags_, MD_BLOCK_SIZE - sizeof(uint32_t));
if (sum.get_sum() != to_cpu<uint32_t>(sbd->csum_)) if (sum.get_sum() != to_cpu<uint32_t>(sbd->csum_))
throw runtime_error("bad checksum in superblock"); throw checksum_error("bad checksum in superblock");
} }
virtual void prepare(block_manager<>::buffer &b, block_address location) const { virtual void prepare(block_manager<>::buffer &b, block_address location) const {
@ -65,8 +65,8 @@ namespace {
if (r) if (r)
throw runtime_error("Couldn't stat dev path"); throw runtime_error("Couldn't stat dev path");
if (S_ISREG(info.st_mode)) if (S_ISREG(info.st_mode) && info.st_size)
nr_blocks = div_down<block_address>(info.st_size, MD_BLOCK_SIZE); nr_blocks = div_up<block_address>(info.st_size, MD_BLOCK_SIZE);
else if (S_ISBLK(info.st_mode)) { else if (S_ISBLK(info.st_mode)) {
// To get the size of a block device we need to // To get the size of a block device we need to

View File

@ -27,26 +27,30 @@ namespace {
// devices having mappings defined, which can later be cross // devices having mappings defined, which can later be cross
// referenced with the details tree. A separate block_counter is // referenced with the details tree. A separate block_counter is
// used to later verify the data space map. // used to later verify the data space map.
class mapping_validator : public btree_checker<2, block_traits> { class mapping_validator : public btree<2, block_traits>::visitor {
public: public:
typedef boost::shared_ptr<mapping_validator> ptr; typedef boost::shared_ptr<mapping_validator> ptr;
typedef btree_checker<2, block_traits> checker;
mapping_validator(block_counter &metadata_counter, block_counter &data_counter) mapping_validator(block_counter &metadata_counter, block_counter &data_counter)
: btree_checker<2, block_traits>(metadata_counter), : checker_(metadata_counter),
data_counter_(data_counter) { data_counter_(data_counter)
{
}
bool visit_internal(unsigned level,
bool sub_root,
optional<uint64_t> key,
btree_detail::node_ref<uint64_traits> const &n) {
return checker_.visit_internal(level, sub_root, key, n);
} }
// Sharing can only occur in level 1 nodes.
// FIXME: not true once we start having held roots.
bool visit_internal_leaf(unsigned level, bool visit_internal_leaf(unsigned level,
bool sub_root, bool sub_root,
optional<uint64_t> key, optional<uint64_t> key,
btree_detail::node_ref<uint64_traits> const &n) { btree_detail::node_ref<uint64_traits> const &n) {
bool r = btree_checker<2, block_traits>::visit_internal_leaf(level, sub_root, key, n); bool r = checker_.visit_internal_leaf(level, sub_root, key, n);
if (!r && level == 0) {
throw runtime_error("unexpected sharing in level 0 of mapping tree.");
}
for (unsigned i = 0; i < n.get_nr_entries(); i++) for (unsigned i = 0; i < n.get_nr_entries(); i++)
devices_.insert(n.key_at(i)); devices_.insert(n.key_at(i));
@ -58,7 +62,7 @@ namespace {
bool sub_root, bool sub_root,
optional<uint64_t> key, optional<uint64_t> key,
btree_detail::node_ref<block_traits> const &n) { btree_detail::node_ref<block_traits> const &n) {
bool r = btree_checker<2, block_traits>::visit_leaf(level, sub_root, key, n); bool r = checker_.visit_leaf(level, sub_root, key, n);
if (r) if (r)
for (unsigned i = 0; i < n.get_nr_entries(); i++) for (unsigned i = 0; i < n.get_nr_entries(); i++)
@ -72,29 +76,46 @@ namespace {
} }
private: private:
checker checker_;
block_counter &data_counter_; block_counter &data_counter_;
set<uint64_t> devices_; set<uint64_t> devices_;
}; };
class details_validator : public btree_checker<1, device_details_traits> { class details_validator : public btree<1, device_details_traits>::visitor {
public: public:
typedef boost::shared_ptr<details_validator> ptr; typedef boost::shared_ptr<details_validator> ptr;
typedef btree_checker<1, device_details_traits> checker;
details_validator(block_counter &counter) details_validator(block_counter &counter)
: btree_checker<1, device_details_traits>(counter) { : checker_(counter) {
}
bool visit_internal(unsigned level,
bool sub_root,
optional<uint64_t> key,
btree_detail::node_ref<uint64_traits> const &n) {
return checker_.visit_internal(level, sub_root, key, n);
}
bool visit_internal_leaf(unsigned level,
bool sub_root,
optional<uint64_t> key,
btree_detail::node_ref<uint64_traits> const &n) {
return checker_.visit_internal_leaf(level, sub_root, key, n);
} }
bool visit_leaf(unsigned level, bool visit_leaf(unsigned level,
bool sub_root, bool sub_root,
optional<uint64_t> key, optional<uint64_t> key,
btree_detail::node_ref<device_details_traits> const &n) { btree_detail::node_ref<device_details_traits> const &n) {
bool r = btree_checker<1, device_details_traits>::visit_leaf(level, sub_root, key, n);
if (r) if (!checker_.visit_leaf(level, sub_root, key, n))
for (unsigned i = 0; i < n.get_nr_entries(); i++) return false;
devices_.insert(n.key_at(i));
return r; for (unsigned i = 0; i < n.get_nr_entries(); i++)
devices_.insert(n.key_at(i));
return true;
} }
set<uint64_t> const &get_devices() const { set<uint64_t> const &get_devices() const {
@ -102,6 +123,7 @@ namespace {
} }
private: private:
checker checker_;
set<uint64_t> devices_; set<uint64_t> devices_;
}; };

View File

@ -46,7 +46,7 @@ void
superblock_traits::unpack(superblock_disk const &disk, superblock &value) superblock_traits::unpack(superblock_disk const &disk, superblock &value)
{ {
value.csum_ = to_cpu<uint32_t>(disk.csum_); value.csum_ = to_cpu<uint32_t>(disk.csum_);
value.flags_ = to_cpu<uint32_t>(disk.csum_); value.flags_ = to_cpu<uint32_t>(disk.flags_);
value.blocknr_ = to_cpu<uint64_t>(disk.blocknr_); value.blocknr_ = to_cpu<uint64_t>(disk.blocknr_);
::memcpy(value.uuid_, disk.uuid_, sizeof(value.uuid_)); ::memcpy(value.uuid_, disk.uuid_, sizeof(value.uuid_));
@ -79,7 +79,7 @@ void
superblock_traits::pack(superblock const &value, superblock_disk &disk) superblock_traits::pack(superblock const &value, superblock_disk &disk)
{ {
disk.csum_ = to_disk<__le32>(value.csum_); disk.csum_ = to_disk<__le32>(value.csum_);
disk.flags_ = to_disk<__le32>(value.csum_); disk.flags_ = to_disk<__le32>(value.flags_);
disk.blocknr_ = to_disk<__le64>(value.blocknr_); disk.blocknr_ = to_disk<__le64>(value.blocknr_);
::memcpy(disk.uuid_, value.uuid_, sizeof(disk.uuid_)); ::memcpy(disk.uuid_, value.uuid_, sizeof(disk.uuid_));

View File

@ -27,28 +27,48 @@ namespace {
class mappings_extractor : public btree<2, block_traits>::visitor { class mappings_extractor : public btree<2, block_traits>::visitor {
public: public:
typedef boost::shared_ptr<mappings_extractor> ptr; typedef boost::shared_ptr<mappings_extractor> ptr;
typedef btree_checker<2, block_traits> checker;
mappings_extractor(uint64_t dev_id, emitter::ptr e, mappings_extractor(uint64_t dev_id, emitter::ptr e,
space_map::ptr md_sm, space_map::ptr data_sm) space_map::ptr md_sm, space_map::ptr data_sm)
: dev_id_(dev_id), : counter_(),
checker_(counter_),
dev_id_(dev_id),
e_(e), e_(e),
md_sm_(md_sm), md_sm_(md_sm),
data_sm_(data_sm), data_sm_(data_sm),
in_range_(false) { in_range_(false),
found_errors_(false) {
} }
bool visit_internal(unsigned level, bool sub_root, boost::optional<uint64_t> key, bool visit_internal(unsigned level, bool sub_root, boost::optional<uint64_t> key,
btree_detail::node_ref<uint64_traits> const &n) { btree_detail::node_ref<uint64_traits> const &n) {
if (!checker_.visit_internal(level, sub_root, key, n)) {
found_errors_ = true;
return false;
}
return (sub_root && key) ? (*key == dev_id_) : true; return (sub_root && key) ? (*key == dev_id_) : true;
} }
bool visit_internal_leaf(unsigned level, bool sub_root, boost::optional<uint64_t> key, bool visit_internal_leaf(unsigned level, bool sub_root, boost::optional<uint64_t> key,
btree_detail::node_ref<uint64_traits> const &n) { btree_detail::node_ref<uint64_traits> const &n) {
if (!checker_.visit_internal_leaf(level, sub_root, key, n)) {
found_errors_ = true;
return false;
}
return true; return true;
} }
bool visit_leaf(unsigned level, bool sub_root, boost::optional<uint64_t> maybe_key, bool visit_leaf(unsigned level, bool sub_root, boost::optional<uint64_t> maybe_key,
btree_detail::node_ref<block_traits> const &n) { btree_detail::node_ref<block_traits> const &n) {
if (!checker_.visit_leaf(level, sub_root, maybe_key, n)) {
found_errors_ = true;
return false;
}
for (unsigned i = 0; i < n.get_nr_entries(); i++) { for (unsigned i = 0; i < n.get_nr_entries(); i++) {
block_time bt = n.value_at(i); block_time bt = n.value_at(i);
add_mapping(n.key_at(i), bt.block_, bt.time_); add_mapping(n.key_at(i), bt.block_, bt.time_);
@ -57,11 +77,14 @@ namespace {
return true; return true;
} }
void visit_complete() { void visit_complete() {
end_mapping(); end_mapping();
} }
bool corruption() const {
return !checker_.get_errors()->empty();
}
private: private:
void start_mapping(uint64_t origin_block, uint64_t dest_block, uint32_t time) { void start_mapping(uint64_t origin_block, uint64_t dest_block, uint32_t time) {
origin_start_ = origin_block; origin_start_ = origin_block;
@ -97,6 +120,9 @@ namespace {
} }
} }
// Declaration order of counter_ and checker_ is important.
block_counter counter_;
checker checker_;
uint64_t dev_id_; uint64_t dev_id_;
emitter::ptr e_; emitter::ptr e_;
space_map::ptr md_sm_; space_map::ptr md_sm_;
@ -105,28 +131,34 @@ namespace {
bool in_range_; bool in_range_;
uint64_t origin_start_, dest_start_, len_; uint64_t origin_start_, dest_start_, len_;
uint32_t time_; uint32_t time_;
bool found_errors_;
}; };
class details_extractor : public btree<1, device_details_traits>::visitor { class details_extractor : public btree<1, device_details_traits>::visitor {
public: public:
typedef boost::shared_ptr<details_extractor> ptr; typedef boost::shared_ptr<details_extractor> ptr;
typedef btree_checker<1, device_details_traits> checker;
details_extractor() { details_extractor()
: counter_(),
checker_(counter_, false) {
} }
bool visit_internal(unsigned level, bool sub_root, boost::optional<uint64_t> key, bool visit_internal(unsigned level, bool sub_root, boost::optional<uint64_t> key,
btree_detail::node_ref<uint64_traits> const &n) { btree_detail::node_ref<uint64_traits> const &n) {
return true; return checker_.visit_internal(level, sub_root, key, n);
} }
bool visit_internal_leaf(unsigned level, bool sub_root, boost::optional<uint64_t> key, bool visit_internal_leaf(unsigned level, bool sub_root, boost::optional<uint64_t> key,
btree_detail::node_ref<uint64_traits> const &n) { btree_detail::node_ref<uint64_traits> const &n) {
return true; return checker_.visit_internal_leaf(level, sub_root, key, n);
} }
bool visit_leaf(unsigned level, bool sub_root, boost::optional<uint64_t> maybe_key, bool visit_leaf(unsigned level, bool sub_root, boost::optional<uint64_t> maybe_key,
btree_detail::node_ref<device_details_traits> const &n) { btree_detail::node_ref<device_details_traits> const &n) {
if (!checker_.visit_leaf(level, sub_root, maybe_key, n))
return false;
for (unsigned i = 0; i < n.get_nr_entries(); i++) for (unsigned i = 0; i < n.get_nr_entries(); i++)
devices_.insert(make_pair(n.key_at(i), n.value_at(i))); devices_.insert(make_pair(n.key_at(i), n.value_at(i)));
@ -137,7 +169,14 @@ namespace {
return devices_; return devices_;
} }
bool corruption() const {
return !checker_.get_errors()->empty();
}
private: private:
// Declaration order of counter_ and checker_ is important.
block_counter counter_;
checker checker_;
map<uint64_t, device_details> devices_; map<uint64_t, device_details> devices_;
}; };
} }
@ -145,13 +184,19 @@ namespace {
//---------------------------------------------------------------- //----------------------------------------------------------------
void void
thin_provisioning::metadata_dump(metadata::ptr md, emitter::ptr e) thin_provisioning::metadata_dump(metadata::ptr md, emitter::ptr e, bool repair)
{ {
e->begin_superblock("", md->sb_.time_, md->sb_.trans_id_, md->sb_.data_block_size_); e->begin_superblock("", md->sb_.time_,
md->sb_.trans_id_,
md->sb_.data_block_size_,
md->data_sm_->get_nr_blocks());
details_extractor::ptr de(new details_extractor); details_extractor::ptr de(new details_extractor);
md->details_->visit(de); md->details_->visit(de);
if (de->corruption() && !repair)
throw runtime_error("corruption in device details tree");
map<uint64_t, device_details> const &devs = de->get_devices(); map<uint64_t, device_details> const &devs = de->get_devices();
map<uint64_t, device_details>::const_iterator it, end = devs.end(); map<uint64_t, device_details>::const_iterator it, end = devs.end();
@ -168,6 +213,12 @@ thin_provisioning::metadata_dump(metadata::ptr md, emitter::ptr e)
mappings_extractor::ptr me(new mappings_extractor(dev_id, e, md->metadata_sm_, md->data_sm_)); mappings_extractor::ptr me(new mappings_extractor(dev_id, e, md->metadata_sm_, md->data_sm_));
md->mappings_->visit(me); md->mappings_->visit(me);
if (me->corruption() && !repair) {
ostringstream out;
out << "corruption in mappings for device " << dev_id;
throw runtime_error(out.str());
}
e->end_device(); e->end_device();
} }

View File

@ -25,7 +25,10 @@
//---------------------------------------------------------------- //----------------------------------------------------------------
namespace thin_provisioning { namespace thin_provisioning {
void metadata_dump(metadata::ptr md, emitter::ptr e); // Set the @repair flag if your metadata is corrupt, and you'd like
// the dumper to do it's best to recover info. If not set, any
// corruption encountered will cause an exception to be thrown.
void metadata_dump(metadata::ptr md, emitter::ptr e, bool repair);
} }
//---------------------------------------------------------------- //----------------------------------------------------------------

0
mk_release Normal file → Executable file
View File

View File

@ -33,15 +33,13 @@ namespace {
} }
virtual ~restorer() { virtual ~restorer() {
if (in_superblock_) {
throw runtime_error("still in superblock");
}
} }
virtual void begin_superblock(std::string const &uuid, virtual void begin_superblock(std::string const &uuid,
uint64_t time, uint64_t time,
uint64_t trans_id, uint64_t trans_id,
uint32_t data_block_size) { uint32_t data_block_size,
uint64_t nr_data_blocks) {
in_superblock_ = true; in_superblock_ = true;
superblock &sb = md_->sb_; superblock &sb = md_->sb_;
@ -49,6 +47,7 @@ namespace {
sb.time_ = time; sb.time_ = time;
sb.trans_id_ = trans_id; sb.trans_id_ = trans_id;
sb.data_block_size_ = data_block_size; sb.data_block_size_ = data_block_size;
md_->data_sm_->extend(nr_data_blocks);
} }
virtual void end_superblock() { virtual void end_superblock() {
@ -72,7 +71,7 @@ namespace {
// Add entry to the details tree // Add entry to the details tree
uint64_t key[1] = {dev}; uint64_t key[1] = {dev};
device_details details = {mapped_blocks, trans_id, creation_time, snap_time}; device_details details = {mapped_blocks, trans_id, (uint32_t)creation_time, (uint32_t)snap_time};
md_->details_->insert(key, details); md_->details_->insert(key, details);
// Insert an empty mapping tree // Insert an empty mapping tree

View File

@ -43,10 +43,10 @@ namespace {
crc32c sum(BITMAP_CSUM_XOR); crc32c sum(BITMAP_CSUM_XOR);
sum.append(&data->not_used, MD_BLOCK_SIZE - sizeof(uint32_t)); sum.append(&data->not_used, MD_BLOCK_SIZE - sizeof(uint32_t));
if (sum.get_sum() != to_cpu<uint32_t>(data->csum)) if (sum.get_sum() != to_cpu<uint32_t>(data->csum))
throw runtime_error("bad checksum in space map bitmap"); throw checksum_error("bad checksum in space map bitmap");
if (to_cpu<uint64_t>(data->blocknr) != location) if (to_cpu<uint64_t>(data->blocknr) != location)
throw runtime_error("bad block nr in space map bitmap"); throw checksum_error("bad block nr in space map bitmap");
} }
virtual void prepare(block_manager<>::buffer &b, block_address location) const { virtual void prepare(block_manager<>::buffer &b, block_address location) const {
@ -75,10 +75,10 @@ namespace {
crc32c sum(INDEX_CSUM_XOR); crc32c sum(INDEX_CSUM_XOR);
sum.append(&mi->padding_, MD_BLOCK_SIZE - sizeof(uint32_t)); sum.append(&mi->padding_, MD_BLOCK_SIZE - sizeof(uint32_t));
if (sum.get_sum() != to_cpu<uint32_t>(mi->csum_)) if (sum.get_sum() != to_cpu<uint32_t>(mi->csum_))
throw runtime_error("bad checksum in metadata index block"); throw checksum_error("bad checksum in metadata index block");
if (to_cpu<uint64_t>(mi->blocknr_) != location) if (to_cpu<uint64_t>(mi->blocknr_) != location)
throw runtime_error("bad block nr in metadata index block"); throw checksum_error("bad block nr in metadata index block");
} }
virtual void prepare(block_manager<>::buffer &b, block_address location) const { virtual void prepare(block_manager<>::buffer &b, block_address location) const {

View File

@ -29,31 +29,41 @@ using namespace std;
using namespace thin_provisioning; using namespace thin_provisioning;
namespace { namespace {
int check(string const &path) { int check(string const &path, bool quiet) {
metadata::ptr md(new metadata(path, metadata::OPEN)); try {
metadata::ptr md(new metadata(path, metadata::OPEN));
optional<error_set::ptr> maybe_errors = metadata_check(md); optional<error_set::ptr> maybe_errors = metadata_check(md);
if (maybe_errors) { if (maybe_errors) {
cerr << error_selector(*maybe_errors, 3); if (!quiet)
cerr << error_selector(*maybe_errors, 3);
return 1;
}
} catch (std::exception &e) {
if (!quiet)
cerr << e.what() << endl;
return 1; return 1;
} }
return 0; return 0;
} }
void usage(string const &cmd) { void usage(ostream &out, string const &cmd) {
cerr << "Usage: " << cmd << " {device|file}" << endl; out << "Usage: " << cmd << " [options] {device|file}" << endl
cerr << "Options:" << endl; << "Options:" << endl
cerr << " {-h|--help}" << endl; << " {-q|--quiet}" << endl
cerr << " {-V|--version}" << endl; << " {-h|--help}" << endl
<< " {-V|--version}" << endl;
} }
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int c; int c;
const char shortopts[] = "hV"; bool quiet = false;
const char shortopts[] = "qhV";
const struct option longopts[] = { const struct option longopts[] = {
{ "quiet", no_argument, NULL, 'q'},
{ "help", no_argument, NULL, 'h'}, { "help", no_argument, NULL, 'h'},
{ "version", no_argument, NULL, 'V'}, { "version", no_argument, NULL, 'V'},
{ NULL, no_argument, NULL, 0 } { NULL, no_argument, NULL, 0 }
@ -61,19 +71,29 @@ int main(int argc, char **argv)
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(basename(argv[0])); usage(cout, basename(argv[0]));
return 0; return 0;
case 'V':
cerr << THIN_PROVISIONING_TOOLS_VERSION << endl; case 'q':
return 0; quiet = true;
break;
case 'V':
cout << THIN_PROVISIONING_TOOLS_VERSION << endl;
return 0;
default:
usage(cerr, basename(argv[0]));
return 1;
} }
} }
if (argc != 2) { if (argc == optind) {
usage(basename(argv[0])); cerr << "No input file provided." << endl;
usage(cerr, basename(argv[0]));
exit(1); exit(1);
} }
return check(argv[1]); return check(argv[optind], quiet);
} }

345
thin_debug.cc Normal file
View File

@ -0,0 +1,345 @@
// Copyright (C) 2012 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 <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/variant.hpp>
#include <getopt.h>
#include <iostream>
#include <libgen.h>
#include <map>
#include <string>
#include <vector>
#include "btree.h"
#include "metadata.h"
#include "metadata_checker.h"
#include "version.h"
using namespace boost;
using namespace persistent_data;
using namespace std;
using namespace thin_provisioning;
namespace {
typedef vector<string> strings;
class formatter {
public:
typedef shared_ptr<formatter> ptr;
virtual ~formatter() {}
typedef optional<string> maybe_string;
void field(string const &name, string const &value) {
fields_.push_back(field_type(name, value));
}
void child(string const &name, formatter::ptr t) {
fields_.push_back(field_type(name, t));
}
virtual void output(ostream &out, int depth = 0) = 0;
protected:
typedef variant<string, ptr> value;
typedef tuple<string, value> field_type;
vector<field_type> fields_;
};
template <typename T>
void
field(formatter &t, string const &name, T const &value) {
t.field(name, lexical_cast<string>(value));
}
//--------------------------------
class xml_formatter : public formatter {
public:
virtual void output(ostream &out, int depth) {
indent(depth, out);
out << "<fields>" << endl;
vector<field_type>::const_iterator it;
for (it = fields_.begin(); it != fields_.end(); ++it) {
if (string const *s = get<string>(&it->get<1>())) {
indent(depth + 1, out);
out << "<field key=\""
<< it->get<0>()
<< "\" value=\""
<< *s
<< "\"/>"
<< endl;
} else {
formatter::ptr f = get<formatter::ptr>(it->get<1>());
f->output(out, depth + 1);
}
}
indent(depth, out);
out << "</fields>" << endl;
}
private:
void indent(int depth, ostream &out) const {
for (int i = 0; i < depth * 2; i++)
out << ' ';
}
};
//--------------------------------
class command {
public:
typedef boost::shared_ptr<command> ptr;
virtual ~command() {}
virtual void exec(strings const &args, ostream &out) = 0;
};
class command_interpreter {
public:
command_interpreter(istream &in, ostream &out)
: in_(in),
out_(out) {
}
void register_command(string const &str, command::ptr cmd) {
commands_.insert(make_pair(str, cmd));
}
void enter_main_loop() {
while (true)
do_once();
}
private:
strings read_input() {
using namespace boost::algorithm;
string input;
getline(in_, input);
strings toks;
split(toks, input, is_any_of(" \t"), token_compress_on);
return toks;
}
void do_once() {
if (in_.eof())
throw runtime_error("input closed");
out_ << "> ";
strings args = read_input();
map<string, command::ptr>::iterator it;
it = commands_.find(args[0]);
if (it == commands_.end())
out_ << "Unrecognised command" << endl;
else
it->second->exec(args, out_);
}
istream &in_;
ostream &out_;
map <string, command::ptr> commands_;
};
//--------------------------------
class hello : public command {
virtual void exec(strings const &args, ostream &out) {
out << "Hello, world!" << endl;
}
};
class show_superblock : public command {
public:
explicit show_superblock(metadata::ptr md)
: md_(md) {
}
virtual void exec(strings const &args, ostream &out) {
xml_formatter f;
superblock const &sb = md_->sb_;
field(f, "csum", sb.csum_);
field(f, "flags", sb.flags_);
field(f, "blocknr", sb.blocknr_);
field(f, "uuid", sb.uuid_); // FIXME: delimit, and handle non-printable chars
field(f, "magic", sb.magic_);
field(f, "version", sb.version_);
field(f, "time", sb.time_);
field(f, "trans id", sb.trans_id_);
field(f, "held root", sb.held_root_);
field(f, "data mapping root", sb.data_mapping_root_);
field(f, "device details root", sb.device_details_root_);
field(f, "data block size", sb.data_block_size_);
field(f, "metadata block size", sb.metadata_block_size_);
field(f, "metadata nr blocks", sb.metadata_nr_blocks_);
field(f, "compat flags", sb.compat_flags_);
field(f, "compat ro flags", sb.compat_ro_flags_);
field(f, "incompat flags", sb.incompat_flags_);
f.output(out, 0);
}
private:
metadata::ptr md_;
};
class device_details_show_traits : public device_details_traits {
public:
static void show(formatter &f, string const &key, device_details const &value) {
field(f, "mapped blocks", value.mapped_blocks_);
field(f, "transaction id", value.transaction_id_);
field(f, "creation time", value.creation_time_);
field(f, "snap time", value.snapshotted_time_);
}
};
class uint64_show_traits : public uint64_traits {
public:
static void show(formatter &f, string const &key, uint64_t const &value) {
field(f, key, lexical_cast<string>(value));
}
};
class block_show_traits : public block_traits {
public:
static void show(formatter &f, string const &key, block_time const &value) {
field(f, "block", value.block_);
field(f, "time", value.time_);
}
};
template <typename ValueTraits>
class show_btree_node : public command {
public:
explicit show_btree_node(metadata::ptr md)
: md_(md) {
}
virtual void exec(strings const &args, ostream &out) {
using namespace persistent_data::btree_detail;
if (args.size() != 2)
throw runtime_error("incorrect number of arguments");
block_address block = lexical_cast<block_address>(args[1]);
block_manager<>::read_ref rr = md_->tm_->read_lock(block);
node_ref<uint64_show_traits> n = btree_detail::to_node<uint64_show_traits>(rr);
if (n.get_type() == INTERNAL)
show_node<uint64_show_traits>(n, out);
else {
node_ref<ValueTraits> n = btree_detail::to_node<ValueTraits>(rr);
show_node<ValueTraits>(n, out);
}
}
private:
template <typename VT>
void show_node(node_ref<VT> n, ostream &out) {
xml_formatter f;
field(f, "csum", n.get_checksum());
field(f, "blocknr", n.get_location());
field(f, "type", n.get_type() == INTERNAL ? "internal" : "leaf");
field(f, "nr entries", n.get_nr_entries());
field(f, "max entries", n.get_max_entries());
field(f, "value size", n.get_value_size());
for (unsigned i = 0; i < n.get_nr_entries(); i++) {
formatter::ptr f2(new xml_formatter);
field(*f2, "key", n.key_at(i));
VT::show(*f2, "value", n.value_at(i));
f.child("child", f2);
}
f.output(out, 0);
}
metadata::ptr md_;
};
//--------------------------------
int debug(string const &path) {
try {
metadata::ptr md(new metadata(path, metadata::OPEN));
command_interpreter interp(cin, cout);
interp.register_command("hello", command::ptr(new hello));
interp.register_command("superblock", command::ptr(new show_superblock(md)));
interp.register_command("m1_node", command::ptr(new show_btree_node<uint64_show_traits>(md)));
interp.register_command("m2_node", command::ptr(new show_btree_node<block_show_traits>(md)));
interp.register_command("detail_node", command::ptr(new show_btree_node<device_details_show_traits>(md)));
interp.enter_main_loop();
} catch (std::exception &e) {
cerr << e.what();
return 1;
}
return 0;
}
void usage(string const &cmd) {
cerr << "Usage: " << cmd << " {device|file}" << endl
<< "Options:" << endl
<< " {-h|--help}" << endl
<< " {-V|--version}" << endl;
}
}
int main(int argc, char **argv)
{
int c;
const char shortopts[] = "hV";
const struct option longopts[] = {
{ "help", no_argument, NULL, 'h'},
{ "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(basename(argv[0]));
return 0;
case 'V':
cerr << THIN_PROVISIONING_TOOLS_VERSION << endl;
return 0;
}
}
if (argc == optind) {
usage(basename(argv[0]));
exit(1);
}
return debug(argv[optind]);
}

View File

@ -30,77 +30,83 @@ using namespace persistent_data;
using namespace std; using namespace std;
using namespace thin_provisioning; using namespace thin_provisioning;
//----------------------------------------------------------------
namespace { namespace {
void dump(string const &path, string const &format) { int dump(string const &path, string const &format, bool repair) {
metadata::ptr md(new metadata(path, metadata::OPEN)); try {
emitter::ptr e; metadata::ptr md(new metadata(path, metadata::OPEN));
emitter::ptr e;
if (format == "xml") if (format == "xml")
e = create_xml_emitter(cout); e = create_xml_emitter(cout);
else if (format == "human_readable") else if (format == "human_readable")
e = create_human_readable_emitter(cout); e = create_human_readable_emitter(cout);
else { else {
cerr << "unknown format '" << format << "'" << endl; cerr << "unknown format '" << format << "'" << endl;
exit(1); exit(1);
}
metadata_dump(md, e, repair);
} catch (std::exception &e) {
cerr << e.what() << endl;
return 1;
} }
metadata_dump(md, e); return 0;
} }
void usage(string const &cmd) { void usage(ostream &out, string const &cmd) {
cerr << "Usage: " << cmd << " [options] {device|file}" << endl << endl; out << "Usage: " << cmd << " [options] {device|file}" << endl
cerr << "Options:" << endl; << "Options:" << endl
cerr << " {-h|--help}" << endl; << " {-h|--help}" << endl
cerr << " {-f|--format} {xml|human_readable}" << endl; << " {-f|--format} {xml|human_readable}" << endl
cerr << " {-i|--input} {xml|human_readable} input_file" << endl; << " {-r|--repair}" << endl
cerr << " {-V|--version}" << endl; << " {-V|--version}" << endl;
} }
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int c; int c;
const char shortopts[] = "hf:i:V"; bool repair = false;
string filename, format = "xml"; const char shortopts[] = "hf:rV";
string format = "xml";
const struct option longopts[] = { const struct option longopts[] = {
{ "help", no_argument, NULL, 'h'}, { "help", no_argument, NULL, 'h'},
{ "format", required_argument, NULL, 'f' }, { "format", required_argument, NULL, 'f' },
{ "input", required_argument, NULL, 'i'}, { "repair", no_argument, NULL, 'r'},
{ "version", no_argument, NULL, 'V'}, { "version", no_argument, NULL, 'V'},
{ NULL, no_argument, NULL, 0 } { 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(basename(argv[0])); usage(cout, basename(argv[0]));
return 0; return 0;
case 'f':
format = optarg; case 'f':
break; format = optarg;
case 'i': break;
filename = optarg;
break; case 'r':
case 'V': repair = true;
cerr << THIN_PROVISIONING_TOOLS_VERSION << endl; break;
return 0;
case 'V':
cout << THIN_PROVISIONING_TOOLS_VERSION << endl;
return 0;
default:
usage(cerr, basename(argv[0]));
return 1;
} }
} }
if (argc == 1) { if (argc == optind) {
usage(basename(argv[0])); cerr << "No input file provided." << endl;
usage(cerr, basename(argv[0]));
return 1; return 1;
} }
if (filename.empty()) { return dump(argv[optind], format, repair);
cerr << "No output file provided." << endl;
return 1;
}
dump(filename, format);
return 0;
} }
//----------------------------------------------------------------

View File

@ -24,42 +24,43 @@
#include "version.h" #include "version.h"
#include <fstream> #include <fstream>
#include <iostream>
#include <getopt.h> #include <getopt.h>
#include <iostream>
#include <libgen.h> #include <libgen.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
using namespace persistent_data; using namespace persistent_data;
using namespace std; using namespace std;
using namespace thin_provisioning; using namespace thin_provisioning;
//----------------------------------------------------------------
namespace { namespace {
void restore(string const &backup_file, string const &dev) { int restore(string const &backup_file, string const &dev) {
// FIXME: hard coded try {
block_address const NR_BLOCKS = 100000; // The block size gets updated by the restorer.
metadata::ptr md(new metadata(dev, metadata::CREATE, 128, 0));
metadata::ptr md(new metadata(dev, metadata::CREATE, 128, NR_BLOCKS)); emitter::ptr restorer = create_restore_emitter(md);
emitter::ptr restorer = create_restore_emitter(md); ifstream in(backup_file.c_str(), ifstream::in);
ifstream in(backup_file.c_str(), ifstream::in);
// FIXME:
//try {
parse_xml(in, restorer); parse_xml(in, restorer);
#if 0
} catch (...) { } catch (std::exception &e) {
in.close(); cerr << e.what() << endl;
throw; return 1;
} }
#endif
return 0;
} }
void usage(string const &cmd) { void usage(ostream &out, string const &cmd) {
cerr << "Usage: " << cmd << " [options]" << endl << endl; out << "Usage: " << cmd << " [options]" << endl
cerr << "Options:" << endl; << "Options:" << endl
cerr << " {-h|--help}" << endl; << " {-h|--help}" << endl
cerr << " {-i|--input} input_file" << endl; << " {-i|--input} input_file" << endl
cerr << " {-o [ --output} {device|file}" << endl; << " {-o|--output} {device|file}" << endl
cerr << " {-V|--version}" << endl; << " {-V|--version}" << endl;
} }
} }
@ -79,38 +80,43 @@ int main(int argc, char **argv)
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(basename(argv[0])); usage(cout, basename(argv[0]));
return 0; return 0;
case 'i': case 'i':
input = optarg; input = optarg;
break; break;
case 'o': case 'o':
output = optarg; output = optarg;
break; break;
case 'V': case 'V':
cerr << THIN_PROVISIONING_TOOLS_VERSION << endl; cout << THIN_PROVISIONING_TOOLS_VERSION << endl;
return 0; return 0;
default:
usage(cerr, basename(argv[0]));
return 1;
} }
} }
if (argc == 1) { if (argc != optind) {
usage(basename(argv[0])); usage(cerr, basename(argv[0]));
return 1; return 1;
} }
if (input.empty()) { if (input.empty()) {
cerr << "No input file provided." << endl; cerr << "No input file provided." << endl;
usage(cerr, basename(argv[0]));
return 1; return 1;
} }
if (output.empty()) { if (output.empty()) {
cerr << "No output file provided." << endl; cerr << "No output file provided." << endl;
usage(cerr, basename(argv[0]));
return 1; return 1;
} }
restore(input, output); return restore(input, output);
return 0;
} }
//----------------------------------------------------------------

View File

@ -48,12 +48,14 @@ namespace {
void begin_superblock(string const &uuid, void begin_superblock(string const &uuid,
uint64_t time, uint64_t time,
uint64_t trans_id, uint64_t trans_id,
uint32_t data_block_size) { uint32_t data_block_size,
uint64_t nr_data_blocks) {
indent(); indent();
out_ << "<superblock uuid=\"" << uuid << "\"" out_ << "<superblock uuid=\"" << uuid << "\""
<< " time=\"" << time << "\"" << " time=\"" << time << "\""
<< " transaction=\"" << trans_id << "\"" << " transaction=\"" << trans_id << "\""
<< " data_block_size=\"" << data_block_size << "\">" << " data_block_size=\"" << data_block_size << "\""
<< " nr_data_blocks=\"" << nr_data_blocks << "\">"
<< endl; << endl;
inc(); inc();
} }
@ -176,7 +178,8 @@ namespace {
e->begin_superblock(get_attr<string>(attr, "uuid"), e->begin_superblock(get_attr<string>(attr, "uuid"),
get_attr<uint64_t>(attr, "time"), get_attr<uint64_t>(attr, "time"),
get_attr<uint64_t>(attr, "transaction"), get_attr<uint64_t>(attr, "transaction"),
get_attr<uint32_t>(attr, "data_block_size")); get_attr<uint32_t>(attr, "data_block_size"),
get_attr<uint64_t>(attr, "nr_data_blocks"));
} }
void parse_device(emitter *e, attributes const &attr) { void parse_device(emitter *e, attributes const &attr) {