diff --git a/include/dwarfs/filesystem_v2.h b/include/dwarfs/filesystem_v2.h index 87f5b843..9b15e231 100644 --- a/include/dwarfs/filesystem_v2.h +++ b/include/dwarfs/filesystem_v2.h @@ -76,6 +76,8 @@ class filesystem_v2 { static std::optional> header(std::shared_ptr mm, file_off_t image_offset); + static fsinfo_features features_for_level(int level); + int check(filesystem_check_level level, size_t num_threads = 0) const { return impl_->check(level, num_threads); } @@ -90,6 +92,18 @@ class filesystem_v2 { return impl_->info_as_json(detail_level); } + void dump(std::ostream& os, fsinfo_options const& opts) const { + impl_->dump(os, opts); + } + + std::string dump(fsinfo_options const& opts) const { + return impl_->dump(opts); + } + + nlohmann::json info_as_json(fsinfo_options const& opts) const { + return impl_->info_as_json(opts); + } + nlohmann::json metadata_as_json() const { return impl_->metadata_as_json(); } std::string serialize_metadata_as_json(bool simple) const { @@ -304,6 +318,9 @@ class filesystem_v2 { virtual void dump(std::ostream& os, int detail_level) const = 0; virtual std::string dump(int detail_level) const = 0; virtual nlohmann::json info_as_json(int detail_level) const = 0; + virtual void dump(std::ostream& os, fsinfo_options const& opts) const = 0; + virtual std::string dump(fsinfo_options const& opts) const = 0; + virtual nlohmann::json info_as_json(fsinfo_options const& opts) const = 0; virtual nlohmann::json metadata_as_json() const = 0; virtual std::string serialize_metadata_as_json(bool simple) const = 0; virtual void diff --git a/include/dwarfs/internal/metadata_v2.h b/include/dwarfs/internal/metadata_v2.h index 124ed572..6df4389b 100644 --- a/include/dwarfs/internal/metadata_v2.h +++ b/include/dwarfs/internal/metadata_v2.h @@ -45,6 +45,7 @@ class logger; struct getattr_options; struct metadata_options; struct filesystem_info; +struct fsinfo_options; struct vfs_stat; class performance_monitor; @@ -69,14 +70,15 @@ class metadata_v2 { void check_consistency() const { impl_->check_consistency(); } void - dump(std::ostream& os, int detail_level, filesystem_info const& fsinfo, + dump(std::ostream& os, fsinfo_options const& opts, + filesystem_info const* fsinfo, std::function const& icb) const { - impl_->dump(os, detail_level, fsinfo, icb); + impl_->dump(os, opts, fsinfo, icb); } - nlohmann::json - info_as_json(int detail_level, filesystem_info const& fsinfo) const { - return impl_->info_as_json(detail_level, fsinfo); + nlohmann::json info_as_json(fsinfo_options const& opts, + filesystem_info const* fsinfo) const { + return impl_->info_as_json(opts, fsinfo); } nlohmann::json as_json() const { return impl_->as_json(); } @@ -176,11 +178,13 @@ class metadata_v2 { virtual void check_consistency() const = 0; virtual void dump( - std::ostream& os, int detail_level, filesystem_info const& fsinfo, + std::ostream& os, fsinfo_options const& opts, + filesystem_info const* fsinfo, std::function const& icb) const = 0; virtual nlohmann::json - info_as_json(int detail_level, filesystem_info const& fsinfo) const = 0; + info_as_json(fsinfo_options const& opts, + filesystem_info const* fsinfo) const = 0; virtual nlohmann::json as_json() const = 0; virtual std::string serialize_as_json(bool simple) const = 0; diff --git a/include/dwarfs/options.h b/include/dwarfs/options.h index 758268a7..0a8a05dd 100644 --- a/include/dwarfs/options.h +++ b/include/dwarfs/options.h @@ -24,9 +24,14 @@ #include #include #include +#include #include +#include #include #include +#include +#include +#include #include #include @@ -69,6 +74,78 @@ struct getattr_options { bool no_size{false}; }; +enum class block_access_level { + no_access, + no_verify, + unrestricted, +}; + +inline auto operator<=>(block_access_level lhs, block_access_level rhs) { + return static_cast>(lhs) <=> + static_cast>(rhs); +} + +enum class fsinfo_feature { + version, + history, + metadata_summary, + metadata_details, + metadata_full_dump, + frozen_analysis, + frozen_layout, + directory_tree, + section_details, + chunk_details, + num_fsinfo_feature_bits, +}; + +class fsinfo_features { + public: + constexpr fsinfo_features() = default; + constexpr fsinfo_features(std::initializer_list features) { + for (auto f : features) { + set(f); + } + } + + constexpr bool has(fsinfo_feature f) const { + return features_ & (1 << static_cast(f)); + } + + constexpr void set(fsinfo_feature f) { + features_ |= (1 << static_cast(f)); + } + + constexpr void clear(fsinfo_feature f) { + features_ &= ~(1 << static_cast(f)); + } + + constexpr void reset() { features_ = 0; } + + constexpr fsinfo_features& operator|=(fsinfo_features const& other) { + features_ |= other.features_; + return *this; + } + + constexpr fsinfo_features& operator|=(fsinfo_feature f) { + set(f); + return *this; + } + + constexpr bool operator&(fsinfo_feature f) const { return has(f); } + + private: + // can be upgraded to std::bitset if needed and when it's constexpr + uint64_t features_{0}; + static_assert(static_cast(fsinfo_feature::num_fsinfo_feature_bits) <= + std::numeric_limits::digits); +}; + +struct fsinfo_options { + fsinfo_features features; + block_access_level block_access{block_access_level::unrestricted}; +}; + struct metadata_options { bool enable_nlink{false}; bool readonly{false}; diff --git a/src/dwarfs/filesystem_v2.cpp b/src/dwarfs/filesystem_v2.cpp index 407edfae..060d1520 100644 --- a/src/dwarfs/filesystem_v2.cpp +++ b/src/dwarfs/filesystem_v2.cpp @@ -61,6 +61,28 @@ namespace internal { namespace { +constexpr std::array level_features{ + /* 0 */ fsinfo_features(), + /* 1 */ + fsinfo_features( + {fsinfo_feature::version, fsinfo_feature::metadata_summary}), + /* 2 */ + fsinfo_features({fsinfo_feature::frozen_analysis, fsinfo_feature::history}), + /* 3 */ + fsinfo_features( + {fsinfo_feature::metadata_details, fsinfo_feature::section_details}), + /* 4 */ + fsinfo_features( + {fsinfo_feature::directory_tree, fsinfo_feature::frozen_layout}), + /* 5 */ fsinfo_features({fsinfo_feature::chunk_details}), + /* 6 */ fsinfo_features({fsinfo_feature::metadata_full_dump}), + /* 7 */ fsinfo_features({}), +}; + +fsinfo_options fsinfo_opts_for_level(int level) { + return fsinfo_options{.features = filesystem_v2::features_for_level(level)}; +} + void check_section_logger(logger& lgr, fs_section const& section) { LOG_PROXY(debug_logger_policy, lgr); @@ -410,6 +432,9 @@ class filesystem_ final : public filesystem_v2::impl { void dump(std::ostream& os, int detail_level) const override; std::string dump(int detail_level) const override; nlohmann::json info_as_json(int detail_level) const override; + void dump(std::ostream& os, fsinfo_options const& opts) const override; + std::string dump(fsinfo_options const& opts) const override; + nlohmann::json info_as_json(fsinfo_options const& opts) const override; nlohmann::json metadata_as_json() const override; std::string serialize_metadata_as_json(bool simple) const override; void walk(std::function const& func) const override; @@ -488,7 +513,7 @@ class filesystem_ final : public filesystem_v2::impl { rewrite_options const& opts) const override; private: - filesystem_info const& get_info() const; + filesystem_info const* get_info(fsinfo_options const& opts) const; void check_section(fs_section const& section) const; std::string read_string_ec(uint32_t inode, size_t size, file_off_t offset, std::error_code& ec) const; @@ -508,6 +533,8 @@ class filesystem_ final : public filesystem_v2::impl { mutable std::mutex mx_; std::vector meta_buffer_; std::optional> header_; + mutable block_access_level fsinfo_block_access_level_{ + block_access_level::no_access}; mutable std::unique_ptr fsinfo_; history history_; file_off_t const image_offset_; @@ -545,10 +572,11 @@ void filesystem_::check_section(fs_section const& section) const { } template -filesystem_info const& filesystem_::get_info() const { +filesystem_info const* +filesystem_::get_info(fsinfo_options const& opts) const { std::lock_guard lock(mx_); - if (!fsinfo_) { + if (!fsinfo_ || opts.block_access > fsinfo_block_access_level_) { filesystem_parser parser(mm_, image_offset_); filesystem_info info; @@ -561,11 +589,17 @@ filesystem_info const& filesystem_::get_info() const { ++info.block_count; info.compressed_block_size += s->length(); info.compressed_block_sizes.push_back(s->length()); - try { - auto uncompressed_size = get_uncompressed_section_size(mm_, *s); - info.uncompressed_block_size += uncompressed_size; - info.uncompressed_block_sizes.push_back(uncompressed_size); - } catch (std::exception const&) { + if (opts.block_access >= block_access_level::unrestricted) { + try { + auto uncompressed_size = get_uncompressed_section_size(mm_, *s); + info.uncompressed_block_size += uncompressed_size; + info.uncompressed_block_sizes.push_back(uncompressed_size); + } catch (std::exception const&) { + info.uncompressed_block_size += s->length(); + info.uncompressed_block_size_is_estimate = true; + info.uncompressed_block_sizes.push_back(std::nullopt); + } + } else { info.uncompressed_block_size += s->length(); info.uncompressed_block_size_is_estimate = true; info.uncompressed_block_sizes.push_back(std::nullopt); @@ -583,9 +617,10 @@ filesystem_info const& filesystem_::get_info() const { } fsinfo_ = std::make_unique(info); + fsinfo_block_access_level_ = opts.block_access; } - return *fsinfo_; + return fsinfo_.get(); } template @@ -921,9 +956,25 @@ int filesystem_::check(filesystem_check_level level, template void filesystem_::dump(std::ostream& os, int detail_level) const { + dump(os, fsinfo_opts_for_level(detail_level)); +} + +template +std::string filesystem_::dump(int detail_level) const { + return dump(fsinfo_opts_for_level(detail_level)); +} + +template +nlohmann::json filesystem_::info_as_json(int detail_level) const { + return info_as_json(fsinfo_opts_for_level(detail_level)); +} + +template +void filesystem_::dump(std::ostream& os, + fsinfo_options const& opts) const { filesystem_parser parser(mm_, image_offset_); - if (detail_level > 0) { + if (opts.features.has(fsinfo_feature::version)) { os << "DwarFS version " << parser.version(); if (auto off = parser.image_offset(); off > 0) { os << " at offset " << off; @@ -933,7 +984,7 @@ void filesystem_::dump(std::ostream& os, int detail_level) const { size_t block_no{0}; - if (detail_level > 2) { + if (opts.features.has(fsinfo_feature::section_details)) { while (auto sp = parser.next_section()) { auto const& s = *sp; @@ -962,34 +1013,34 @@ void filesystem_::dump(std::ostream& os, int detail_level) const { } } - if (detail_level > 1) { + if (opts.features.has(fsinfo_feature::history)) { history_.dump(os); } - meta_.dump(os, detail_level, get_info(), - [&](const std::string& indent, uint32_t inode) { - std::error_code ec; - auto chunks = meta_.get_chunks(inode, ec); - if (!ec) { - os << indent << chunks.size() << " chunks in inode " << inode - << "\n"; - ir_.dump(os, indent + " ", chunks); - } else { - LOG_ERROR << "error reading chunks for inode " << inode << ": " - << ec.message(); - } - }); + meta_.dump( + os, opts, get_info(opts), [&](const std::string& indent, uint32_t inode) { + std::error_code ec; + auto chunks = meta_.get_chunks(inode, ec); + if (!ec) { + os << indent << chunks.size() << " chunks in inode " << inode << "\n"; + ir_.dump(os, indent + " ", chunks); + } else { + LOG_ERROR << "error reading chunks for inode " << inode << ": " + << ec.message(); + } + }); } template -std::string filesystem_::dump(int detail_level) const { +std::string filesystem_::dump(fsinfo_options const& opts) const { std::ostringstream oss; - dump(oss, detail_level); + dump(oss, opts); return oss.str(); } template -nlohmann::json filesystem_::info_as_json(int detail_level) const { +nlohmann::json +filesystem_::info_as_json(fsinfo_options const& opts) const { filesystem_parser parser(mm_, image_offset_); nlohmann::json info{ @@ -1002,11 +1053,11 @@ nlohmann::json filesystem_::info_as_json(int detail_level) const { {"image_offset", parser.image_offset()}, }; - if (detail_level > 1) { + if (opts.features.has(fsinfo_feature::history)) { info["history"] = history_.as_json(); } - if (detail_level > 2) { + if (opts.features.has(fsinfo_feature::section_details)) { size_t block_no{0}; while (auto sp = parser.next_section()) { @@ -1036,7 +1087,7 @@ nlohmann::json filesystem_::info_as_json(int detail_level) const { } } - info.update(meta_.info_as_json(detail_level, get_info())); + info.update(meta_.info_as_json(opts, get_info(opts))); return info; } @@ -1400,4 +1451,16 @@ filesystem_v2::header(std::shared_ptr mm, file_off_t image_offset) { return internal::filesystem_parser(mm, image_offset).header(); } +fsinfo_features filesystem_v2::features_for_level(int level) { + fsinfo_features features; + + level = std::min(level, internal::level_features.size() - 1); + + for (int i = 0; i <= level; ++i) { + features |= internal::level_features[i]; + } + + return features; +} + } // namespace dwarfs diff --git a/src/dwarfs/internal/metadata_v2.cpp b/src/dwarfs/internal/metadata_v2.cpp index 21011e17..2a52d738 100644 --- a/src/dwarfs/internal/metadata_v2.cpp +++ b/src/dwarfs/internal/metadata_v2.cpp @@ -140,7 +140,7 @@ check_metadata_consistency(logger& lgr, global_metadata::Meta const& meta, void analyze_frozen(std::ostream& os, MappedFrozen const& meta, - size_t total_size, int detail) { + size_t total_size, fsinfo_options const& opts) { using namespace ::apache::thrift::frozen; null_logger lgr; @@ -305,7 +305,7 @@ void analyze_frozen(std::ostream& os, os << u.second; } - if (detail > 3) { + if (opts.features.has(fsinfo_feature::frozen_layout)) { l->print(os, 0); os << '\n'; } @@ -332,25 +332,31 @@ void parse_metadata_options( struct category_info { size_t count{0}; - size_t compressed_size{0}; - size_t uncompressed_size{0}; + std::optional compressed_size; + std::optional uncompressed_size; bool uncompressed_size_is_estimate{false}; }; std::map get_category_info(MappedFrozen const& meta, - filesystem_info const& fsinfo) { + filesystem_info const* fsinfo) { std::map catinfo; if (auto blockcat = meta.block_categories()) { for (auto [block, category] : ranges::views::enumerate(blockcat.value())) { auto& ci = catinfo[category]; ++ci.count; - ci.compressed_size += fsinfo.compressed_block_sizes.at(block); - if (auto size = fsinfo.uncompressed_block_sizes.at(block)) { - ci.uncompressed_size += *size; - } else { - ci.uncompressed_size_is_estimate = true; + if (fsinfo) { + if (!ci.compressed_size) { + ci.compressed_size = 0; + ci.uncompressed_size = 0; + } + *ci.compressed_size += fsinfo->compressed_block_sizes.at(block); + if (auto size = fsinfo->uncompressed_block_sizes.at(block)) { + *ci.uncompressed_size += *size; + } else { + ci.uncompressed_size_is_estimate = true; + } } } } @@ -457,12 +463,13 @@ class metadata_ final : public metadata_v2::impl { void check_consistency() const override; - void dump(std::ostream& os, int detail_level, filesystem_info const& fsinfo, + void dump(std::ostream& os, fsinfo_options const& opts, + filesystem_info const* fsinfo, std::function const& icb) const override; - nlohmann::json - info_as_json(int detail_level, filesystem_info const& fsinfo) const override; + nlohmann::json info_as_json(fsinfo_options const& opts, + filesystem_info const* fsinfo) const override; nlohmann::json as_json() const override; std::string serialize_as_json(bool simple) const override; @@ -610,10 +617,10 @@ class metadata_ final : public metadata_v2::impl { // TODO: see if we really need to pass the extra dir_entry_view in // addition to directory_view void dump(std::ostream& os, const std::string& indent, dir_entry_view entry, - int detail_level, + fsinfo_options const& opts, std::function const& icb) const; void dump(std::ostream& os, const std::string& indent, directory_view dir, - dir_entry_view entry, int detail_level, + dir_entry_view entry, fsinfo_options const& opts, std::function const& icb) const; nlohmann::json as_json(dir_entry_view entry) const; @@ -913,7 +920,7 @@ void metadata_::check_consistency() const { template void metadata_::dump( std::ostream& os, const std::string& indent, dir_entry_view entry, - int detail_level, + fsinfo_options const& opts, std::function const& icb) const { auto iv = entry.inode(); auto mode = iv.mode(); @@ -933,14 +940,13 @@ void metadata_::dump( fmt::format("get_chunk_range({}): {}", inode, ec.message())); os << " [" << cr.begin_ << ", " << cr.end_ << "]"; os << " " << file_size(iv, mode) << "\n"; - if (detail_level > 4) { + if (opts.features.has(fsinfo_feature::chunk_details)) { icb(indent + " ", inode); } } break; case posix_file_type::directory: - dump(os, indent + " ", make_directory_view(iv), entry, detail_level, - std::move(icb)); + dump(os, indent + " ", make_directory_view(iv), entry, opts, icb); break; case posix_file_type::symlink: @@ -967,8 +973,8 @@ void metadata_::dump( template nlohmann::json -metadata_::info_as_json(int detail_level, - filesystem_info const& fsinfo) const { +metadata_::info_as_json(fsinfo_options const& opts, + filesystem_info const* fsinfo) const { nlohmann::json info; vfs_stat stbuf; statvfs(&stbuf); @@ -982,21 +988,25 @@ metadata_::info_as_json(int detail_level, fmt::format("{:%Y-%m-%dT%H:%M:%S}", fmt::localtime(ts.value())); } - if (detail_level > 0) { + if (opts.features.has(fsinfo_feature::metadata_summary)) { info["block_size"] = meta_.block_size(); - info["block_count"] = fsinfo.block_count; + if (fsinfo) { + info["block_count"] = fsinfo->block_count; + } info["inode_count"] = stbuf.files; if (auto ps = meta_.preferred_path_separator()) { info["preferred_path_separator"] = std::string(1, static_cast(*ps)); } info["original_filesystem_size"] = stbuf.blocks; - info["compressed_block_size"] = fsinfo.compressed_block_size; - if (!fsinfo.uncompressed_block_size_is_estimate) { - info["uncompressed_block_size"] = fsinfo.uncompressed_block_size; - } - info["compressed_metadata_size"] = fsinfo.compressed_metadata_size; - if (!fsinfo.uncompressed_metadata_size_is_estimate) { - info["uncompressed_metadata_size"] = fsinfo.uncompressed_metadata_size; + if (fsinfo) { + info["compressed_block_size"] = fsinfo->compressed_block_size; + if (!fsinfo->uncompressed_block_size_is_estimate) { + info["uncompressed_block_size"] = fsinfo->uncompressed_block_size; + } + info["compressed_metadata_size"] = fsinfo->compressed_metadata_size; + if (!fsinfo->uncompressed_metadata_size_is_estimate) { + info["uncompressed_metadata_size"] = fsinfo->uncompressed_metadata_size; + } } if (auto opt = meta_.options()) { @@ -1020,16 +1030,18 @@ metadata_::info_as_json(int detail_level, std::string name{catnames[category]}; categories[name] = { {"block_count", ci.count}, - {"compressed_size", ci.compressed_size}, }; - if (!ci.uncompressed_size_is_estimate) { - categories[name]["uncompressed_size"] = ci.uncompressed_size; + if (ci.compressed_size) { + categories[name]["compressed_size"] = ci.compressed_size.value(); + } + if (ci.uncompressed_size && !ci.uncompressed_size_is_estimate) { + categories[name]["uncompressed_size"] = ci.uncompressed_size.value(); } } } } - if (detail_level > 2) { + if (opts.features.has(fsinfo_feature::metadata_details)) { nlohmann::json meta; meta["symlink_inode_offset"] = symlink_inode_offset_; @@ -1068,7 +1080,7 @@ metadata_::info_as_json(int detail_level, info["meta"] = std::move(meta); } - if (detail_level > 3) { + if (opts.features.has(fsinfo_feature::directory_tree)) { info["root"] = as_json(root_); } @@ -1079,7 +1091,7 @@ metadata_::info_as_json(int detail_level, template void metadata_::dump( std::ostream& os, const std::string& indent, directory_view dir, - dir_entry_view entry, int detail_level, + dir_entry_view entry, fsinfo_options const& opts, std::function const& icb) const { auto count = dir.entry_count(); auto first = dir.first_entry(); @@ -1087,14 +1099,14 @@ void metadata_::dump( os << " (" << count << " entries, parent=" << dir.parent_entry() << ")\n"; for (size_t i = 0; i < count; ++i) { - dump(os, indent, make_dir_entry_view(first + i, entry.self_index()), - detail_level, icb); + dump(os, indent, make_dir_entry_view(first + i, entry.self_index()), opts, + icb); } } template void metadata_::dump( - std::ostream& os, int detail_level, filesystem_info const& fsinfo, + std::ostream& os, fsinfo_options const& opts, filesystem_info const* fsinfo, std::function const& icb) const { vfs_stat stbuf; statvfs(&stbuf); @@ -1111,39 +1123,44 @@ void metadata_::dump( os << "created on: " << str << "\n"; } - if (detail_level > 0) { + if (opts.features.has(fsinfo_feature::metadata_summary)) { os << "block size: " << size_with_unit(stbuf.bsize) << "\n"; - os << "block count: " << fsinfo.block_count << "\n"; + if (fsinfo) { + os << "block count: " << fsinfo->block_count << "\n"; + } os << "inode count: " << stbuf.files << "\n"; if (auto ps = meta_.preferred_path_separator()) { os << "preferred path separator: " << static_cast(*ps) << "\n"; } os << "original filesystem size: " << size_with_unit(stbuf.blocks) << "\n"; - os << "compressed block size: " - << size_with_unit(fsinfo.compressed_block_size); - if (!fsinfo.uncompressed_block_size_is_estimate) { - os << fmt::format(" ({0:.2f}%)", (100.0 * fsinfo.compressed_block_size) / - fsinfo.uncompressed_block_size); + if (fsinfo) { + os << "compressed block size: " + << size_with_unit(fsinfo->compressed_block_size); + if (!fsinfo->uncompressed_block_size_is_estimate) { + os << fmt::format(" ({0:.2f}%)", + (100.0 * fsinfo->compressed_block_size) / + fsinfo->uncompressed_block_size); + } + os << "\n"; + os << "uncompressed block size: "; + if (fsinfo->uncompressed_block_size_is_estimate) { + os << "(at least) "; + } + os << size_with_unit(fsinfo->uncompressed_block_size) << "\n"; + os << "compressed metadata size: " + << size_with_unit(fsinfo->compressed_metadata_size); + if (!fsinfo->uncompressed_metadata_size_is_estimate) { + os << fmt::format(" ({0:.2f}%)", + (100.0 * fsinfo->compressed_metadata_size) / + fsinfo->uncompressed_metadata_size); + } + os << "\n"; + os << "uncompressed metadata size: "; + if (fsinfo->uncompressed_metadata_size_is_estimate) { + os << "(at least) "; + } + os << size_with_unit(fsinfo->uncompressed_metadata_size) << "\n"; } - os << "\n"; - os << "uncompressed block size: "; - if (fsinfo.uncompressed_block_size_is_estimate) { - os << "(at least) "; - } - os << size_with_unit(fsinfo.uncompressed_block_size) << "\n"; - os << "compressed metadata size: " - << size_with_unit(fsinfo.compressed_metadata_size); - if (!fsinfo.uncompressed_metadata_size_is_estimate) { - os << fmt::format(" ({0:.2f}%)", - (100.0 * fsinfo.compressed_metadata_size) / - fsinfo.uncompressed_metadata_size); - } - os << "\n"; - os << "uncompressed metadata size: "; - if (fsinfo.uncompressed_metadata_size_is_estimate) { - os << "(at least) "; - } - os << size_with_unit(fsinfo.uncompressed_metadata_size) << "\n"; if (auto opt = meta_.options()) { std::vector options; parse_metadata_options(meta_, [&](auto const& name, bool value) { @@ -1163,15 +1180,20 @@ void metadata_::dump( os << "categories:\n"; for (auto const& [category, ci] : catinfo) { os << " " << catnames[category] << ": " << ci.count << " blocks"; - if (ci.uncompressed_size_is_estimate || - ci.uncompressed_size != ci.compressed_size) { - os << ", " << size_with_unit(ci.compressed_size) << " compressed"; - } - if (!ci.uncompressed_size_is_estimate) { - os << ", " << size_with_unit(ci.uncompressed_size) << " uncompressed"; - if (ci.uncompressed_size != ci.compressed_size) { - os << fmt::format(" ({0:.2f}%)", (100.0 * ci.compressed_size) / - ci.uncompressed_size); + if (ci.compressed_size) { + if (ci.uncompressed_size_is_estimate || + ci.uncompressed_size.value() != ci.compressed_size.value()) { + os << ", " << size_with_unit(ci.compressed_size.value()) + << " compressed"; + } + if (!ci.uncompressed_size_is_estimate) { + os << ", " << size_with_unit(ci.uncompressed_size.value()) + << " uncompressed"; + if (ci.uncompressed_size.value() != ci.compressed_size.value()) { + os << fmt::format(" ({0:.2f}%)", + (100.0 * ci.compressed_size.value()) / + ci.uncompressed_size.value()); + } } } os << "\n"; @@ -1179,11 +1201,11 @@ void metadata_::dump( } } - if (detail_level > 1) { - analyze_frozen(os, meta_, data_.size(), detail_level); + if (opts.features.has(fsinfo_feature::frozen_analysis)) { + analyze_frozen(os, meta_, data_.size(), opts); } - if (detail_level > 2) { + if (opts.features.has(fsinfo_feature::metadata_details)) { os << "symlink_inode_offset: " << symlink_inode_offset_ << "\n"; os << "file_inode_offset: " << file_inode_offset_ << "\n"; os << "dev_inode_offset: " << dev_inode_offset_ << "\n"; @@ -1216,12 +1238,12 @@ void metadata_::dump( analyze_chunks(os); } - if (detail_level > 5) { + if (opts.features.has(fsinfo_feature::metadata_full_dump)) { os << ::apache::thrift::debugString(meta_.thaw()) << '\n'; } - if (detail_level > 3) { - dump(os, "", root_, detail_level, icb); + if (opts.features.has(fsinfo_feature::directory_tree)) { + dump(os, "", root_, opts, icb); } } diff --git a/src/dwarfsck_main.cpp b/src/dwarfsck_main.cpp index 3be1de9c..1deec7c4 100644 --- a/src/dwarfsck_main.cpp +++ b/src/dwarfsck_main.cpp @@ -323,10 +323,16 @@ int dwarfsck_main(int argc, sys_char** argv, iolayer const& iol) { auto errors = no_check ? 0 : fs.check(level, num_workers); if (!quiet && !list_files && checksum_algo.empty()) { + fsinfo_options opts; + + opts.block_access = no_check ? block_access_level::no_verify + : block_access_level::unrestricted; + opts.features = filesystem_v2::features_for_level(detail); + if (output_json) { - iol.out << fs.info_as_json(detail) << "\n"; + iol.out << fs.info_as_json(opts) << "\n"; } else { - fs.dump(iol.out, detail); + fs.dump(iol.out, opts); } } diff --git a/test/tools_test.cpp b/test/tools_test.cpp index 64745b91..53ceb04f 100644 --- a/test/tools_test.cpp +++ b/test/tools_test.cpp @@ -1006,8 +1006,10 @@ TEST_P(tools_test, end_to_end) { for (auto const& [path, ref] : xattr_tests) { EXPECT_EQ(dwarfs::listxattr(path), ref) << runner.cmdline(); - auto info = - nlohmann::json::parse(dwarfs::getxattr(path, kInodeInfoXattr)); + auto xattr = dwarfs::getxattr(path, kInodeInfoXattr); + nlohmann::json info; + EXPECT_NO_THROW(info = nlohmann::json::parse(xattr)) + << runner.cmdline() << ", " << xattr; EXPECT_TRUE(info.count("uid")); EXPECT_TRUE(info.count("gid")); EXPECT_TRUE(info.count("mode")); @@ -1556,7 +1558,8 @@ TEST_P(tools_test, categorize) { image_recompressed, "--json"); ASSERT_TRUE(json_info); - auto info = nlohmann::json::parse(*json_info); + nlohmann::json info; + EXPECT_NO_THROW(info = nlohmann::json::parse(*json_info)) << *json_info; EXPECT_EQ(info["block_size"], 65'536); EXPECT_EQ(info["image_offset"], 0);