From 1dfeb2e7f9faaf1931af193992a2fb33cdfac836 Mon Sep 17 00:00:00 2001 From: Marcus Holland-Moritz Date: Wed, 31 Jul 2024 18:27:21 +0200 Subject: [PATCH] refactor: delay file_stat errors --- include/dwarfs/file_stat.h | 28 +++++- include/dwarfs/internal/entry.h | 8 +- src/dwarfs/file_stat.cpp | 138 ++++++++++++++++++---------- src/dwarfs/internal/entry.cpp | 76 +++++++++++++-- src/dwarfs/internal/metadata_v2.cpp | 2 + test/test_helpers.cpp | 1 + 6 files changed, 187 insertions(+), 66 deletions(-) diff --git a/include/dwarfs/file_stat.h b/include/dwarfs/file_stat.h index 823dc2ff..862a922f 100644 --- a/include/dwarfs/file_stat.h +++ b/include/dwarfs/file_stat.h @@ -26,11 +26,13 @@ #include #include +#include #include namespace dwarfs { struct file_stat { + using valid_fields_type = uint32_t; using perms_type = std::underlying_type_t; 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(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); diff --git a/include/dwarfs/internal/entry.h b/include/dwarfs/internal/entry.h index 231e7b73..b5cf2312 100644 --- a/include/dwarfs/internal/entry.h +++ b/include/dwarfs/internal/entry.h @@ -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 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 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 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; diff --git a/src/dwarfs/file_stat.cpp b/src/dwarfs/file_stat.cpp index 0d9300d5..b88c9cec 100644 --- a/src/dwarfs/file_stat.cpp +++ b/src/dwarfs/file_stat.cpp @@ -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(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(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(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(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 diff --git a/src/dwarfs/internal/entry.cpp b/src/dwarfs/internal/entry.cpp index d80cb68b..c3a8a64b 100644 --- a/src/dwarfs/internal/entry.cpp +++ b/src/dwarfs/internal/entry.cpp @@ -147,6 +147,9 @@ void entry::walk(std::function const& f) { f(this); } void entry::walk(std::function 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; diff --git a/src/dwarfs/internal/metadata_v2.cpp b/src/dwarfs/internal/metadata_v2.cpp index f6ff5b6f..45901210 100644 --- a/src/dwarfs/internal/metadata_v2.cpp +++ b/src/dwarfs/internal/metadata_v2.cpp @@ -1542,6 +1542,8 @@ metadata_::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(); diff --git a/test/test_helpers.cpp b/test/test_helpers.cpp index 4ee6346d..351ef0a0 100644 --- a/test/test_helpers.cpp +++ b/test/test_helpers.cpp @@ -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;