refactor: delay file_stat errors

This commit is contained in:
Marcus Holland-Moritz 2024-07-31 18:27:21 +02:00
parent df6ff2ccbd
commit 1dfeb2e7f9
6 changed files with 187 additions and 66 deletions

View File

@ -26,11 +26,13 @@
#include <memory>
#include <type_traits>
#include <dwarfs/error.h>
#include <dwarfs/file_type.h>
namespace dwarfs {
struct file_stat {
using valid_fields_type = uint32_t;
using perms_type = std::underlying_type_t<std::filesystem::perms>;
using mode_type = uint32_t;
using dev_type = uint64_t;
@ -43,15 +45,22 @@ struct file_stat {
using blkcnt_type = int64_t;
using time_type = int64_t;
void ensure_valid(valid_fields_type fields) const;
std::filesystem::file_status status() const {
ensure_valid(mode_valid);
return file_mode_to_status(mode);
};
posix_file_type::value type() const {
ensure_valid(mode_valid);
return static_cast<posix_file_type::value>(mode & posix_file_type::mask);
};
perms_type permissions() const { return mode & 07777; };
perms_type permissions() const {
ensure_valid(mode_valid);
return mode & 07777;
};
void set_permissions(perms_type perms) { mode = type() | (perms & 07777); }
@ -69,6 +78,22 @@ struct file_stat {
static std::string perm_string(mode_type mode);
static std::string mode_string(mode_type mode);
static constexpr valid_fields_type dev_valid = 1 << 0;
static constexpr valid_fields_type ino_valid = 1 << 1;
static constexpr valid_fields_type nlink_valid = 1 << 2;
static constexpr valid_fields_type mode_valid = 1 << 3;
static constexpr valid_fields_type uid_valid = 1 << 4;
static constexpr valid_fields_type gid_valid = 1 << 5;
static constexpr valid_fields_type rdev_valid = 1 << 6;
static constexpr valid_fields_type size_valid = 1 << 7;
static constexpr valid_fields_type blksize_valid = 1 << 8;
static constexpr valid_fields_type blocks_valid = 1 << 9;
static constexpr valid_fields_type atime_valid = 1 << 10;
static constexpr valid_fields_type mtime_valid = 1 << 11;
static constexpr valid_fields_type ctime_valid = 1 << 12;
static constexpr valid_fields_type all_valid = (1 << 13) - 1;
uint32_t valid_fields{0};
dev_type dev;
ino_type ino;
nlink_type nlink;
@ -82,6 +107,7 @@ struct file_stat {
time_type atime;
time_type mtime;
time_type ctime;
std::exception_ptr exception;
};
file_stat make_file_stat(std::filesystem::path const& path);

View File

@ -84,7 +84,7 @@ class entry : public entry_interface {
std::string unix_dpath() const override;
std::string const& name() const override { return name_; }
bool less_revpath(entry const& rhs) const;
size_t size() const override { return stat_.size; }
size_t size() const override;
virtual type_t type() const = 0;
bool is_directory() const override;
virtual void walk(std::function<void(entry*)> const& f);
@ -97,8 +97,8 @@ class entry : public entry_interface {
file_stat const& status() const { return stat_; }
void set_entry_index(uint32_t index) { entry_index_ = index; }
std::optional<uint32_t> const& entry_index() const { return entry_index_; }
uint64_t raw_inode_num() const { return stat_.ino; }
uint64_t num_hard_links() const { return stat_.nlink; }
uint64_t raw_inode_num() const;
uint64_t num_hard_links() const;
virtual void set_inode_num(uint32_t ino) = 0;
virtual std::optional<uint32_t> const& inode_num() const = 0;
@ -116,7 +116,7 @@ class entry : public entry_interface {
uint64_t get_ctime() const override;
void set_ctime(uint64_t ctime) override;
void override_size(size_t size) { stat_.size = size; }
void override_size(size_t size);
private:
std::u8string u8name() const;

View File

@ -101,7 +101,19 @@ void perms_to_stream(std::ostream& os, file_stat::mode_type mode) {
#ifdef _WIN32
file_stat make_file_stat(fs::path const& path) {
auto status = fs::symlink_status(path);
std::error_code ec;
auto status = fs::symlink_status(path, ec);
if (ec) {
status = fs::status(path, ec);
}
file_stat rv;
if (ec) {
rv.exception = std::make_exception_ptr(std::system_error(ec));
return rv;
}
if (status.type() == fs::file_type::not_found ||
status.type() == fs::file_type::unknown) {
@ -113,7 +125,7 @@ file_stat make_file_stat(fs::path const& path) {
u8string_to_string(path.u8string())));
}
file_stat rv;
rv.valid_fields = file_stat::mode_valid;
rv.mode = file_status_to_mode(status);
rv.blksize = 0;
rv.blocks = 0;
@ -124,64 +136,75 @@ file_stat make_file_stat(fs::path const& path) {
::WIN32_FILE_ATTRIBUTE_DATA info;
if (::GetFileAttributesExW(wps.c_str(), GetFileExInfoStandard, &info) ==
0) {
throw std::system_error(::GetLastError(), std::system_category(),
"GetFileAttributesExW");
rv.exception = std::make_exception_ptr(std::system_error(
::GetLastError(), std::system_category(), "GetFileAttributesExW"));
} else {
rv.valid_fields = file_stat::all_valid;
rv.dev = 0;
rv.ino = 0;
rv.nlink = 0;
rv.uid = 0;
rv.gid = 0;
rv.rdev = 0;
rv.size =
(static_cast<uint64_t>(info.nFileSizeHigh) << 32) + info.nFileSizeLow;
rv.atime = time_from_filetime(info.ftLastAccessTime);
rv.mtime = time_from_filetime(info.ftLastWriteTime);
rv.ctime = time_from_filetime(info.ftCreationTime);
}
rv.dev = 0;
rv.ino = 0;
rv.nlink = 0;
rv.uid = 0;
rv.gid = 0;
rv.rdev = 0;
rv.size =
(static_cast<uint64_t>(info.nFileSizeHigh) << 32) + info.nFileSizeLow;
rv.atime = time_from_filetime(info.ftLastAccessTime);
rv.mtime = time_from_filetime(info.ftLastWriteTime);
rv.ctime = time_from_filetime(info.ftCreationTime);
} else {
struct ::__stat64 st;
if (::_wstat64(wps.c_str(), &st) != 0) {
throw std::system_error(errno, std::generic_category(), "_stat64");
}
if (::_wstat64(wps.c_str(), &st) == 0) {
if (status.type() == fs::file_type::regular) {
::HANDLE hdl =
::CreateFileW(wps.c_str(), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
if (status.type() == fs::file_type::regular) {
::HANDLE hdl =
::CreateFileW(wps.c_str(), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
if (hdl == INVALID_HANDLE_VALUE) {
throw std::system_error(::GetLastError(), std::system_category(),
fmt::format("CreateFileW({})", path.string()));
if (hdl != INVALID_HANDLE_VALUE) {
::BY_HANDLE_FILE_INFORMATION info;
if (::GetFileInformationByHandle(hdl, &info)) {
if (::CloseHandle(hdl)) {
rv.valid_fields |= file_stat::ino_valid | file_stat::nlink_valid;
rv.ino = (static_cast<uint64_t>(info.nFileIndexHigh) << 32) +
info.nFileIndexLow;
rv.nlink = info.nNumberOfLinks;
} else {
rv.exception = std::make_exception_ptr(std::system_error(
::GetLastError(), std::system_category(), "CloseHandle"));
}
} else {
rv.exception = std::make_exception_ptr(
std::system_error(::GetLastError(), std::system_category(),
"GetFileInformationByHandle"));
::CloseHandle(hdl);
}
} else {
rv.exception = std::make_exception_ptr(std::system_error(
::GetLastError(), std::system_category(), "CreateFileW"));
}
} else {
rv.valid_fields |= file_stat::ino_valid | file_stat::nlink_valid;
rv.ino = st.st_ino;
rv.nlink = st.st_nlink;
}
::BY_HANDLE_FILE_INFORMATION info;
if (!::GetFileInformationByHandle(hdl, &info)) {
throw std::system_error(::GetLastError(), std::system_category(),
"GetFileInformationByHandle");
}
if (!::CloseHandle(hdl)) {
throw std::system_error(::GetLastError(), std::system_category(),
"CloseHandle");
}
rv.ino = (static_cast<uint64_t>(info.nFileIndexHigh) << 32) +
info.nFileIndexLow;
rv.nlink = info.nNumberOfLinks;
rv.valid_fields |= file_stat::dev_valid | file_stat::uid_valid |
file_stat::gid_valid | file_stat::rdev_valid |
file_stat::size_valid | file_stat::atime_valid |
file_stat::mtime_valid | file_stat::ctime_valid;
rv.dev = st.st_dev;
rv.uid = st.st_uid;
rv.gid = st.st_gid;
rv.rdev = st.st_rdev;
rv.size = st.st_size;
rv.atime = st.st_atime;
rv.mtime = st.st_mtime;
rv.ctime = st.st_ctime;
} else {
rv.ino = st.st_ino;
rv.nlink = st.st_nlink;
rv.exception = std::make_exception_ptr(
std::system_error(errno, std::generic_category(), "_stat64"));
}
rv.dev = st.st_dev;
rv.uid = st.st_uid;
rv.gid = st.st_gid;
rv.rdev = st.st_rdev;
rv.size = st.st_size;
rv.atime = st.st_atime;
rv.mtime = st.st_mtime;
rv.ctime = st.st_ctime;
}
return rv;
@ -197,6 +220,7 @@ file_stat make_file_stat(fs::path const& path) {
}
file_stat rv;
rv.valid_fields = file_stat::all_valid;
rv.dev = st.st_dev;
rv.ino = st.st_ino;
rv.nlink = st.st_nlink;
@ -240,4 +264,16 @@ std::string file_stat::perm_string(mode_type mode) {
return oss.str();
}
void file_stat::ensure_valid(valid_fields_type fields) const {
if ((valid_fields & fields) != fields) {
if (exception) {
std::rethrow_exception(exception);
} else {
DWARFS_THROW(runtime_error,
fmt::format("missing stat fields: {:#x} (have: {:#x})",
fields, valid_fields));
}
}
}
} // namespace dwarfs

View File

@ -147,6 +147,9 @@ void entry::walk(std::function<void(entry*)> const& f) { f(this); }
void entry::walk(std::function<void(const entry*)> const& f) const { f(this); }
void entry::update(global_entry_data& data) const {
stat_.ensure_valid(file_stat::uid_valid | file_stat::gid_valid |
file_stat::mode_valid | file_stat::atime_valid |
file_stat::mtime_valid | file_stat::ctime_valid);
data.add_uid(stat_.uid);
data.add_gid(stat_.gid);
data.add_mode(stat_.mode);
@ -157,6 +160,9 @@ void entry::update(global_entry_data& data) const {
void entry::pack(thrift::metadata::inode_data& entry_v2,
global_entry_data const& data) const {
stat_.ensure_valid(file_stat::uid_valid | file_stat::gid_valid |
file_stat::mode_valid | file_stat::atime_valid |
file_stat::mtime_valid | file_stat::ctime_valid);
entry_v2.mode_index() = data.get_mode_index(stat_.mode);
entry_v2.owner_index() = data.get_uid_index(stat_.uid);
entry_v2.group_index() = data.get_gid_index(stat_.gid);
@ -165,31 +171,81 @@ void entry::pack(thrift::metadata::inode_data& entry_v2,
entry_v2.ctime_offset() = data.get_ctime_offset(stat_.ctime);
}
size_t entry::size() const {
stat_.ensure_valid(file_stat::size_valid);
return stat_.size;
}
uint64_t entry::raw_inode_num() const {
stat_.ensure_valid(file_stat::ino_valid);
return stat_.ino;
}
uint64_t entry::num_hard_links() const {
stat_.ensure_valid(file_stat::nlink_valid);
return stat_.nlink;
}
void entry::override_size(size_t size) {
stat_.size = size;
stat_.valid_fields |= file_stat::size_valid;
}
entry::type_t file::type() const { return E_FILE; }
auto entry::get_permissions() const -> mode_type { return stat_.permissions(); }
void entry::set_permissions(mode_type perm) { stat_.set_permissions(perm); }
auto entry::get_uid() const -> uid_type { return stat_.uid; }
auto entry::get_uid() const -> uid_type {
stat_.ensure_valid(file_stat::uid_valid);
return stat_.uid;
}
void entry::set_uid(uid_type uid) { stat_.uid = uid; }
void entry::set_uid(uid_type uid) {
stat_.uid = uid;
stat_.valid_fields |= file_stat::uid_valid;
}
auto entry::get_gid() const -> gid_type { return stat_.gid; }
auto entry::get_gid() const -> gid_type {
stat_.ensure_valid(file_stat::gid_valid);
return stat_.gid;
}
void entry::set_gid(gid_type gid) { stat_.gid = gid; }
void entry::set_gid(gid_type gid) {
stat_.gid = gid;
stat_.valid_fields |= file_stat::gid_valid;
}
uint64_t entry::get_atime() const { return stat_.atime; }
uint64_t entry::get_atime() const {
stat_.ensure_valid(file_stat::atime_valid);
return stat_.atime;
}
void entry::set_atime(uint64_t atime) { stat_.atime = atime; }
void entry::set_atime(uint64_t atime) {
stat_.atime = atime;
stat_.valid_fields |= file_stat::atime_valid;
}
uint64_t entry::get_mtime() const { return stat_.mtime; }
uint64_t entry::get_mtime() const {
stat_.ensure_valid(file_stat::mtime_valid);
return stat_.mtime;
}
void entry::set_mtime(uint64_t mtime) { stat_.mtime = mtime; }
void entry::set_mtime(uint64_t mtime) {
stat_.mtime = mtime;
stat_.valid_fields |= file_stat::mtime_valid;
}
uint64_t entry::get_ctime() const { return stat_.ctime; }
uint64_t entry::get_ctime() const {
stat_.ensure_valid(file_stat::ctime_valid);
return stat_.ctime;
}
void entry::set_ctime(uint64_t ctime) { stat_.ctime = ctime; }
void entry::set_ctime(uint64_t ctime) {
stat_.ctime = ctime;
stat_.valid_fields |= file_stat::ctime_valid;
}
std::string_view file::hash() const {
auto& h = data_->hash;

View File

@ -1542,6 +1542,8 @@ metadata_<LoggerPolicy>::getattr_impl(inode_view iv,
::memset(&stbuf, 0, sizeof(stbuf));
stbuf.valid_fields = file_stat::all_valid;
auto mode = iv.mode();
auto timebase = meta_.timestamp_base();
auto inode = iv.inode_num();

View File

@ -50,6 +50,7 @@ namespace {
file_stat make_file_stat(simplestat const& ss) {
file_stat rv;
::memset(&rv, 0, sizeof(rv));
rv.valid_fields = file_stat::all_valid;
rv.ino = ss.ino;
rv.nlink = ss.nlink;
rv.mode = ss.mode;