diff --git a/Makefile.in b/Makefile.in index e737ff1..315b28e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -19,7 +19,8 @@ .PHONY: all PROGRAMS=\ - thin_repair \ + thin_debug \ + thin_check \ thin_dump \ thin_restore @@ -45,15 +46,15 @@ SOURCE=\ xml_format.cc PROGRAM_SOURCE=\ + thin_check.cc \ thin_dump.cc \ - thin_repair.cc \ thin_restore.cc CXX:=@CXX@ OBJECTS:=$(subst .cc,.o,$(SOURCE)) TOP_DIR:=@top_srcdir@ TOP_BUILDDIR:=@top_builddir@ -CXXFLAGS+=-Wall +CXXFLAGS+=-Wall -fno-strict-aliasing CXXFLAGS+=@CXXOPTIMISE_FLAG@ CXXFLAGS+=@CXXDEBUG_FLAG@ INCLUDES+=-I$(TOP_BUILDDIR) -I$(TOP_DIR) @@ -61,8 +62,8 @@ LIBS:=-lstdc++ LIBEXPAT:=-lexpat INSTALL:=@INSTALL@ STRIP= -DESTDIR:=@prefix@ -BINDIR:=$(DESTDIR)/sbin +PREFIX:=@prefix@ +BINDIR:=$(DESTDIR)$(PREFIX)/sbin MANPATH:=$(DESTDIR)$(MANDIR) vpath %.cc $(TOP_DIR) @@ -85,9 +86,10 @@ test-programs: $(TEST_PROGRAMS) %.o: %.cc $(CXX) -c $(INCLUDES) $(CXXFLAGS) -o $@ $< +THIN_DEBUG_SOURCE=$(SOURCE) THIN_DUMP_SOURCE=$(SOURCE) THIN_RESTORE_SOURCE=$(SOURCE) -THIN_REPAIR_SOURCE=\ +THIN_CHECK_SOURCE=\ checksum.cc \ endian_utils.cc \ error_set.cc \ @@ -101,9 +103,13 @@ THIN_REPAIR_SOURCE=\ space_map_transactional.cc \ transaction_manager.cc +THIN_DEBUG_OBJECTS=$(subst .cc,.o,$(THIN_DEBUG_SOURCE)) THIN_DUMP_OBJECTS=$(subst .cc,.o,$(THIN_DUMP_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 $(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 $(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) clean: @@ -122,11 +128,11 @@ distclean: clean install: $(PROGRAMS) $(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_restore $(BINDIR)/thin_restore $(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_restore.8 $(MANPATH)/man8/thin_restore.8 .PHONY: install diff --git a/VERSION b/VERSION index 19bbd7b..233aae5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.1-github(0) (2011-12-06) \ No newline at end of file +0.1.4+ diff --git a/btree.tcc b/btree.tcc index ebf0636..fa16243 100644 --- a/btree.tcc +++ b/btree.tcc @@ -18,11 +18,13 @@ #include "btree.h" +#include "errors.h" #include "checksum.h" #include "transaction_manager.h" #include +using namespace base; using namespace btree_detail; using namespace persistent_data; using namespace std; @@ -37,10 +39,10 @@ namespace { crc32c sum(BTREE_CSUM_XOR); sum.append(&n->flags, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(n->csum)) - throw runtime_error("bad checksum in btree node"); + throw checksum_error("bad checksum in btree node"); if (to_cpu(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 { diff --git a/btree_checker.h b/btree_checker.h index 4078034..2b579ae 100644 --- a/btree_checker.h +++ b/btree_checker.h @@ -51,81 +51,39 @@ namespace persistent_data { // Not implemented // --------------- // - // - checksum // - leaf | internal flags (this can be inferred from siblings) //---------------------------------------------------------------- template class btree_checker : public btree::visitor { public: - btree_checker(block_counter &counter) + btree_checker(block_counter &counter, bool avoid_repeated_visits = true) : 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 sub_root, optional key, btree_detail::node_ref const &n) { - if (already_visited(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() : key, n); - return true; + return check_internal(level, sub_root, key, n); } bool visit_internal_leaf(unsigned level, bool sub_root, optional key, btree_detail::node_ref const &n) { - if (already_visited(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() : key, n); - check_leaf_key(level, n); - - return true; + return check_leaf(level, sub_root, key, n); } bool visit_leaf(unsigned level, bool sub_root, optional key, btree_detail::node_ref const &n) { - if (already_visited(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() : key, n); - check_leaf_key(level, n); - return true; + return check_leaf(level, sub_root, key, n); } - boost::optional get_errors() const { + error_set::ptr get_errors() const { return errs_; } @@ -135,21 +93,66 @@ namespace persistent_data { } private: + bool check_internal(unsigned level, + bool sub_root, + optional key, + btree_detail::node_ref 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() : key, n)) { + if (sub_root) + new_root(level); + + return true; + } + + return false; + } + + template + bool check_leaf(unsigned level, + bool sub_root, + optional key, + btree_detail::node_ref 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() : key, n)) { + if (sub_root) + new_root(level); + + return check_leaf_key(level, n); + } + + return false; + } + + template bool already_visited(node const &n) { block_address b = n.get_location(); counter_.inc(b); - if (seen_.count(b) > 0) - return true; + if (avoid_repeated_visits_) { + if (seen_.count(b) > 0) + return true; + + seen_.insert(b); + } - seen_.insert(b); return false; } template - void check_sum(node const &n) const { + bool check_sum(node const &n) const { crc32c sum(BTREE_CSUM_XOR); disk_node const *data = n.raw(); @@ -160,48 +163,55 @@ namespace persistent_data { << ", sum was " << sum.get_sum() << ", on disk " << n.get_checksum(); errs_->add_child(out.str()); - throw runtime_error(out.str()); + return false; } + + return true; } template - void check_block_nr(node const &n) const { + bool check_block_nr(node const &n) const { if (n.get_location() != n.get_block_nr()) { std::ostringstream out; out << "block number mismatch: actually " << n.get_location() << ", claims " << n.get_block_nr(); errs_->add_child(out.str()); - throw runtime_error(out.str()); + return false; } + + return true; } template - 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(); if (elt_size * n.get_max_entries() + sizeof(node_header) > MD_BLOCK_SIZE) { std::ostringstream out; out << "max entries too large: " << n.get_max_entries(); errs_->add_child(out.str()); + return false; } if (n.get_max_entries() % 3) { std::ostringstream out; out << "max entries is not divisible by 3: " << n.get_max_entries(); errs_->add_child(out.str()); - throw runtime_error(out.str()); + return false; } + + return true; } template - 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()) { std::ostringstream out; out << "bad nr_entries: " << n.get_nr_entries() << " < " << n.get_max_entries(); errs_->add_child(out.str()); - throw std::runtime_error(out.str()); + return false; } block_address min = n.get_max_entries() / 3; @@ -213,16 +223,18 @@ namespace persistent_data { << min << "(max_entries = " << n.get_max_entries() << ")"; errs_->add_child(out.str()); - throw runtime_error(out.str()); + return false; } + + return true; } template - void check_ordered_keys(node const &n) const { + bool check_ordered_keys(node const &n) const { unsigned nr_entries = n.get_nr_entries(); 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); @@ -231,50 +243,59 @@ namespace persistent_data { if (k <= last_key) { ostringstream out; out << "keys are out of order, " << k << " <= " << last_key; - throw runtime_error(out.str()); + errs_->add_child(out.str()); + return false; } last_key = k; } + + return true; } template - void check_parent_key(boost::optional key, node const &n) const { + bool check_parent_key(boost::optional key, node const &n) const { if (!key) - return; + return true; if (*key > n.key_at(0)) { ostringstream out; out << "parent key mismatch: parent was " << *key << ", but lowest in node was " << n.key_at(0); - throw runtime_error(out.str()); + errs_->add_child(out.str()); + return false; } + + return true; } template - void check_leaf_key(unsigned level, node const &n) { + bool check_leaf_key(unsigned level, node const &n) { 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)) { ostringstream out; 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); - throw runtime_error(out.str()); + errs_->add_child(out.str()); + return false; } last_leaf_key_[level] = n.key_at(n.get_nr_entries() - 1); + return true; } void new_root(unsigned level) { - // we're starting a new subtree, so should - // reset the last_leaf value. - last_leaf_key_[level] = boost::optional(); + // we're starting a new subtree, so should + // reset the last_leaf value. + last_leaf_key_[level] = boost::optional(); } block_counter &counter_; std::set seen_; error_set::ptr errs_; boost::optional last_leaf_key_[Levels]; + bool avoid_repeated_visits_; }; } diff --git a/emitter.h b/emitter.h index 14e3e82..8409cfb 100644 --- a/emitter.h +++ b/emitter.h @@ -47,7 +47,8 @@ namespace thin_provisioning { virtual void begin_superblock(std::string const &uuid, uint64_t time, 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 begin_device(uint32_t dev_id, diff --git a/endian_utils.h b/endian_utils.h index 5ddb1e5..50a65e2 100644 --- a/endian_utils.h +++ b/endian_utils.h @@ -19,6 +19,7 @@ #ifndef ENDIAN_H #define ENDIAN_H +#include #include #include @@ -69,32 +70,32 @@ namespace base { template <> inline uint16_t to_cpu(__le16 const &d) { - return d.v_; + return le16toh(d.v_); } template <> inline __le16 to_disk<__le16, uint16_t>(uint16_t const &v) { - return __le16(v); + return __le16(htole16(v)); } template <> inline uint32_t to_cpu(__le32 const &d) { - return d.v_; + return le32toh(d.v_); } template <> inline __le32 to_disk<__le32, uint32_t>(uint32_t const &v) { - return __le32(v); + return __le32(htole32(v)); } template <> inline uint64_t to_cpu(__le64 const &d) { - return d.v_; + return le64toh(d.v_); } template <> inline __le64 to_disk<__le64, uint64_t>(uint64_t const &v) { - return __le64(v); + return __le64(htole64(v)); } //-------------------------------- diff --git a/error_set.cc b/error_set.cc index a8dfae7..d225824 100644 --- a/error_set.cc +++ b/error_set.cc @@ -61,6 +61,12 @@ error_set::add_child(string const &err) add_child(e); } +bool +error_set::empty() const +{ + return !children_.size(); +} + //-------------------------------- namespace { diff --git a/error_set.h b/error_set.h index 52d12df..f3b5ece 100644 --- a/error_set.h +++ b/error_set.h @@ -43,6 +43,7 @@ namespace persistent_data { void add_child(error_set::ptr err); void add_child(boost::optional maybe_errs); void add_child(std::string const &err); + bool empty() const; private: std::string err_; diff --git a/errors.h b/errors.h new file mode 100644 index 0000000..f0c830a --- /dev/null +++ b/errors.h @@ -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 +// . + +#ifndef THINP_EXCEPTION_H +#define THINP_EXCEPTION_H + +#include + +//---------------------------------------------------------------- + +namespace base { + class checksum_error : public std::runtime_error { + public: + explicit checksum_error(std::string const &what) + : std::runtime_error(what) { + } + }; +} + +//---------------------------------------------------------------- + +#endif diff --git a/human_readable_format.cc b/human_readable_format.cc index ad60c21..f30f50c 100644 --- a/human_readable_format.cc +++ b/human_readable_format.cc @@ -35,11 +35,13 @@ namespace { void begin_superblock(string const &uuid, uint64_t time, uint64_t trans_id, - uint32_t data_block_size) { + uint32_t data_block_size, + uint64_t nr_data_blocks) { out_ << "begin superblock: \"" << uuid << "\"" << ", " << time << ", " << trans_id << ", " << data_block_size + << ", " << nr_data_blocks << endl; } diff --git a/man8/thin_check.8 b/man8/thin_check.8 new file mode 100644 index 0000000..6daff5a --- /dev/null +++ b/man8/thin_check.8 @@ -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 +.br +Heinz Mauelshagen diff --git a/man8/thin_dump.8 b/man8/thin_dump.8 index 5dd8920..697df83 100644 --- a/man8/thin_dump.8 +++ b/man8/thin_dump.8 @@ -3,41 +3,49 @@ thin_dump \- dump thin provisioning metadata from device or file to standard output .SH SYNOPSIS -.B thin_dump [options] {metadata device|file} +.B thin_dump +.RB [options] +.I {metadata device|file} .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 analysis or postprocessing in either XML or human readable format. XML formated metadata can be fed into thin_restore (see -.B thin_restore(8) -) in order to put it back onto a metadata device (to process by +.BR thin_restore(8) ) +in order to put it back onto a metadata device (to process by the device-mapper target) or file. -.B thin_dump - {-i|--input} {device|file} - [{-f|--format} {xml|human_readable}] +.IP "\fB\-f, \-\-format\fP \fI{xml|human_readable}\fP". +Print output in XML or human readable format. -.B thin_dump - {-h|--help} +.IP "\fB\-r, \-\-repair\fP". -.B thin_dump - {-V|--version} +.IP "\fB\-h, \-\-help\fP". +Print help and exit. + +.IP "\fB\-V, \-\-version\fP". +Output version information and exit. .SH EXAMPLES -"thin_dump -f human_redable -i /dev/vg/metadata" -dumps the thin provisioning metadata on logical volume /dev/vg/metadata -to standard output in human readable format. +Dumps the thin provisioning metadata on logical volume /dev/vg/metadata +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 -to standard output in XML format. +Dumps the thin provisioning metadata on logical volume /dev/vg/metadata +to standard output in XML format: +.sp +.B thin_dump /dev/vg/metadata .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 -.B thin_repair(8), thin_restore(8) +.B thin_check(8) +.B thin_restore(8) .SH AUTHOR Joe Thornber diff --git a/man8/thin_repair.8 b/man8/thin_repair.8 deleted file mode 100644 index 303ffb5..0000000 --- a/man8/thin_repair.8 +++ /dev/null @@ -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 -.br -Heinz Mauelshagen diff --git a/man8/thin_restore.8 b/man8/thin_restore.8 index c5407ca..b4a07aa 100644 --- a/man8/thin_restore.8 +++ b/man8/thin_restore.8 @@ -3,37 +3,49 @@ thin_restore \- restore thin provisioning metadata file to device or file .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 -thin_restore restores thin provisioning metadata created by the -respective device-mapper target dumped into an XML formated (see -.B thin_dump(8) -) file, which optionally can be preproccessed to another device or file. +.B thin_restore +restores thin provisioning metadata created by the +respective device-mapper target dumped into an XML formatted (see +.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 by the device-mapper target. -.B thin_restore - {-i|--input} {device|file} - {-o|--output} {device|file} +.IP "\fB\-i, \-\-input\fP \fI{device|file}\fP" +Input file or device with metadata. -.B thin_restore - -h|--help +.IP "\fB\-o, \-\-output\fP \fI{device|file}\fP" +Output file or device. -.B thin_dump - {-V|--version} +.IP "\fB\-h, \-\-help\fP" +Print help and exit. + +.IP "\fB\-V, \-\-version\fP" +Output version information and exit. .SH EXAMPLE -"thin_restore -i metadata -o /dev/vg/metadata" -restores the XML formatted thin provisioning metadata on file "metadata" +Restores the XML formatted thin provisioning metadata on file +.B metadata 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 -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 -.B thin_dump(8), thin_repair(8) +.B thin_dump(8) +.B thin_check(8) .SH AUTHOR Joe Thornber diff --git a/metadata.cc b/metadata.cc index 7194dc8..a7da4b9 100644 --- a/metadata.cc +++ b/metadata.cc @@ -45,7 +45,7 @@ namespace { crc32c sum(SUPERBLOCK_CSUM_SEED); sum.append(&sbd->flags_, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(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 { @@ -65,8 +65,8 @@ namespace { if (r) throw runtime_error("Couldn't stat dev path"); - if (S_ISREG(info.st_mode)) - nr_blocks = div_down(info.st_size, MD_BLOCK_SIZE); + if (S_ISREG(info.st_mode) && info.st_size) + nr_blocks = div_up(info.st_size, MD_BLOCK_SIZE); else if (S_ISBLK(info.st_mode)) { // To get the size of a block device we need to diff --git a/metadata_checker.cc b/metadata_checker.cc index 3d3fe1a..ed7a5d1 100644 --- a/metadata_checker.cc +++ b/metadata_checker.cc @@ -27,26 +27,30 @@ namespace { // devices having mappings defined, which can later be cross // referenced with the details tree. A separate block_counter is // used to later verify the data space map. - class mapping_validator : public btree_checker<2, block_traits> { + class mapping_validator : public btree<2, block_traits>::visitor { public: typedef boost::shared_ptr ptr; + typedef btree_checker<2, block_traits> checker; mapping_validator(block_counter &metadata_counter, block_counter &data_counter) - : btree_checker<2, block_traits>(metadata_counter), - data_counter_(data_counter) { + : checker_(metadata_counter), + data_counter_(data_counter) + { + } + + bool visit_internal(unsigned level, + bool sub_root, + optional key, + btree_detail::node_ref const &n) { + return checker_.visit_internal(level, sub_root, key, n); } - // Sharing can only occur in level 1 nodes. - // FIXME: not true once we start having held roots. bool visit_internal_leaf(unsigned level, bool sub_root, optional key, btree_detail::node_ref const &n) { - bool r = btree_checker<2, block_traits>::visit_internal_leaf(level, sub_root, key, n); - if (!r && level == 0) { - throw runtime_error("unexpected sharing in level 0 of mapping tree."); - } + bool r = checker_.visit_internal_leaf(level, sub_root, key, n); for (unsigned i = 0; i < n.get_nr_entries(); i++) devices_.insert(n.key_at(i)); @@ -58,7 +62,7 @@ namespace { bool sub_root, optional key, btree_detail::node_ref 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) for (unsigned i = 0; i < n.get_nr_entries(); i++) @@ -72,29 +76,46 @@ namespace { } private: + checker checker_; block_counter &data_counter_; set devices_; }; - class details_validator : public btree_checker<1, device_details_traits> { + class details_validator : public btree<1, device_details_traits>::visitor { public: typedef boost::shared_ptr ptr; + typedef btree_checker<1, device_details_traits> checker; details_validator(block_counter &counter) - : btree_checker<1, device_details_traits>(counter) { + : checker_(counter) { + } + + bool visit_internal(unsigned level, + bool sub_root, + optional key, + btree_detail::node_ref const &n) { + return checker_.visit_internal(level, sub_root, key, n); + } + + bool visit_internal_leaf(unsigned level, + bool sub_root, + optional key, + btree_detail::node_ref const &n) { + return checker_.visit_internal_leaf(level, sub_root, key, n); } bool visit_leaf(unsigned level, bool sub_root, optional key, btree_detail::node_ref const &n) { - bool r = btree_checker<1, device_details_traits>::visit_leaf(level, sub_root, key, n); - if (r) - for (unsigned i = 0; i < n.get_nr_entries(); i++) - devices_.insert(n.key_at(i)); + if (!checker_.visit_leaf(level, sub_root, key, n)) + return false; - return r; + for (unsigned i = 0; i < n.get_nr_entries(); i++) + devices_.insert(n.key_at(i)); + + return true; } set const &get_devices() const { @@ -102,6 +123,7 @@ namespace { } private: + checker checker_; set devices_; }; diff --git a/metadata_disk_structures.cc b/metadata_disk_structures.cc index 239a3bb..8a0fe41 100644 --- a/metadata_disk_structures.cc +++ b/metadata_disk_structures.cc @@ -46,7 +46,7 @@ void superblock_traits::unpack(superblock_disk const &disk, superblock &value) { value.csum_ = to_cpu(disk.csum_); - value.flags_ = to_cpu(disk.csum_); + value.flags_ = to_cpu(disk.flags_); value.blocknr_ = to_cpu(disk.blocknr_); ::memcpy(value.uuid_, disk.uuid_, sizeof(value.uuid_)); @@ -79,7 +79,7 @@ void superblock_traits::pack(superblock const &value, superblock_disk &disk) { 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_); ::memcpy(disk.uuid_, value.uuid_, sizeof(disk.uuid_)); diff --git a/metadata_dumper.cc b/metadata_dumper.cc index d20bac7..f73c44a 100644 --- a/metadata_dumper.cc +++ b/metadata_dumper.cc @@ -27,28 +27,48 @@ namespace { class mappings_extractor : public btree<2, block_traits>::visitor { public: typedef boost::shared_ptr ptr; + typedef btree_checker<2, block_traits> checker; mappings_extractor(uint64_t dev_id, emitter::ptr e, space_map::ptr md_sm, space_map::ptr data_sm) - : dev_id_(dev_id), + : counter_(), + checker_(counter_), + dev_id_(dev_id), e_(e), md_sm_(md_sm), data_sm_(data_sm), - in_range_(false) { + in_range_(false), + found_errors_(false) { } bool visit_internal(unsigned level, bool sub_root, boost::optional key, btree_detail::node_ref const &n) { + + if (!checker_.visit_internal(level, sub_root, key, n)) { + found_errors_ = true; + return false; + } + return (sub_root && key) ? (*key == dev_id_) : true; } bool visit_internal_leaf(unsigned level, bool sub_root, boost::optional key, btree_detail::node_ref const &n) { + if (!checker_.visit_internal_leaf(level, sub_root, key, n)) { + found_errors_ = true; + return false; + } + return true; } bool visit_leaf(unsigned level, bool sub_root, boost::optional maybe_key, btree_detail::node_ref 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++) { block_time bt = n.value_at(i); add_mapping(n.key_at(i), bt.block_, bt.time_); @@ -57,11 +77,14 @@ namespace { return true; } - void visit_complete() { end_mapping(); } + bool corruption() const { + return !checker_.get_errors()->empty(); + } + private: void start_mapping(uint64_t origin_block, uint64_t dest_block, uint32_t time) { 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_; emitter::ptr e_; space_map::ptr md_sm_; @@ -105,28 +131,34 @@ namespace { bool in_range_; uint64_t origin_start_, dest_start_, len_; uint32_t time_; - + bool found_errors_; }; class details_extractor : public btree<1, device_details_traits>::visitor { public: typedef boost::shared_ptr 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 key, btree_detail::node_ref const &n) { - return true; + return checker_.visit_internal(level, sub_root, key, n); } bool visit_internal_leaf(unsigned level, bool sub_root, boost::optional key, btree_detail::node_ref const &n) { - return true; + return checker_.visit_internal_leaf(level, sub_root, key, n); } bool visit_leaf(unsigned level, bool sub_root, boost::optional maybe_key, btree_detail::node_ref const &n) { + if (!checker_.visit_leaf(level, sub_root, maybe_key, n)) + return false; + for (unsigned i = 0; i < n.get_nr_entries(); i++) devices_.insert(make_pair(n.key_at(i), n.value_at(i))); @@ -137,7 +169,14 @@ namespace { return devices_; } + bool corruption() const { + return !checker_.get_errors()->empty(); + } + private: + // Declaration order of counter_ and checker_ is important. + block_counter counter_; + checker checker_; map devices_; }; } @@ -145,13 +184,19 @@ namespace { //---------------------------------------------------------------- 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); md->details_->visit(de); + if (de->corruption() && !repair) + throw runtime_error("corruption in device details tree"); + map const &devs = de->get_devices(); map::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_)); 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(); } diff --git a/metadata_dumper.h b/metadata_dumper.h index ec03335..c96d22e 100644 --- a/metadata_dumper.h +++ b/metadata_dumper.h @@ -25,7 +25,10 @@ //---------------------------------------------------------------- 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); } //---------------------------------------------------------------- diff --git a/mk_release b/mk_release old mode 100644 new mode 100755 diff --git a/restore_emitter.cc b/restore_emitter.cc index 2db15bd..d8dbaae 100644 --- a/restore_emitter.cc +++ b/restore_emitter.cc @@ -33,15 +33,13 @@ namespace { } virtual ~restorer() { - if (in_superblock_) { - throw runtime_error("still in superblock"); - } } virtual void begin_superblock(std::string const &uuid, uint64_t time, uint64_t trans_id, - uint32_t data_block_size) { + uint32_t data_block_size, + uint64_t nr_data_blocks) { in_superblock_ = true; superblock &sb = md_->sb_; @@ -49,6 +47,7 @@ namespace { sb.time_ = time; sb.trans_id_ = trans_id; sb.data_block_size_ = data_block_size; + md_->data_sm_->extend(nr_data_blocks); } virtual void end_superblock() { @@ -72,7 +71,7 @@ namespace { // Add entry to the details tree 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); // Insert an empty mapping tree diff --git a/space_map_disk.cc b/space_map_disk.cc index 42ca283..4c7a7ed 100644 --- a/space_map_disk.cc +++ b/space_map_disk.cc @@ -43,10 +43,10 @@ namespace { crc32c sum(BITMAP_CSUM_XOR); sum.append(&data->not_used, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(data->csum)) - throw runtime_error("bad checksum in space map bitmap"); + throw checksum_error("bad checksum in space map bitmap"); if (to_cpu(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 { @@ -75,10 +75,10 @@ namespace { crc32c sum(INDEX_CSUM_XOR); sum.append(&mi->padding_, MD_BLOCK_SIZE - sizeof(uint32_t)); if (sum.get_sum() != to_cpu(mi->csum_)) - throw runtime_error("bad checksum in metadata index block"); + throw checksum_error("bad checksum in metadata index block"); if (to_cpu(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 { diff --git a/thin_repair.cc b/thin_check.cc similarity index 56% rename from thin_repair.cc rename to thin_check.cc index 22b8ea1..abf87df 100644 --- a/thin_repair.cc +++ b/thin_check.cc @@ -29,31 +29,41 @@ using namespace std; using namespace thin_provisioning; namespace { - int check(string const &path) { - metadata::ptr md(new metadata(path, metadata::OPEN)); + int check(string const &path, bool quiet) { + try { + metadata::ptr md(new metadata(path, metadata::OPEN)); - optional maybe_errors = metadata_check(md); - if (maybe_errors) { - cerr << error_selector(*maybe_errors, 3); + optional maybe_errors = metadata_check(md); + if (maybe_errors) { + if (!quiet) + cerr << error_selector(*maybe_errors, 3); + return 1; + } + } catch (std::exception &e) { + if (!quiet) + cerr << e.what() << endl; return 1; } return 0; } - void usage(string const &cmd) { - cerr << "Usage: " << cmd << " {device|file}" << endl; - cerr << "Options:" << endl; - cerr << " {-h|--help}" << endl; - cerr << " {-V|--version}" << endl; + void usage(ostream &out, string const &cmd) { + out << "Usage: " << cmd << " [options] {device|file}" << endl + << "Options:" << endl + << " {-q|--quiet}" << endl + << " {-h|--help}" << endl + << " {-V|--version}" << endl; } } int main(int argc, char **argv) { int c; - const char shortopts[] = "hV"; + bool quiet = false; + const char shortopts[] = "qhV"; const struct option longopts[] = { + { "quiet", no_argument, NULL, 'q'}, { "help", no_argument, NULL, 'h'}, { "version", no_argument, NULL, 'V'}, { 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) { switch(c) { - case 'h': - usage(basename(argv[0])); - return 0; - case 'V': - cerr << THIN_PROVISIONING_TOOLS_VERSION << endl; - return 0; + case 'h': + usage(cout, basename(argv[0])); + return 0; + + case 'q': + quiet = true; + break; + + case 'V': + cout << THIN_PROVISIONING_TOOLS_VERSION << endl; + return 0; + + default: + usage(cerr, basename(argv[0])); + return 1; } } - if (argc != 2) { - usage(basename(argv[0])); + if (argc == optind) { + cerr << "No input file provided." << endl; + usage(cerr, basename(argv[0])); exit(1); } - return check(argv[1]); + return check(argv[optind], quiet); } diff --git a/thin_debug.cc b/thin_debug.cc new file mode 100644 index 0000000..45a3d73 --- /dev/null +++ b/thin_debug.cc @@ -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 +// . + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 strings; + + class formatter { + public: + typedef shared_ptr ptr; + + virtual ~formatter() {} + + typedef optional 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 value; + typedef tuple field_type; + + vector fields_; + }; + + template + void + field(formatter &t, string const &name, T const &value) { + t.field(name, lexical_cast(value)); + } + + //-------------------------------- + + class xml_formatter : public formatter { + public: + virtual void output(ostream &out, int depth) { + indent(depth, out); + out << "" << endl; + vector::const_iterator it; + for (it = fields_.begin(); it != fields_.end(); ++it) { + if (string const *s = get(&it->get<1>())) { + indent(depth + 1, out); + out << "get<0>() + << "\" value=\"" + << *s + << "\"/>" + << endl; + + } else { + formatter::ptr f = get(it->get<1>()); + f->output(out, depth + 1); + } + } + + indent(depth, out); + out << "" << 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 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::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 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(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 + 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(args[1]); + block_manager<>::read_ref rr = md_->tm_->read_lock(block); + + node_ref n = btree_detail::to_node(rr); + if (n.get_type() == INTERNAL) + show_node(n, out); + else { + node_ref n = btree_detail::to_node(rr); + show_node(n, out); + } + } + + private: + template + void show_node(node_ref 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(md))); + interp.register_command("m2_node", command::ptr(new show_btree_node(md))); + interp.register_command("detail_node", command::ptr(new show_btree_node(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]); +} diff --git a/thin_dump.cc b/thin_dump.cc index bf907ff..5980e2c 100644 --- a/thin_dump.cc +++ b/thin_dump.cc @@ -30,77 +30,83 @@ using namespace persistent_data; using namespace std; using namespace thin_provisioning; -//---------------------------------------------------------------- - namespace { - void dump(string const &path, string const &format) { - metadata::ptr md(new metadata(path, metadata::OPEN)); - emitter::ptr e; + int dump(string const &path, string const &format, bool repair) { + try { + metadata::ptr md(new metadata(path, metadata::OPEN)); + emitter::ptr e; - if (format == "xml") - e = create_xml_emitter(cout); - else if (format == "human_readable") - e = create_human_readable_emitter(cout); - else { - cerr << "unknown format '" << format << "'" << endl; - exit(1); + if (format == "xml") + e = create_xml_emitter(cout); + else if (format == "human_readable") + e = create_human_readable_emitter(cout); + else { + cerr << "unknown format '" << format << "'" << endl; + 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) { - cerr << "Usage: " << cmd << " [options] {device|file}" << endl << endl; - cerr << "Options:" << endl; - cerr << " {-h|--help}" << endl; - cerr << " {-f|--format} {xml|human_readable}" << endl; - cerr << " {-i|--input} {xml|human_readable} input_file" << endl; - cerr << " {-V|--version}" << endl; + void usage(ostream &out, string const &cmd) { + out << "Usage: " << cmd << " [options] {device|file}" << endl + << "Options:" << endl + << " {-h|--help}" << endl + << " {-f|--format} {xml|human_readable}" << endl + << " {-r|--repair}" << endl + << " {-V|--version}" << endl; } } int main(int argc, char **argv) { int c; - const char shortopts[] = "hf:i:V"; - string filename, format = "xml"; + bool repair = false; + const char shortopts[] = "hf:rV"; + string format = "xml"; const struct option longopts[] = { { "help", no_argument, NULL, 'h'}, { "format", required_argument, NULL, 'f' }, - { "input", required_argument, NULL, 'i'}, + { "repair", no_argument, NULL, 'r'}, { "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 'f': - format = optarg; - break; - case 'i': - filename = optarg; - break; - case 'V': - cerr << THIN_PROVISIONING_TOOLS_VERSION << endl; - return 0; + case 'h': + usage(cout, basename(argv[0])); + return 0; + + case 'f': + format = optarg; + break; + + case 'r': + repair = true; + break; + + case 'V': + cout << THIN_PROVISIONING_TOOLS_VERSION << endl; + return 0; + + default: + usage(cerr, basename(argv[0])); + return 1; } } - if (argc == 1) { - usage(basename(argv[0])); + if (argc == optind) { + cerr << "No input file provided." << endl; + usage(cerr, basename(argv[0])); return 1; } - if (filename.empty()) { - cerr << "No output file provided." << endl; - return 1; - } - - dump(filename, format); - return 0; + return dump(argv[optind], format, repair); } - -//---------------------------------------------------------------- diff --git a/thin_restore.cc b/thin_restore.cc index 789731f..a3ff53f 100644 --- a/thin_restore.cc +++ b/thin_restore.cc @@ -24,42 +24,43 @@ #include "version.h" #include -#include #include +#include #include +#include +#include +#include +#include +#include using namespace persistent_data; using namespace std; using namespace thin_provisioning; -//---------------------------------------------------------------- - namespace { - void restore(string const &backup_file, string const &dev) { - // FIXME: hard coded - block_address const NR_BLOCKS = 100000; - - metadata::ptr md(new metadata(dev, metadata::CREATE, 128, NR_BLOCKS)); - emitter::ptr restorer = create_restore_emitter(md); - ifstream in(backup_file.c_str(), ifstream::in); - // FIXME: - //try { + int restore(string const &backup_file, string const &dev) { + try { + // The block size gets updated by the restorer. + metadata::ptr md(new metadata(dev, metadata::CREATE, 128, 0)); + emitter::ptr restorer = create_restore_emitter(md); + ifstream in(backup_file.c_str(), ifstream::in); parse_xml(in, restorer); -#if 0 - } catch (...) { - in.close(); - throw; + + } catch (std::exception &e) { + cerr << e.what() << endl; + return 1; } -#endif + + return 0; } - void usage(string const &cmd) { - cerr << "Usage: " << cmd << " [options]" << endl << endl; - cerr << "Options:" << endl; - cerr << " {-h|--help}" << endl; - cerr << " {-i|--input} input_file" << endl; - cerr << " {-o [ --output} {device|file}" << endl; - cerr << " {-V|--version}" << endl; + void usage(ostream &out, string const &cmd) { + out << "Usage: " << cmd << " [options]" << endl + << "Options:" << endl + << " {-h|--help}" << endl + << " {-i|--input} input_file" << endl + << " {-o|--output} {device|file}" << endl + << " {-V|--version}" << endl; } } @@ -79,38 +80,43 @@ int main(int argc, char **argv) while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { switch(c) { case 'h': - usage(basename(argv[0])); + usage(cout, basename(argv[0])); return 0; + case 'i': input = optarg; break; + case 'o': output = optarg; break; + case 'V': - cerr << THIN_PROVISIONING_TOOLS_VERSION << endl; + cout << THIN_PROVISIONING_TOOLS_VERSION << endl; return 0; + + default: + usage(cerr, basename(argv[0])); + return 1; } } - if (argc == 1) { - usage(basename(argv[0])); + if (argc != optind) { + usage(cerr, basename(argv[0])); return 1; } if (input.empty()) { cerr << "No input file provided." << endl; + usage(cerr, basename(argv[0])); return 1; } if (output.empty()) { cerr << "No output file provided." << endl; + usage(cerr, basename(argv[0])); return 1; } - restore(input, output); - return 0; - + return restore(input, output); } - -//---------------------------------------------------------------- diff --git a/xml_format.cc b/xml_format.cc index 2809878..5e5004d 100644 --- a/xml_format.cc +++ b/xml_format.cc @@ -48,12 +48,14 @@ namespace { void begin_superblock(string const &uuid, uint64_t time, uint64_t trans_id, - uint32_t data_block_size) { + uint32_t data_block_size, + uint64_t nr_data_blocks) { indent(); out_ << "" + << " data_block_size=\"" << data_block_size << "\"" + << " nr_data_blocks=\"" << nr_data_blocks << "\">" << endl; inc(); } @@ -176,7 +178,8 @@ namespace { e->begin_superblock(get_attr(attr, "uuid"), get_attr(attr, "time"), get_attr(attr, "transaction"), - get_attr(attr, "data_block_size")); + get_attr(attr, "data_block_size"), + get_attr(attr, "nr_data_blocks")); } void parse_device(emitter *e, attributes const &attr) {