/* 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 .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace dwarfs::reader {
namespace internal {
using namespace dwarfs::internal;
namespace {
void check_section_logger(logger& lgr, fs_section const& section) {
LOG_PROXY(debug_logger_policy, lgr);
LOG_DEBUG << "section " << section.description() << " @ " << section.start()
<< " [" << section.length() << " bytes]";
if (!section.is_known_type()) {
LOG_WARN << "unknown section type " << folly::to_underlying(section.type())
<< " in section @ " << section.start();
}
if (!section.is_known_compression()) {
LOG_WARN << "unknown compression type "
<< folly::to_underlying(section.compression()) << " in section @ "
<< section.start();
}
}
template
auto call_ec_throw(Fn&& fn) {
std::error_code ec;
auto result = std::forward(fn)(ec);
if (ec) {
throw std::system_error(ec);
}
return result;
}
using section_map = std::unordered_map>;
size_t
get_uncompressed_section_size(std::shared_ptr mm, fs_section const& sec) {
if (sec.compression() == compression_type::NONE) {
return sec.length();
}
if (!sec.check_fast(*mm)) {
DWARFS_THROW(
runtime_error,
fmt::format("attempt to access damaged {} section", sec.name()));
}
std::vector tmp;
auto span = sec.data(*mm);
block_decompressor bd(sec.compression(), span.data(), span.size(), tmp);
return bd.uncompressed_size();
}
std::optional
try_get_uncompressed_section_size(std::shared_ptr mm,
fs_section const& sec) {
if (sec.check_fast(*mm)) {
try {
return get_uncompressed_section_size(mm, sec);
} catch (std::exception const&) {
}
}
return std::nullopt;
}
std::span
get_section_data(std::shared_ptr mm, fs_section const& section,
std::vector& buffer, bool force_buffer) {
DWARFS_CHECK(
section.check_fast(*mm),
fmt::format("attempt to access damaged {} section", section.name()));
auto span = section.data(*mm);
auto compression = section.compression();
if (!force_buffer && compression == compression_type::NONE) {
return span;
}
buffer =
block_decompressor::decompress(compression, span.data(), span.size());
return buffer;
}
metadata_v2
make_metadata(logger& lgr, std::shared_ptr mm,
section_map const& sections, std::vector& schema_buffer,
std::vector& meta_buffer,
const metadata_options& options, int inode_offset = 0,
bool force_buffers = false,
mlock_mode lock_mode = mlock_mode::NONE,
bool force_consistency_check = false,
std::shared_ptr perfmon = nullptr) {
LOG_PROXY(debug_logger_policy, lgr);
auto schema_it = sections.find(section_type::METADATA_V2_SCHEMA);
auto meta_it = sections.find(section_type::METADATA_V2);
if (schema_it == sections.end()) {
DWARFS_THROW(runtime_error, "no metadata schema found");
}
if (schema_it->second.size() > 1) {
DWARFS_THROW(runtime_error, "multiple metadata schemas found");
}
if (meta_it == sections.end()) {
DWARFS_THROW(runtime_error, "no metadata found");
}
if (meta_it->second.size() > 1) {
DWARFS_THROW(runtime_error, "multiple metadata found");
}
auto& meta_section = meta_it->second.front();
auto meta_section_range =
get_section_data(mm, meta_section, meta_buffer, force_buffers);
if (lock_mode != mlock_mode::NONE) {
if (auto ec = mm->lock(meta_section.start(), meta_section_range.size())) {
if (lock_mode == mlock_mode::MUST) {
DWARFS_THROW(system_error, "mlock");
} else {
LOG_WARN << "mlock() failed: " << ec.message();
}
}
}
// don't keep the compressed metadata in cache
if (meta_section.compression() != compression_type::NONE) {
if (auto ec = mm->release(meta_section.start(), meta_section.length())) {
LOG_INFO << "madvise() failed: " << ec.message();
}
}
return metadata_v2(lgr,
get_section_data(mm, schema_it->second.front(),
schema_buffer, force_buffers),
meta_section_range, options, inode_offset,
force_consistency_check, perfmon);
}
} // namespace
template
class filesystem_ final : public filesystem_v2::impl {
public:
filesystem_(logger& lgr, os_access const& os, std::shared_ptr mm,
const filesystem_options& options,
std::shared_ptr perfmon);
int check(filesystem_check_level level, size_t num_threads) 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;
void walk_data_order(
std::function const& func) const override;
std::optional find(const char* path) const override;
std::optional find(int inode) const override;
std::optional find(int inode, const char* name) const override;
file_stat getattr(inode_view entry, std::error_code& ec) const override;
file_stat getattr(inode_view entry, getattr_options const& opts,
std::error_code& ec) const override;
file_stat getattr(inode_view entry) const override;
file_stat
getattr(inode_view entry, getattr_options const& opts) const override;
bool access(inode_view entry, int mode, file_stat::uid_type uid,
file_stat::gid_type gid) const override;
void access(inode_view entry, int mode, file_stat::uid_type uid,
file_stat::gid_type gid, std::error_code& ec) const override;
std::optional opendir(inode_view entry) const override;
std::optional>
readdir(directory_view dir, size_t offset) const override;
size_t dirsize(directory_view dir) const override;
std::string readlink(inode_view entry, readlink_mode mode,
std::error_code& ec) const override;
std::string readlink(inode_view entry, readlink_mode mode) const override;
void statvfs(vfs_stat* stbuf) const override;
int open(inode_view entry) const override;
int open(inode_view entry, std::error_code& ec) const override;
std::string read_string(uint32_t inode) const override;
std::string read_string(uint32_t inode, std::error_code& ec) const override;
std::string
read_string(uint32_t inode, size_t size, file_off_t offset) const override;
std::string read_string(uint32_t inode, size_t size, file_off_t offset,
std::error_code& ec) const override;
size_t read(uint32_t inode, char* buf, size_t size,
file_off_t offset) const override;
size_t read(uint32_t inode, char* buf, size_t size, file_off_t offset,
std::error_code& ec) const override;
size_t readv(uint32_t inode, iovec_read_buf& buf) const override;
size_t readv(uint32_t inode, iovec_read_buf& buf,
std::error_code& ec) const override;
size_t readv(uint32_t inode, iovec_read_buf& buf, size_t size,
file_off_t offset, std::error_code& ec) const override;
size_t readv(uint32_t inode, iovec_read_buf& buf, size_t size,
file_off_t offset) const override;
std::vector> readv(uint32_t inode) const override;
std::vector>
readv(uint32_t inode, std::error_code& ec) const override;
std::vector>
readv(uint32_t inode, size_t size, file_off_t offset) const override;
std::vector>
readv(uint32_t inode, size_t size, file_off_t offset,
std::error_code& ec) const override;
std::optional> header() const override;
void set_num_workers(size_t num) override { ir_.set_num_workers(num); }
void set_cache_tidy_config(cache_tidy_config const& cfg) override {
ir_.set_cache_tidy_config(cfg);
}
size_t num_blocks() const override { return ir_.num_blocks(); }
bool has_symlinks() const override { return meta_.has_symlinks(); }
history const& get_history() const override { return history_; }
nlohmann::json get_inode_info(inode_view entry) const override {
return meta_.get_inode_info(entry);
}
std::vector get_all_block_categories() const override {
return meta_.get_all_block_categories();
}
std::vector get_all_uids() const override {
return meta_.get_all_uids();
}
std::vector get_all_gids() const override {
return meta_.get_all_gids();
}
std::shared_ptr get_parser() const override {
return std::make_unique(mm_, image_offset_);
}
std::optional
get_block_category(size_t block_no) const override {
return meta_.get_block_category(block_no);
}
private:
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;
size_t read_ec(uint32_t inode, char* buf, size_t size, file_off_t offset,
std::error_code& ec) const;
size_t readv_ec(uint32_t inode, iovec_read_buf& buf, size_t size,
file_off_t offset, std::error_code& ec) const;
std::vector>
readv_ec(uint32_t inode, size_t size, file_off_t offset,
std::error_code& ec) const;
LOG_PROXY_DECL(LoggerPolicy);
os_access const& os_;
std::shared_ptr mm_;
metadata_v2 meta_;
inode_reader_v2 ir_;
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_;
PERFMON_CLS_PROXY_DECL
PERFMON_CLS_TIMER_DECL(find_path)
PERFMON_CLS_TIMER_DECL(find_inode)
PERFMON_CLS_TIMER_DECL(find_inode_name)
PERFMON_CLS_TIMER_DECL(getattr)
PERFMON_CLS_TIMER_DECL(getattr_ec)
PERFMON_CLS_TIMER_DECL(getattr_opts)
PERFMON_CLS_TIMER_DECL(getattr_opts_ec)
PERFMON_CLS_TIMER_DECL(access)
PERFMON_CLS_TIMER_DECL(access_ec)
PERFMON_CLS_TIMER_DECL(opendir)
PERFMON_CLS_TIMER_DECL(readdir)
PERFMON_CLS_TIMER_DECL(dirsize)
PERFMON_CLS_TIMER_DECL(readlink)
PERFMON_CLS_TIMER_DECL(readlink_ec)
PERFMON_CLS_TIMER_DECL(statvfs)
PERFMON_CLS_TIMER_DECL(open)
PERFMON_CLS_TIMER_DECL(open_ec)
PERFMON_CLS_TIMER_DECL(read_string)
PERFMON_CLS_TIMER_DECL(read_string_ec)
PERFMON_CLS_TIMER_DECL(read)
PERFMON_CLS_TIMER_DECL(read_ec)
PERFMON_CLS_TIMER_DECL(readv_iovec)
PERFMON_CLS_TIMER_DECL(readv_iovec_ec)
PERFMON_CLS_TIMER_DECL(readv_future)
PERFMON_CLS_TIMER_DECL(readv_future_ec)
};
template
void filesystem_::check_section(fs_section const& section) const {
check_section_logger(LOG_GET_LOGGER, section);
}
template
filesystem_info const*
filesystem_::get_info(fsinfo_options const& opts) const {
std::lock_guard lock(mx_);
if (!fsinfo_ || opts.block_access > fsinfo_block_access_level_) {
filesystem_parser parser(mm_, image_offset_);
filesystem_info info;
parser.rewind();
while (auto s = parser.next_section()) {
check_section(*s);
if (s->type() == section_type::BLOCK) {
++info.block_count;
info.compressed_block_size += s->length();
info.compressed_block_sizes.push_back(s->length());
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);
}
} else if (s->type() == section_type::METADATA_V2) {
info.compressed_metadata_size += s->length();
try {
info.uncompressed_metadata_size +=
get_uncompressed_section_size(mm_, *s);
} catch (std::exception const&) {
info.uncompressed_metadata_size += s->length();
info.uncompressed_metadata_size_is_estimate = true;
}
}
}
fsinfo_ = std::make_unique(info);
fsinfo_block_access_level_ = opts.block_access;
}
return fsinfo_.get();
}
template
filesystem_::filesystem_(
logger& lgr, os_access const& os, std::shared_ptr mm,
const filesystem_options& options,
std::shared_ptr perfmon)
: LOG_PROXY_INIT(lgr)
, os_{os}
, mm_{std::move(mm)}
, history_({.with_timestamps = true})
, image_offset_{filesystem_parser::find_image_offset(
*mm_, options.image_offset)} // clang-format off
PERFMON_CLS_PROXY_INIT(perfmon, "filesystem_v2")
PERFMON_CLS_TIMER_INIT(find_path)
PERFMON_CLS_TIMER_INIT(find_inode)
PERFMON_CLS_TIMER_INIT(find_inode_name)
PERFMON_CLS_TIMER_INIT(getattr)
PERFMON_CLS_TIMER_INIT(getattr_ec)
PERFMON_CLS_TIMER_INIT(getattr_opts)
PERFMON_CLS_TIMER_INIT(getattr_opts_ec)
PERFMON_CLS_TIMER_INIT(access)
PERFMON_CLS_TIMER_INIT(access_ec)
PERFMON_CLS_TIMER_INIT(opendir)
PERFMON_CLS_TIMER_INIT(readdir)
PERFMON_CLS_TIMER_INIT(dirsize)
PERFMON_CLS_TIMER_INIT(readlink)
PERFMON_CLS_TIMER_INIT(readlink_ec)
PERFMON_CLS_TIMER_INIT(statvfs)
PERFMON_CLS_TIMER_INIT(open)
PERFMON_CLS_TIMER_INIT(open_ec)
PERFMON_CLS_TIMER_INIT(read_string)
PERFMON_CLS_TIMER_INIT(read_string_ec)
PERFMON_CLS_TIMER_INIT(read)
PERFMON_CLS_TIMER_INIT(read_ec)
PERFMON_CLS_TIMER_INIT(readv_iovec)
PERFMON_CLS_TIMER_INIT(readv_iovec_ec)
PERFMON_CLS_TIMER_INIT(readv_future)
PERFMON_CLS_TIMER_INIT(readv_future_ec) // clang-format on
{
block_cache cache(lgr, os_, mm_, options.block_cache, perfmon);
filesystem_parser parser(mm_, image_offset_);
if (parser.has_index()) {
LOG_DEBUG << "found valid section index";
}
header_ = parser.header();
section_map sections;
while (auto s = parser.next_section()) {
if (s->type() == section_type::BLOCK) {
// Don't use check_section() here because it'll trigger the lazy
// section to load, defeating the purpose of the section index.
// See github issue #183.
LOG_DEBUG << "section " << s->name() << " @ " << s->start() << " ["
<< s->length() << " bytes]";
cache.insert(*s);
} else {
check_section(*s);
if (!s->check_fast(*mm_)) {
switch (s->type()) {
case section_type::METADATA_V2:
case section_type::METADATA_V2_SCHEMA:
DWARFS_THROW(runtime_error,
"checksum error in section: " + s->name());
break;
default:
LOG_WARN << "checksum error in section: " << s->name();
break;
}
}
sections[s->type()].push_back(*s);
}
}
std::vector schema_buffer;
meta_ = make_metadata(lgr, mm_, sections, schema_buffer, meta_buffer_,
options.metadata, options.inode_offset, false,
options.lock_mode, !parser.has_checksums(), perfmon);
LOG_DEBUG << "read " << cache.block_count() << " blocks and " << meta_.size()
<< " bytes of metadata";
cache.set_block_size(meta_.block_size());
ir_ = inode_reader_v2(lgr, std::move(cache), options.inode_reader, perfmon);
if (auto it = sections.find(section_type::HISTORY); it != sections.end()) {
for (auto& section : it->second) {
if (section.check_fast(*mm_)) {
std::vector buffer;
history_.parse_append(get_section_data(mm_, section, buffer, false));
}
}
}
}
template
int filesystem_::check(filesystem_check_level level,
size_t num_threads) const {
filesystem_parser parser(mm_, image_offset_);
worker_group wg(LOG_GET_LOGGER, os_, "fscheck", num_threads);
std::vector> sections;
while (auto sp = parser.next_section()) {
check_section(*sp);
std::packaged_task task{[this, level, s = std::move(*sp)] {
if (level == filesystem_check_level::INTEGRITY ||
level == filesystem_check_level::FULL) {
if (!s.verify(*mm_)) {
DWARFS_THROW(runtime_error,
"integrity check error in section: " + s.name());
}
} else {
if (!s.check_fast(*mm_)) {
DWARFS_THROW(runtime_error, "checksum error in section: " + s.name());
}
}
return s;
}};
sections.emplace_back(task.get_future());
wg.add_job(std::move(task));
}
std::unordered_set seen;
int errors = 0;
for (auto& sf : sections) {
try {
auto s = sf.get();
if (s.type() != section_type::BLOCK &&
s.type() != section_type::HISTORY) {
if (!seen.emplace(s.type()).second) {
DWARFS_THROW(runtime_error, "duplicate section: " + s.name());
}
}
} catch (std::exception const& e) {
LOG_ERROR << exception_str(e);
++errors;
}
}
if (level == filesystem_check_level::FULL) {
try {
meta_.check_consistency();
} catch (std::exception const& e) {
LOG_ERROR << exception_str(e);
++errors;
}
}
return errors;
}
template
void filesystem_::dump(std::ostream& os,
fsinfo_options const& opts) const {
filesystem_parser parser(mm_, image_offset_);
if (opts.features.has(fsinfo_feature::version)) {
os << "DwarFS version " << parser.version();
if (auto off = parser.image_offset(); off > 0) {
os << " at offset " << off;
}
os << "\n";
}
size_t block_no{0};
if (opts.features.has(fsinfo_feature::section_details)) {
while (auto sp = parser.next_section()) {
auto const& s = *sp;
std::string block_size;
if (auto uncompressed_size = try_get_uncompressed_section_size(mm_, s)) {
float compression_ratio = float(s.length()) / uncompressed_size.value();
block_size =
fmt::format("blocksize={}, ratio={:.2f}%",
uncompressed_size.value(), 100.0 * compression_ratio);
} else {
block_size = fmt::format("blocksize={} (estimate)", s.length());
}
std::string category;
if (s.type() == section_type::BLOCK) {
if (auto catstr = meta_.get_block_category(block_no)) {
category = fmt::format(", category={}", catstr.value());
}
++block_no;
}
os << "SECTION " << s.description() << ", " << block_size << category
<< "\n";
}
}
if (opts.features.has(fsinfo_feature::history)) {
history_.dump(os);
}
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(fsinfo_options const& opts) const {
std::ostringstream oss;
dump(oss, opts);
return oss.str();
}
template
nlohmann::json
filesystem_::info_as_json(fsinfo_options const& opts) const {
filesystem_parser parser(mm_, image_offset_);
nlohmann::json info{
{"version",
{
{"major", parser.major_version()},
{"minor", parser.minor_version()},
{"header", parser.header_version()},
}},
{"image_offset", parser.image_offset()},
};
if (opts.features.has(fsinfo_feature::history)) {
info["history"] = history_.as_json();
}
if (opts.features.has(fsinfo_feature::section_details)) {
size_t block_no{0};
while (auto sp = parser.next_section()) {
auto const& s = *sp;
bool checksum_ok = s.check_fast(*mm_);
nlohmann::json section_info{
{"type", s.name()},
{"compressed_size", s.length()},
{"checksum_ok", checksum_ok},
};
if (auto uncompressed_size = try_get_uncompressed_section_size(mm_, s)) {
section_info["size"] = uncompressed_size.value();
section_info["ratio"] = float(s.length()) / uncompressed_size.value();
}
if (s.type() == section_type::BLOCK) {
if (auto catstr = meta_.get_block_category(block_no)) {
section_info["category"] = catstr.value();
}
++block_no;
}
info["sections"].push_back(std::move(section_info));
}
}
info.update(meta_.info_as_json(opts, get_info(opts)));
return info;
}
template
nlohmann::json filesystem_::metadata_as_json() const {
return meta_.as_json();
}
template
std::string
filesystem_::serialize_metadata_as_json(bool simple) const {
return meta_.serialize_as_json(simple);
}
template
void filesystem_::walk(
std::function const& func) const {
meta_.walk(func);
}
template
void filesystem_::walk_data_order(
std::function const& func) const {
meta_.walk_data_order(func);
}
template
std::optional
filesystem_::find(const char* path) const {
PERFMON_CLS_SCOPED_SECTION(find_path)
return meta_.find(path);
}
template
std::optional filesystem_::find(int inode) const {
PERFMON_CLS_SCOPED_SECTION(find_inode)
return meta_.find(inode);
}
template
std::optional
filesystem_::find(int inode, const char* name) const {
PERFMON_CLS_SCOPED_SECTION(find_inode_name)
return meta_.find(inode, name);
}
template
file_stat filesystem_::getattr(inode_view entry,
std::error_code& ec) const {
PERFMON_CLS_SCOPED_SECTION(getattr_ec)
return meta_.getattr(entry, ec);
}
template
file_stat filesystem_::getattr(inode_view entry) const {
PERFMON_CLS_SCOPED_SECTION(getattr)
return call_ec_throw(
[&](std::error_code& ec) { return meta_.getattr(entry, ec); });
}
template
file_stat filesystem_::getattr(inode_view entry,
getattr_options const& opts,
std::error_code& ec) const {
PERFMON_CLS_SCOPED_SECTION(getattr_opts_ec)
return meta_.getattr(entry, opts, ec);
}
template
file_stat
filesystem_::getattr(inode_view entry,
getattr_options const& opts) const {
PERFMON_CLS_SCOPED_SECTION(getattr_opts)
return call_ec_throw(
[&](std::error_code& ec) { return meta_.getattr(entry, opts, ec); });
}
template
bool filesystem_::access(inode_view entry, int mode,
file_stat::uid_type uid,
file_stat::gid_type gid) const {
PERFMON_CLS_SCOPED_SECTION(access)
std::error_code ec;
meta_.access(entry, mode, uid, gid, ec);
return !ec;
}
template
void filesystem_::access(inode_view entry, int mode,
file_stat::uid_type uid,
file_stat::gid_type gid,
std::error_code& ec) const {
PERFMON_CLS_SCOPED_SECTION(access_ec)
meta_.access(entry, mode, uid, gid, ec);
}
template
std::optional
filesystem_::opendir(inode_view entry) const {
PERFMON_CLS_SCOPED_SECTION(opendir)
return meta_.opendir(entry);
}
template
std::optional>
filesystem_::readdir(directory_view dir, size_t offset) const {
PERFMON_CLS_SCOPED_SECTION(readdir)
return meta_.readdir(dir, offset);
}
template
size_t filesystem_::dirsize(directory_view dir) const {
PERFMON_CLS_SCOPED_SECTION(dirsize)
return meta_.dirsize(dir);
}
template
std::string
filesystem_::readlink(inode_view entry, readlink_mode mode,
std::error_code& ec) const {
PERFMON_CLS_SCOPED_SECTION(readlink_ec)
return meta_.readlink(entry, mode, ec);
}
template
std::string filesystem_::readlink(inode_view entry,
readlink_mode mode) const {
PERFMON_CLS_SCOPED_SECTION(readlink)
return call_ec_throw(
[&](std::error_code& ec) { return meta_.readlink(entry, mode, ec); });
}
template
void filesystem_::statvfs(vfs_stat* stbuf) const {
PERFMON_CLS_SCOPED_SECTION(statvfs)
// TODO: not sure if that's the right abstraction...
meta_.statvfs(stbuf);
}
template
int filesystem_::open(inode_view entry,
std::error_code& ec) const {
PERFMON_CLS_SCOPED_SECTION(open_ec)
return meta_.open(entry, ec);
}
template
int filesystem_::open(inode_view entry) const {
PERFMON_CLS_SCOPED_SECTION(open)
return call_ec_throw(
[&](std::error_code& ec) { return meta_.open(entry, ec); });
}
template
std::string
filesystem_::read_string_ec(uint32_t inode, size_t size,
file_off_t offset,
std::error_code& ec) const {
auto chunks = meta_.get_chunks(inode, ec);
if (!ec) {
return ir_.read_string(inode, size, offset, chunks, ec);
}
return {};
}
template
std::string filesystem_::read_string(uint32_t inode,
std::error_code& ec) const {
PERFMON_CLS_SCOPED_SECTION(read_string_ec)
return read_string_ec(inode, std::numeric_limits::max(), 0, ec);
}
template
std::string filesystem_::read_string(uint32_t inode) const {
PERFMON_CLS_SCOPED_SECTION(read_string)
return call_ec_throw([&](std::error_code& ec) {
return read_string_ec(inode, std::numeric_limits::max(), 0, ec);
});
}
template
std::string filesystem_::read_string(uint32_t inode, size_t size,
file_off_t offset,
std::error_code& ec) const {
PERFMON_CLS_SCOPED_SECTION(read_string_ec)
return read_string_ec(inode, size, offset, ec);
}
template
std::string filesystem_::read_string(uint32_t inode, size_t size,
file_off_t offset) const {
PERFMON_CLS_SCOPED_SECTION(read_string)
return call_ec_throw([&](std::error_code& ec) {
return read_string_ec(inode, size, offset, ec);
});
}
template
size_t filesystem_::read_ec(uint32_t inode, char* buf,
size_t size, file_off_t offset,
std::error_code& ec) const {
auto chunks = meta_.get_chunks(inode, ec);
if (!ec) {
return ir_.read(buf, inode, size, offset, chunks, ec);
}
return 0;
}
template
size_t
filesystem_::read(uint32_t inode, char* buf, size_t size,
file_off_t offset, std::error_code& ec) const {
PERFMON_CLS_SCOPED_SECTION(read_ec)
return read_ec(inode, buf, size, offset, ec);
}
template
size_t filesystem_::read(uint32_t inode, char* buf, size_t size,
file_off_t offset) const {
PERFMON_CLS_SCOPED_SECTION(read)
return call_ec_throw([&](std::error_code& ec) {
return read_ec(inode, buf, size, offset, ec);
});
}
template
size_t filesystem_::readv_ec(uint32_t inode, iovec_read_buf& buf,
size_t size, file_off_t offset,
std::error_code& ec) const {
auto chunks = meta_.get_chunks(inode, ec);
if (!ec) {
return ir_.readv(buf, inode, size, offset, chunks, ec);
}
return 0;
}
template
size_t filesystem_::readv(uint32_t inode, iovec_read_buf& buf,
std::error_code& ec) const {
PERFMON_CLS_SCOPED_SECTION(readv_iovec_ec)
return readv_ec(inode, buf, std::numeric_limits::max(), 0, ec);
}
template
size_t
filesystem_::readv(uint32_t inode, iovec_read_buf& buf) const {
PERFMON_CLS_SCOPED_SECTION(readv_iovec)
return call_ec_throw([&](std::error_code& ec) {
return readv_ec(inode, buf, std::numeric_limits::max(), 0, ec);
});
}
template
size_t filesystem_::readv(uint32_t inode, iovec_read_buf& buf,
size_t size, file_off_t offset,
std::error_code& ec) const {
PERFMON_CLS_SCOPED_SECTION(readv_iovec_ec)
return readv_ec(inode, buf, size, offset, ec);
}
template
size_t filesystem_::readv(uint32_t inode, iovec_read_buf& buf,
size_t size, file_off_t offset) const {
PERFMON_CLS_SCOPED_SECTION(readv_iovec)
return call_ec_throw([&](std::error_code& ec) {
return readv_ec(inode, buf, size, offset, ec);
});
}
template
std::vector>
filesystem_::readv_ec(uint32_t inode, size_t size,
file_off_t offset,
std::error_code& ec) const {
auto chunks = meta_.get_chunks(inode, ec);
if (!ec) {
return ir_.readv(inode, size, offset, chunks, ec);
}
return {};
}
template
std::vector>
filesystem_::readv(uint32_t inode, std::error_code& ec) const {
PERFMON_CLS_SCOPED_SECTION(readv_future_ec)
return readv_ec(inode, std::numeric_limits::max(), 0, ec);
}
template
std::vector>
filesystem_::readv(uint32_t inode) const {
PERFMON_CLS_SCOPED_SECTION(readv_future)
return call_ec_throw([&](std::error_code& ec) {
return readv_ec(inode, std::numeric_limits::max(), 0, ec);
});
}
template
std::vector>
filesystem_::readv(uint32_t inode, size_t size, file_off_t offset,
std::error_code& ec) const {
PERFMON_CLS_SCOPED_SECTION(readv_future_ec)
return readv_ec(inode, size, offset, ec);
}
template
std::vector>
filesystem_::readv(uint32_t inode, size_t size,
file_off_t offset) const {
PERFMON_CLS_SCOPED_SECTION(readv_future)
return call_ec_throw(
[&](std::error_code& ec) { return readv_ec(inode, size, offset, ec); });
}
template
std::optional>
filesystem_::header() const {
return header_;
}
} // namespace internal
filesystem_v2::filesystem_v2(logger& lgr, os_access const& os,
std::filesystem::path const& path)
: filesystem_v2(lgr, os, os.map_file(os.canonical(path))) {}
filesystem_v2::filesystem_v2(logger& lgr, os_access const& os,
std::filesystem::path const& path,
filesystem_options const& options,
std::shared_ptr perfmon)
: filesystem_v2(lgr, os, os.map_file(os.canonical(path)), options,
std::move(perfmon)) {}
filesystem_v2::filesystem_v2(logger& lgr, os_access const& os,
std::shared_ptr mm)
: filesystem_v2(lgr, os, std::move(mm), filesystem_options()) {}
filesystem_v2::filesystem_v2(logger& lgr, os_access const& os,
std::shared_ptr mm,
const filesystem_options& options,
std::shared_ptr perfmon)
: impl_(make_unique_logging_object(
lgr, os, std::move(mm), options, std::move(perfmon))) {}
int filesystem_v2::identify(logger& lgr, os_access const& os,
std::shared_ptr mm, std::ostream& output,
int detail_level, size_t num_readers,
bool check_integrity, file_off_t image_offset) {
filesystem_options fsopts;
fsopts.metadata.enable_nlink = true;
fsopts.image_offset = image_offset;
filesystem_v2 fs(lgr, os, mm, fsopts);
auto errors = fs.check(check_integrity ? filesystem_check_level::FULL
: filesystem_check_level::CHECKSUM,
num_readers);
fs.dump(output, {.features = fsinfo_features::for_level(detail_level)});
return errors;
}
std::optional>
filesystem_v2::header(std::shared_ptr mm) {
return header(std::move(mm), filesystem_options::IMAGE_OFFSET_AUTO);
}
std::optional>
filesystem_v2::header(std::shared_ptr mm, file_off_t image_offset) {
return internal::filesystem_parser(mm, image_offset).header();
}
} // namespace dwarfs::reader