mirror of
https://github.com/mhx/dwarfs.git
synced 2025-08-04 02:06:22 -04:00
feat(dwarfsck): select detail features by name
This commit is contained in:
parent
248a16c7bb
commit
4f95d66d57
@ -937,6 +937,7 @@ if(WITH_TESTS)
|
|||||||
integral_value_parser_test
|
integral_value_parser_test
|
||||||
lazy_value_test
|
lazy_value_test
|
||||||
metadata_requirements_test
|
metadata_requirements_test
|
||||||
|
options_test
|
||||||
pcm_sample_transformer_test
|
pcm_sample_transformer_test
|
||||||
pcmaudio_categorizer_test
|
pcmaudio_categorizer_test
|
||||||
speedometer_test
|
speedometer_test
|
||||||
|
@ -18,9 +18,9 @@ with a non-zero exit code.
|
|||||||
Path to the filesystem image.
|
Path to the filesystem image.
|
||||||
|
|
||||||
- `-d`, `--detail=`*value*:
|
- `-d`, `--detail=`*value*:
|
||||||
Level of filesystem information detail. The default is 2. Higher values
|
Level of filesystem information detail. This can be a numeric level
|
||||||
mean more output. Values larger than 6 will currently not provide any
|
between 0 and 6, or a comma-separated list of feature names. The
|
||||||
further detail.
|
default corresponds to a level of 2.
|
||||||
|
|
||||||
- `-q`, `--quiet`:
|
- `-q`, `--quiet`:
|
||||||
Don't produce any output unless there is an error.
|
Don't produce any output unless there is an error.
|
||||||
|
@ -110,7 +110,12 @@ class fsinfo_features {
|
|||||||
|
|
||||||
static constexpr fsinfo_features all() { return fsinfo_features().set_all(); }
|
static constexpr fsinfo_features all() { return fsinfo_features().set_all(); }
|
||||||
|
|
||||||
|
static int max_level();
|
||||||
static fsinfo_features for_level(int level);
|
static fsinfo_features for_level(int level);
|
||||||
|
static fsinfo_features parse(std::string_view str);
|
||||||
|
|
||||||
|
std::string to_string() const;
|
||||||
|
std::vector<std::string_view> to_string_views() const;
|
||||||
|
|
||||||
constexpr bool has(fsinfo_feature f) const {
|
constexpr bool has(fsinfo_feature f) const {
|
||||||
return features_ & (1 << static_cast<size_t>(f));
|
return features_ & (1 << static_cast<size_t>(f));
|
||||||
@ -155,10 +160,11 @@ class fsinfo_features {
|
|||||||
using feature_type = uint64_t;
|
using feature_type = uint64_t;
|
||||||
static constexpr size_t max_feature_bits{
|
static constexpr size_t max_feature_bits{
|
||||||
std::numeric_limits<feature_type>::digits};
|
std::numeric_limits<feature_type>::digits};
|
||||||
|
static constexpr size_t num_feature_bits{
|
||||||
|
static_cast<size_t>(fsinfo_feature::num_fsinfo_feature_bits)};
|
||||||
|
static_assert(num_feature_bits <= max_feature_bits);
|
||||||
|
|
||||||
feature_type features_{0};
|
feature_type features_{0};
|
||||||
static_assert(static_cast<size_t>(fsinfo_feature::num_fsinfo_feature_bits) <=
|
|
||||||
max_feature_bits);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fsinfo_options {
|
struct fsinfo_options {
|
||||||
|
@ -19,11 +19,15 @@
|
|||||||
* along with dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
* along with dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include <range/v3/view/split.hpp>
|
||||||
|
|
||||||
#include <dwarfs/error.h>
|
#include <dwarfs/error.h>
|
||||||
#include <dwarfs/options.h>
|
#include <dwarfs/options.h>
|
||||||
|
|
||||||
@ -46,9 +50,36 @@ constexpr std::array level_features{
|
|||||||
{fsinfo_feature::directory_tree, fsinfo_feature::frozen_layout}),
|
{fsinfo_feature::directory_tree, fsinfo_feature::frozen_layout}),
|
||||||
/* 5 */ fsinfo_features({fsinfo_feature::chunk_details}),
|
/* 5 */ fsinfo_features({fsinfo_feature::chunk_details}),
|
||||||
/* 6 */ fsinfo_features({fsinfo_feature::metadata_full_dump}),
|
/* 6 */ fsinfo_features({fsinfo_feature::metadata_full_dump}),
|
||||||
/* 7 */ fsinfo_features({}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr std::array<std::pair<fsinfo_feature, std::string_view>,
|
||||||
|
static_cast<int>(fsinfo_feature::num_fsinfo_feature_bits)>
|
||||||
|
fsinfo_feature_names{{
|
||||||
|
#define FSINFO_FEATURE_PAIR_(f) {fsinfo_feature::f, #f}
|
||||||
|
FSINFO_FEATURE_PAIR_(version),
|
||||||
|
FSINFO_FEATURE_PAIR_(history),
|
||||||
|
FSINFO_FEATURE_PAIR_(metadata_summary),
|
||||||
|
FSINFO_FEATURE_PAIR_(metadata_details),
|
||||||
|
FSINFO_FEATURE_PAIR_(metadata_full_dump),
|
||||||
|
FSINFO_FEATURE_PAIR_(frozen_analysis),
|
||||||
|
FSINFO_FEATURE_PAIR_(frozen_layout),
|
||||||
|
FSINFO_FEATURE_PAIR_(directory_tree),
|
||||||
|
FSINFO_FEATURE_PAIR_(section_details),
|
||||||
|
FSINFO_FEATURE_PAIR_(chunk_details),
|
||||||
|
#undef FSINFO_FEATURE_PAIR_
|
||||||
|
}};
|
||||||
|
|
||||||
|
constexpr bool fsinfo_feature_names_in_order() {
|
||||||
|
for (size_t i = 0; i < fsinfo_feature_names.size(); ++i) {
|
||||||
|
if (fsinfo_feature_names[i].first != static_cast<fsinfo_feature>(i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert(fsinfo_feature_names_in_order());
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, file_order_mode mode) {
|
std::ostream& operator<<(std::ostream& os, file_order_mode mode) {
|
||||||
@ -97,6 +128,10 @@ mlock_mode parse_mlock_mode(std::string_view mode) {
|
|||||||
DWARFS_THROW(runtime_error, fmt::format("invalid lock mode: {}", mode));
|
DWARFS_THROW(runtime_error, fmt::format("invalid lock mode: {}", mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fsinfo_features::max_level() {
|
||||||
|
return static_cast<int>(level_features.size()) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
fsinfo_features fsinfo_features::for_level(int level) {
|
fsinfo_features fsinfo_features::for_level(int level) {
|
||||||
fsinfo_features features;
|
fsinfo_features features;
|
||||||
|
|
||||||
@ -109,4 +144,51 @@ fsinfo_features fsinfo_features::for_level(int level) {
|
|||||||
return features;
|
return features;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fsinfo_features fsinfo_features::parse(std::string_view features) {
|
||||||
|
fsinfo_features result;
|
||||||
|
|
||||||
|
for (auto const& f : features | ranges::views::split(',')) {
|
||||||
|
// TODO: This should be much simpler with C++23
|
||||||
|
std::string_view fsv(&*f.begin(), ranges::distance(f));
|
||||||
|
auto const it =
|
||||||
|
std::find_if(fsinfo_feature_names.begin(), fsinfo_feature_names.end(),
|
||||||
|
[&fsv](auto const& p) { return fsv == p.second; });
|
||||||
|
|
||||||
|
if (it == fsinfo_feature_names.end()) {
|
||||||
|
DWARFS_THROW(runtime_error, fmt::format("invalid feature: \"{}\"", fsv));
|
||||||
|
}
|
||||||
|
|
||||||
|
result |= it->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string fsinfo_features::to_string() const {
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
for (size_t bit = 0; bit < num_feature_bits; ++bit) {
|
||||||
|
if (has(static_cast<fsinfo_feature>(bit))) {
|
||||||
|
if (!result.empty()) {
|
||||||
|
result += ',';
|
||||||
|
}
|
||||||
|
result += fsinfo_feature_names[bit].second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string_view> fsinfo_features::to_string_views() const {
|
||||||
|
std::vector<std::string_view> result;
|
||||||
|
|
||||||
|
for (size_t bit = 0; bit < num_feature_bits; ++bit) {
|
||||||
|
if (has(static_cast<fsinfo_feature>(bit))) {
|
||||||
|
result.push_back(fsinfo_feature_names[bit].second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dwarfs
|
} // namespace dwarfs
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include <folly/portability/Unistd.h>
|
#include <folly/portability/Unistd.h>
|
||||||
|
|
||||||
#include <dwarfs/checksum.h>
|
#include <dwarfs/checksum.h>
|
||||||
|
#include <dwarfs/conv.h>
|
||||||
#include <dwarfs/error.h>
|
#include <dwarfs/error.h>
|
||||||
#include <dwarfs/file_access.h>
|
#include <dwarfs/file_access.h>
|
||||||
#include <dwarfs/filesystem_v2.h>
|
#include <dwarfs/filesystem_v2.h>
|
||||||
@ -161,12 +162,16 @@ int dwarfsck_main(int argc, sys_char** argv, iolayer const& iol) {
|
|||||||
auto algo_list = checksum::available_algorithms();
|
auto algo_list = checksum::available_algorithms();
|
||||||
auto checksum_desc = fmt::format("print checksums for all files ({})",
|
auto checksum_desc = fmt::format("print checksums for all files ({})",
|
||||||
fmt::join(algo_list, ", "));
|
fmt::join(algo_list, ", "));
|
||||||
|
auto detail_desc = fmt::format(
|
||||||
|
"detail level (0-{}, or feature list: {})", fsinfo_features::max_level(),
|
||||||
|
fmt::join(fsinfo_features::all().to_string_views(), ", "));
|
||||||
|
auto const detail_default{fsinfo_features::for_level(2).to_string()};
|
||||||
|
|
||||||
sys_string input, export_metadata;
|
sys_string input, export_metadata;
|
||||||
std::string image_offset, checksum_algo;
|
std::string image_offset, checksum_algo;
|
||||||
logger_options logopts;
|
logger_options logopts;
|
||||||
size_t num_workers;
|
size_t num_workers;
|
||||||
int detail;
|
std::string detail;
|
||||||
bool quiet{false};
|
bool quiet{false};
|
||||||
bool verbose{false};
|
bool verbose{false};
|
||||||
bool output_json{false};
|
bool output_json{false};
|
||||||
@ -182,8 +187,8 @@ int dwarfsck_main(int argc, sys_char** argv, iolayer const& iol) {
|
|||||||
po_sys_value<sys_string>(&input),
|
po_sys_value<sys_string>(&input),
|
||||||
"input filesystem")
|
"input filesystem")
|
||||||
("detail,d",
|
("detail,d",
|
||||||
po::value<int>(&detail)->default_value(2),
|
po::value<std::string>(&detail)->default_value(detail_default.c_str()),
|
||||||
"detail level")
|
detail_desc.c_str())
|
||||||
("quiet,q",
|
("quiet,q",
|
||||||
po::value<bool>(&quiet)->zero_tokens(),
|
po::value<bool>(&quiet)->zero_tokens(),
|
||||||
"don't print anything unless an error occurs")
|
"don't print anything unless an error occurs")
|
||||||
@ -327,7 +332,11 @@ int dwarfsck_main(int argc, sys_char** argv, iolayer const& iol) {
|
|||||||
|
|
||||||
opts.block_access = no_check ? block_access_level::no_verify
|
opts.block_access = no_check ? block_access_level::no_verify
|
||||||
: block_access_level::unrestricted;
|
: block_access_level::unrestricted;
|
||||||
opts.features = fsinfo_features::for_level(detail);
|
|
||||||
|
auto numeric_detail = tryTo<int>(detail);
|
||||||
|
opts.features = numeric_detail.has_value()
|
||||||
|
? fsinfo_features::for_level(*numeric_detail)
|
||||||
|
: fsinfo_features::parse(detail);
|
||||||
|
|
||||||
if (output_json) {
|
if (output_json) {
|
||||||
iol.out << fs.info_as_json(opts) << "\n";
|
iol.out << fs.info_as_json(opts) << "\n";
|
||||||
|
62
test/options_test.cpp
Normal file
62
test/options_test.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||||
|
/**
|
||||||
|
* \author Marcus Holland-Moritz (github@mhxnet.de)
|
||||||
|
* \copyright Copyright (c) Marcus Holland-Moritz
|
||||||
|
*
|
||||||
|
* This file is part of dwarfs.
|
||||||
|
*
|
||||||
|
* dwarfs 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.
|
||||||
|
*
|
||||||
|
* dwarfs 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 dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <dwarfs/options.h>
|
||||||
|
|
||||||
|
using namespace dwarfs;
|
||||||
|
|
||||||
|
TEST(options, fsinfo_features) {
|
||||||
|
fsinfo_features ff;
|
||||||
|
|
||||||
|
EXPECT_EQ(ff.to_string(), "");
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(ff |= fsinfo_features::parse("frozen_layout,history"));
|
||||||
|
|
||||||
|
EXPECT_TRUE(ff.has(fsinfo_feature::history));
|
||||||
|
EXPECT_TRUE(ff & fsinfo_feature::frozen_layout);
|
||||||
|
|
||||||
|
EXPECT_FALSE(ff.has(fsinfo_feature::frozen_analysis));
|
||||||
|
EXPECT_FALSE(ff & fsinfo_feature::version);
|
||||||
|
|
||||||
|
EXPECT_EQ(ff.to_string(), "history,frozen_layout");
|
||||||
|
|
||||||
|
ff.clear(fsinfo_feature::history);
|
||||||
|
|
||||||
|
EXPECT_FALSE(ff & fsinfo_feature::history);
|
||||||
|
EXPECT_TRUE(ff & fsinfo_feature::frozen_layout);
|
||||||
|
EXPECT_EQ(ff.to_string(), "frozen_layout");
|
||||||
|
|
||||||
|
ff.reset();
|
||||||
|
|
||||||
|
EXPECT_FALSE(ff & fsinfo_feature::frozen_layout);
|
||||||
|
EXPECT_EQ(ff.to_string(), "");
|
||||||
|
|
||||||
|
EXPECT_THAT([]() { fsinfo_features::parse("history,whatever"); },
|
||||||
|
testing::ThrowsMessage<runtime_error>(
|
||||||
|
testing::HasSubstr("invalid feature: \"whatever\"")));
|
||||||
|
|
||||||
|
EXPECT_THAT([]() { fsinfo_features::parse("frozen_layout,history,x"); },
|
||||||
|
testing::ThrowsMessage<runtime_error>(
|
||||||
|
testing::HasSubstr("invalid feature: \"x\"")));
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user