From fd157eb6119a9b0cc0ed383970401cf7b2700166 Mon Sep 17 00:00:00 2001 From: Marcus Holland-Moritz Date: Wed, 21 Jun 2023 22:45:30 +0200 Subject: [PATCH] WIP file_stat --- CMakeLists.txt | 3 +- include/dwarfs/entry.h | 25 +- include/dwarfs/file_stat.h | 101 ++++++++ include/dwarfs/file_type.h | 45 ++++ include/dwarfs/filesystem_v2.h | 15 +- include/dwarfs/metadata_v2.h | 16 +- include/dwarfs/os_access.h | 15 +- ...{os_access_posix.h => os_access_generic.h} | 12 +- include/dwarfs/vfs_stat.h | 49 ++++ src/dwarfs/entry.cpp | 125 +++++----- src/dwarfs/file_type.cpp | 98 ++++++++ src/dwarfs/filesystem_extractor.cpp | 18 +- src/dwarfs/filesystem_v2.cpp | 11 +- src/dwarfs/metadata_v2.cpp | 74 +++--- src/dwarfs/os_access_generic.cpp | 182 ++++++++++++++ src/dwarfs/os_access_posix.cpp | 104 -------- src/dwarfs_main.cpp | 41 +++- src/dwarfsbench_main.cpp | 5 +- src/mkdwarfs_main.cpp | 4 +- test/dwarfs.cpp | 214 ++++++++--------- test/dwarfs_compat.cpp | 169 ++++++------- test/dwarfs_tools.cpp | 2 + test/test_dirtree.cpp | 6 +- test/test_helpers.cpp | 227 ++++++++++-------- test/test_helpers.h | 52 ++-- 25 files changed, 1025 insertions(+), 588 deletions(-) create mode 100644 include/dwarfs/file_stat.h create mode 100644 include/dwarfs/file_type.h rename include/dwarfs/{os_access_posix.h => os_access_generic.h} (72%) create mode 100644 include/dwarfs/vfs_stat.h create mode 100644 src/dwarfs/file_type.cpp create mode 100644 src/dwarfs/os_access_generic.cpp delete mode 100644 src/dwarfs/os_access_posix.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c5ed57a4..15297611 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -351,6 +351,7 @@ list( src/dwarfs/entry.cpp src/dwarfs/error.cpp src/dwarfs/file_scanner.cpp + src/dwarfs/file_type.cpp src/dwarfs/filesystem_extractor.cpp src/dwarfs/filesystem_v2.cpp src/dwarfs/filesystem_writer.cpp @@ -366,7 +367,7 @@ list( src/dwarfs/nilsimsa.cpp src/dwarfs/option_map.cpp src/dwarfs/options.cpp - src/dwarfs/os_access_posix.cpp + src/dwarfs/os_access_generic.cpp src/dwarfs/progress.cpp src/dwarfs/scanner.cpp src/dwarfs/similarity.cpp diff --git a/include/dwarfs/entry.h b/include/dwarfs/entry.h index 8dc13aa7..4fb0810d 100644 --- a/include/dwarfs/entry.h +++ b/include/dwarfs/entry.h @@ -30,11 +30,10 @@ #include #include -#include - #include #include "dwarfs/entry_interface.h" +#include "dwarfs/file_stat.h" namespace dwarfs { @@ -68,16 +67,16 @@ class entry : public entry_interface { public: enum type_t { E_FILE, E_DIR, E_LINK, E_DEVICE, E_OTHER }; - entry(const std::string& name, std::shared_ptr parent, - const struct ::stat& st); + entry(std::string const& name, std::shared_ptr parent, + file_stat const& st); bool has_parent() const; std::shared_ptr parent() const; - void set_name(const std::string& name); + void set_name(std::string const& name); std::string path() const override; std::string dpath() const override; - const std::string& name() const override { return name_; } - size_t size() const override { return stat_.st_size; } + std::string const& name() const override { return name_; } + size_t size() const override { return stat_.size; } virtual type_t type() const = 0; std::string type_string() const override; bool is_directory() const override; @@ -88,11 +87,11 @@ class entry : public entry_interface { void update(global_entry_data& data) const; virtual void accept(entry_visitor& v, bool preorder = false) = 0; virtual void scan(os_access& os, progress& prog) = 0; - const struct ::stat& status() const { return stat_; } + 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_.st_ino; } - uint64_t num_hard_links() const { return stat_.st_nlink; } + uint64_t raw_inode_num() const { return stat_.ino; } + uint64_t num_hard_links() const { return stat_.nlink; } virtual void set_inode_num(uint32_t ino) = 0; virtual std::optional const& inode_num() const = 0; @@ -110,19 +109,19 @@ 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_.st_size = size; } + void override_size(size_t size) { stat_.size = size; } private: std::string name_; std::weak_ptr parent_; - struct ::stat stat_; + file_stat stat_; std::optional entry_index_; }; class file : public entry { public: file(const std::string& name, std::shared_ptr parent, - const struct ::stat& st) + file_stat const& st) : entry(name, std::move(parent), st) {} type_t type() const override; diff --git a/include/dwarfs/file_stat.h b/include/dwarfs/file_stat.h new file mode 100644 index 00000000..427aa88e --- /dev/null +++ b/include/dwarfs/file_stat.h @@ -0,0 +1,101 @@ +/* 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 . + */ + +#pragma once + +#include +#include +#include +#include + +#include "dwarfs/file_type.h" + +namespace dwarfs { + +struct file_stat { + using perms_type = std::underlying_type_t; + using mode_type = uint32_t; + using dev_type = uint64_t; + using ino_type = uint64_t; + using nlink_type = uint64_t; + using uid_type = uint32_t; + using gid_type = uint32_t; + using off_type = int64_t; + using blksize_type = int64_t; + using blkcnt_type = int64_t; + using time_type = int64_t; + + std::filesystem::file_status status() const { + return file_mode_to_status(mode); + }; + + posix_file_type::value type() const { + return static_cast(mode & posix_file_type::mask); + }; + + perms_type permissions() const { return mode & 07777; }; + + void set_permissions(perms_type perms) { mode = type() | (perms & 07777); } + + bool is_directory() const { return type() == posix_file_type::directory; } + + bool is_regular_file() const { return type() == posix_file_type::regular; } + + bool is_symlink() const { return type() == posix_file_type::symlink; } + + bool is_device() const { + auto t = type(); + return t == posix_file_type::block || t == posix_file_type::character; + } + + dev_type dev; + ino_type ino; + nlink_type nlink; + mode_type mode; + uid_type uid; + gid_type gid; + dev_type rdev; + off_type size; + blksize_type blksize; + blkcnt_type blocks; + time_type atime; + time_type mtime; + time_type ctime; +}; + +template +void copy_file_stat(T* out, file_stat const& in) { + out->st_dev = in.dev; + out->st_ino = in.ino; + out->st_nlink = in.nlink; + out->st_mode = in.mode; + out->st_uid = in.uid; + out->st_gid = in.gid; + out->st_rdev = in.rdev; + out->st_size = in.size; + out->st_blksize = in.blksize; + out->st_blocks = in.blocks; + out->st_atime = in.atime; + out->st_mtime = in.mtime; + out->st_ctime = in.ctime; +} + +} // namespace dwarfs diff --git a/include/dwarfs/file_type.h b/include/dwarfs/file_type.h new file mode 100644 index 00000000..036ba459 --- /dev/null +++ b/include/dwarfs/file_type.h @@ -0,0 +1,45 @@ +/* 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 . + */ + +#pragma once + +#include +#include + +namespace dwarfs { + +struct posix_file_type { + enum value : uint16_t { + mask = 0170000, + socket = 0140000, + symlink = 0120000, + regular = 0100000, + block = 0060000, + directory = 0040000, + character = 0020000, + fifo = 0010000, + }; +}; + +std::filesystem::file_status file_mode_to_status(uint16_t mode); +uint16_t file_status_to_mode(std::filesystem::file_status status); + +} // namespace dwarfs diff --git a/include/dwarfs/filesystem_v2.h b/include/dwarfs/filesystem_v2.h index 804b5877..29b3882d 100644 --- a/include/dwarfs/filesystem_v2.h +++ b/include/dwarfs/filesystem_v2.h @@ -32,23 +32,20 @@ #include #include -#include - #include #include #include "dwarfs/fstypes.h" #include "dwarfs/metadata_types.h" -struct stat; -struct statvfs; - namespace dwarfs { struct cache_tidy_config; struct filesystem_options; struct rewrite_options; struct iovec_read_buf; +struct file_stat; +struct vfs_stat; class filesystem_writer; class logger; @@ -107,7 +104,7 @@ class filesystem_v2 { return impl_->find(inode, name); } - int getattr(inode_view entry, struct ::stat* stbuf) const { + int getattr(inode_view entry, file_stat* stbuf) const { return impl_->getattr(entry, stbuf); } @@ -134,7 +131,7 @@ class filesystem_v2 { return impl_->readlink(entry); } - int statvfs(struct ::statvfs* stbuf) const { return impl_->statvfs(stbuf); } + int statvfs(vfs_stat* stbuf) const { return impl_->statvfs(stbuf); } int open(inode_view entry) const { return impl_->open(entry); } @@ -176,7 +173,7 @@ class filesystem_v2 { virtual std::optional find(int inode) const = 0; virtual std::optional find(int inode, const char* name) const = 0; - virtual int getattr(inode_view entry, struct ::stat* stbuf) const = 0; + virtual int getattr(inode_view entry, file_stat* stbuf) const = 0; virtual int access(inode_view entry, int mode, uid_t uid, gid_t gid) const = 0; virtual std::optional opendir(inode_view entry) const = 0; @@ -186,7 +183,7 @@ class filesystem_v2 { virtual int readlink(inode_view entry, std::string* buf) const = 0; virtual folly::Expected readlink(inode_view entry) const = 0; - virtual int statvfs(struct ::statvfs* stbuf) const = 0; + virtual int statvfs(vfs_stat* stbuf) const = 0; virtual int open(inode_view entry) const = 0; virtual ssize_t read(uint32_t inode, char* buf, size_t size, off_t offset) const = 0; diff --git a/include/dwarfs/metadata_v2.h b/include/dwarfs/metadata_v2.h index e77d016b..9ab7e09e 100644 --- a/include/dwarfs/metadata_v2.h +++ b/include/dwarfs/metadata_v2.h @@ -32,23 +32,19 @@ #include #include -#include - #include #include #include "dwarfs/metadata_types.h" -struct stat; -struct statvfs; - namespace dwarfs { class logger; struct metadata_options; - struct filesystem_info; +struct file_stat; +struct vfs_stat; namespace thrift::metadata { class metadata; @@ -98,7 +94,7 @@ class metadata_v2 { return impl_->find(inode, name); } - int getattr(inode_view iv, struct ::stat* stbuf) const { + int getattr(inode_view iv, file_stat* stbuf) const { return impl_->getattr(iv, stbuf); } @@ -127,7 +123,7 @@ class metadata_v2 { return impl_->readlink(iv); } - int statvfs(struct ::statvfs* stbuf) const { return impl_->statvfs(stbuf); } + int statvfs(vfs_stat* stbuf) const { return impl_->statvfs(stbuf); } std::optional get_chunks(int inode) const { return impl_->get_chunks(inode); @@ -163,7 +159,7 @@ class metadata_v2 { virtual std::optional find(int inode, const char* name) const = 0; - virtual int getattr(inode_view iv, struct ::stat* stbuf) const = 0; + virtual int getattr(inode_view iv, file_stat* stbuf) const = 0; virtual std::optional opendir(inode_view iv) const = 0; @@ -180,7 +176,7 @@ class metadata_v2 { virtual folly::Expected readlink(inode_view iv) const = 0; - virtual int statvfs(struct ::statvfs* stbuf) const = 0; + virtual int statvfs(vfs_stat* stbuf) const = 0; virtual std::optional get_chunks(int inode) const = 0; diff --git a/include/dwarfs/os_access.h b/include/dwarfs/os_access.h index f720e03b..45dda90a 100644 --- a/include/dwarfs/os_access.h +++ b/include/dwarfs/os_access.h @@ -21,10 +21,11 @@ #pragma once +#include #include #include -#include +#include "dwarfs/file_stat.h" namespace dwarfs { @@ -34,7 +35,7 @@ class dir_reader { public: virtual ~dir_reader() = default; - virtual bool read(std::string& name) const = 0; + virtual bool read(std::string& name) = 0; }; class os_access { @@ -42,11 +43,11 @@ class os_access { virtual ~os_access() = default; virtual std::shared_ptr - opendir(const std::string& path) const = 0; - virtual void lstat(const std::string& path, struct ::stat* st) const = 0; - virtual std::string readlink(const std::string& path, size_t size) const = 0; + opendir(std::string const& path) const = 0; + virtual file_stat symlink_info(std::string const& path) const = 0; + virtual std::string read_symlink(std::string const& path) const = 0; virtual std::shared_ptr - map_file(const std::string& path, size_t size) const = 0; - virtual int access(const std::string& path, int mode) const = 0; + map_file(std::string const& path, size_t size) const = 0; + virtual int access(std::string const& path, int mode) const = 0; }; } // namespace dwarfs diff --git a/include/dwarfs/os_access_posix.h b/include/dwarfs/os_access_generic.h similarity index 72% rename from include/dwarfs/os_access_posix.h rename to include/dwarfs/os_access_generic.h index a1c44b56..590bc911 100644 --- a/include/dwarfs/os_access_posix.h +++ b/include/dwarfs/os_access_generic.h @@ -31,13 +31,13 @@ namespace dwarfs { class mmif; -class os_access_posix : public os_access { +class os_access_generic : public os_access { public: - std::shared_ptr opendir(const std::string& path) const override; - void lstat(const std::string& path, struct ::stat* st) const override; - std::string readlink(const std::string& path, size_t size) const override; + std::shared_ptr opendir(std::string const& path) const override; + file_stat symlink_info(std::string const& path) const override; + std::string read_symlink(std::string const& path) const override; std::shared_ptr - map_file(const std::string& path, size_t size) const override; - int access(const std::string& path, int mode) const override; + map_file(std::string const& path, size_t size) const override; + int access(std::string const& path, int mode) const override; }; } // namespace dwarfs diff --git a/include/dwarfs/vfs_stat.h b/include/dwarfs/vfs_stat.h new file mode 100644 index 00000000..e4aa86c7 --- /dev/null +++ b/include/dwarfs/vfs_stat.h @@ -0,0 +1,49 @@ +/* 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 . + */ + +#pragma once + +#include + +namespace dwarfs { + +struct vfs_stat { + using blkcnt_type = uint64_t; + using filcnt_type = uint64_t; + + uint64_t bsize; + uint64_t frsize; + blkcnt_type blocks; + filcnt_type files; + uint64_t namemax; + bool readonly; +}; + +template +void copy_vfs_stat(T* out, vfs_stat const& in) { + out->f_bsize = in.bsize; + out->f_frsize = in.frsize; + out->f_blocks = in.blocks; + out->f_files = in.files; + out->f_namemax = in.namemax; +} + +} // namespace dwarfs diff --git a/src/dwarfs/entry.cpp b/src/dwarfs/entry.cpp index 3cf25362..7e9358b9 100644 --- a/src/dwarfs/entry.cpp +++ b/src/dwarfs/entry.cpp @@ -28,6 +28,7 @@ #include "dwarfs/checksum.h" #include "dwarfs/entry.h" #include "dwarfs/error.h" +#include "dwarfs/file_type.h" #include "dwarfs/global_entry_data.h" #include "dwarfs/inode.h" #include "dwarfs/mmif.h" @@ -40,11 +41,11 @@ namespace dwarfs { -entry::entry(const std::string& name, std::shared_ptr parent, - const struct ::stat& st) - : name_(name) - , parent_(std::move(parent)) - , stat_(st) {} +entry::entry(std::string const& name, std::shared_ptr parent, + file_stat const& st) + : name_{name} + , parent_{std::move(parent)} + , stat_{st} {} bool entry::has_parent() const { if (parent_.lock()) { @@ -75,80 +76,79 @@ std::string entry::dpath() const { } std::string entry::type_string() const { - auto mode = stat_.st_mode; - - if (S_ISREG(mode)) { + switch (stat_.type()) { + case posix_file_type::regular: return "file"; - } else if (S_ISDIR(mode)) { + case posix_file_type::directory: return "directory"; - } else if (S_ISLNK(mode)) { + case posix_file_type::symlink: return "link"; - } else if (S_ISCHR(mode)) { + case posix_file_type::character: return "chardev"; - } else if (S_ISBLK(mode)) { + case posix_file_type::block: return "blockdev"; - } else if (S_ISFIFO(mode)) { + case posix_file_type::fifo: return "fifo"; - } else if (S_ISSOCK(mode)) { + case posix_file_type::socket: return "socket"; + default: + break; } - DWARFS_THROW(runtime_error, fmt::format("unknown file type: {:#06x}", mode)); + DWARFS_THROW(runtime_error, fmt::format("unknown file type: {:#06x}", + fmt::underlying(stat_.type()))); } -bool entry::is_directory() const { return S_ISDIR(stat_.st_mode); } +bool entry::is_directory() const { return stat_.is_directory(); } 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 { - data.add_uid(stat_.st_uid); - data.add_gid(stat_.st_gid); - data.add_mode(stat_.st_mode & 0xFFFF); - data.add_atime(stat_.st_atime); - data.add_mtime(stat_.st_mtime); - data.add_ctime(stat_.st_ctime); + data.add_uid(stat_.uid); + data.add_gid(stat_.gid); + data.add_mode(stat_.mode); + data.add_atime(stat_.atime); + data.add_mtime(stat_.mtime); + data.add_ctime(stat_.ctime); } void entry::pack(thrift::metadata::inode_data& entry_v2, global_entry_data const& data) const { - entry_v2.mode_index() = data.get_mode_index(stat_.st_mode & 0xFFFF); - entry_v2.owner_index() = data.get_uid_index(stat_.st_uid); - entry_v2.group_index() = data.get_gid_index(stat_.st_gid); - entry_v2.atime_offset() = data.get_atime_offset(stat_.st_atime); - entry_v2.mtime_offset() = data.get_mtime_offset(stat_.st_mtime); - entry_v2.ctime_offset() = data.get_ctime_offset(stat_.st_ctime); + 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); + entry_v2.atime_offset() = data.get_atime_offset(stat_.atime); + entry_v2.mtime_offset() = data.get_mtime_offset(stat_.mtime); + entry_v2.ctime_offset() = data.get_ctime_offset(stat_.ctime); } entry::type_t file::type() const { return E_FILE; } -uint16_t entry::get_permissions() const { return stat_.st_mode & 07777; } +uint16_t entry::get_permissions() const { return stat_.permissions(); } -void entry::set_permissions(uint16_t perm) { - stat_.st_mode &= ~07777; - stat_.st_mode |= perm; -} +void entry::set_permissions(uint16_t perm) { stat_.set_permissions(perm); } -uint16_t entry::get_uid() const { return stat_.st_uid; } +uint16_t entry::get_uid() const { return stat_.uid; } -void entry::set_uid(uint16_t uid) { stat_.st_uid = uid; } +void entry::set_uid(uint16_t uid) { stat_.uid = uid; } -uint16_t entry::get_gid() const { return stat_.st_gid; } +uint16_t entry::get_gid() const { return stat_.gid; } -void entry::set_gid(uint16_t gid) { stat_.st_gid = gid; } +void entry::set_gid(uint16_t gid) { stat_.gid = gid; } -uint64_t entry::get_atime() const { return stat_.st_atime; } +uint64_t entry::get_atime() const { return stat_.atime; } -void entry::set_atime(uint64_t atime) { stat_.st_atime = atime; } +void entry::set_atime(uint64_t atime) { stat_.atime = atime; } -uint64_t entry::get_mtime() const { return stat_.st_mtime; } +uint64_t entry::get_mtime() const { return stat_.mtime; } -void entry::set_mtime(uint64_t mtime) { stat_.st_atime = mtime; } +void entry::set_mtime(uint64_t mtime) { stat_.mtime = mtime; } -uint64_t entry::get_ctime() const { return stat_.st_ctime; } +uint64_t entry::get_ctime() const { return stat_.ctime; } -void entry::set_ctime(uint64_t ctime) { stat_.st_atime = ctime; } +void entry::set_ctime(uint64_t ctime) { stat_.ctime = ctime; } std::string_view file::hash() const { auto& h = data_->hash; @@ -374,43 +374,54 @@ const std::string& link::linkname() const { return link_; } void link::accept(entry_visitor& v, bool) { v.visit(this); } void link::scan(os_access& os, progress& prog) { - link_ = os.readlink(path(), size()); + link_ = os.read_symlink(path()); prog.original_size += size(); prog.symlink_size += size(); } entry::type_t device::type() const { - auto mode = status().st_mode; - return S_ISCHR(mode) || S_ISBLK(mode) ? E_DEVICE : E_OTHER; + switch (status().type()) { + case posix_file_type::character: + case posix_file_type::block: + return E_DEVICE; + default: + return E_OTHER; + } } void device::accept(entry_visitor& v, bool) { v.visit(this); } void device::scan(os_access&, progress&) {} -uint64_t device::device_id() const { return status().st_rdev; } +uint64_t device::device_id() const { return status().rdev; } class entry_factory_ : public entry_factory { public: std::shared_ptr create(os_access& os, const std::string& name, std::shared_ptr parent) override { - const std::string& p = parent ? parent->path() + "/" + name : name; - struct ::stat st; + // TODO: std::filesystem::path + std::string const& p = parent ? parent->path() + "/" + name : name; - os.lstat(p, &st); - auto mode = st.st_mode; + auto st = os.symlink_info(p); - if (S_ISREG(mode)) { + switch (st.type()) { + case posix_file_type::regular: return std::make_shared(name, std::move(parent), st); - } else if (S_ISDIR(mode)) { + + case posix_file_type::directory: return std::make_shared(name, std::move(parent), st); - } else if (S_ISLNK(mode)) { + + case posix_file_type::symlink: return std::make_shared(name, std::move(parent), st); - } else if (S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode) || - S_ISSOCK(mode)) { + + case posix_file_type::character: + case posix_file_type::block: + case posix_file_type::fifo: + case posix_file_type::socket: return std::make_shared(name, std::move(parent), st); - } else { + default: // TODO: warn + break; } return nullptr; diff --git a/src/dwarfs/file_type.cpp b/src/dwarfs/file_type.cpp new file mode 100644 index 00000000..4bda3e8f --- /dev/null +++ b/src/dwarfs/file_type.cpp @@ -0,0 +1,98 @@ +/* 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 "dwarfs/file_type.h" + +namespace dwarfs { + +namespace fs = std::filesystem; + +fs::file_status file_mode_to_status(uint16_t mode) { + fs::file_type ft; + + switch (mode & posix_file_type::mask) { + case posix_file_type::socket: + ft = fs::file_type::socket; + break; + case posix_file_type::symlink: + ft = fs::file_type::symlink; + break; + case posix_file_type::regular: + ft = fs::file_type::regular; + break; + case posix_file_type::block: + ft = fs::file_type::block; + break; + case posix_file_type::directory: + ft = fs::file_type::directory; + break; + case posix_file_type::character: + ft = fs::file_type::character; + break; + case posix_file_type::fifo: + ft = fs::file_type::fifo; + break; + default: + throw std::runtime_error(fmt::format("invalid file mode: {:#06x}", mode)); + break; + } + + return fs::file_status(ft, fs::perms(mode & 07777)); +} + +uint16_t file_status_to_mode(std::filesystem::file_status status) { + posix_file_type::value ft; + + switch (status.type()) { + case fs::file_type::socket: + ft = posix_file_type::socket; + break; + case fs::file_type::symlink: + ft = posix_file_type::symlink; + break; + case fs::file_type::regular: + ft = posix_file_type::regular; + break; + case fs::file_type::block: + ft = posix_file_type::block; + break; + case fs::file_type::directory: + ft = posix_file_type::directory; + break; + case fs::file_type::character: + ft = posix_file_type::character; + break; + case fs::file_type::fifo: + ft = posix_file_type::fifo; + break; + default: + throw std::runtime_error( + fmt::format("invalid file type: {}", fmt::underlying(status.type()))); + break; + } + + return static_cast(ft) | + static_cast(status.permissions()); +} + +} // namespace dwarfs diff --git a/src/dwarfs/filesystem_extractor.cpp b/src/dwarfs/filesystem_extractor.cpp index 629f179e..e7fb7894 100644 --- a/src/dwarfs/filesystem_extractor.cpp +++ b/src/dwarfs/filesystem_extractor.cpp @@ -25,9 +25,6 @@ #include #include -#include -#include - #include #include @@ -35,11 +32,13 @@ #include #include +#include "dwarfs/file_stat.h" #include "dwarfs/filesystem_extractor.h" #include "dwarfs/filesystem_v2.h" #include "dwarfs/fstypes.h" #include "dwarfs/logger.h" #include "dwarfs/options.h" +#include "dwarfs/vfs_stat.h" #include "dwarfs/worker_group.h" namespace dwarfs { @@ -230,13 +229,13 @@ bool filesystem_extractor_::extract( sem.post(opts.max_queued_bytes); - struct ::statvfs vfs; + vfs_stat vfs; fs.statvfs(&vfs); std::atomic hard_error{0}; std::atomic soft_error{0}; std::atomic bytes_written{0}; - uint64_t const bytes_total{vfs.f_blocks}; + uint64_t const bytes_total{vfs.blocks}; auto do_archive = [&](::archive_entry* ae, inode_view entry) { // TODO: inode vs. entry @@ -322,14 +321,19 @@ bool filesystem_extractor_::extract( auto inode = entry.inode(); auto ae = ::archive_entry_new(); - struct ::stat stbuf; + file_stat stbuf; if (fs.getattr(inode, &stbuf) != 0) { DWARFS_THROW(runtime_error, "getattr() failed"); } + struct ::stat st; + + ::memset(&st, 0, sizeof(st)); + copy_file_stat(&st, stbuf); + ::archive_entry_set_pathname(ae, entry.path().c_str()); - ::archive_entry_copy_stat(ae, &stbuf); + ::archive_entry_copy_stat(ae, &st); if (S_ISLNK(inode.mode())) { std::string link; diff --git a/src/dwarfs/filesystem_v2.cpp b/src/dwarfs/filesystem_v2.cpp index 498af1b8..4765be77 100644 --- a/src/dwarfs/filesystem_v2.cpp +++ b/src/dwarfs/filesystem_v2.cpp @@ -25,9 +25,6 @@ #include #include -#include -#include - #include #include "dwarfs/block_cache.h" @@ -328,7 +325,7 @@ class filesystem_ final : public filesystem_v2::impl { 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; - int getattr(inode_view entry, struct ::stat* stbuf) const override; + int getattr(inode_view entry, file_stat* stbuf) const override; int access(inode_view entry, int mode, uid_t uid, gid_t gid) const override; std::optional opendir(inode_view entry) const override; std::optional> @@ -336,7 +333,7 @@ class filesystem_ final : public filesystem_v2::impl { size_t dirsize(directory_view dir) const override; int readlink(inode_view entry, std::string* buf) const override; folly::Expected readlink(inode_view entry) const override; - int statvfs(struct ::statvfs* stbuf) const override; + int statvfs(vfs_stat* stbuf) const override; int open(inode_view entry) const override; ssize_t read(uint32_t inode, char* buf, size_t size, off_t offset) const override; @@ -494,7 +491,7 @@ filesystem_::find(int inode, const char* name) const { template int filesystem_::getattr(inode_view entry, - struct ::stat* stbuf) const { + file_stat* stbuf) const { return meta_.getattr(entry, stbuf); } @@ -534,7 +531,7 @@ filesystem_::readlink(inode_view entry) const { } template -int filesystem_::statvfs(struct ::statvfs* stbuf) const { +int filesystem_::statvfs(vfs_stat* stbuf) const { // TODO: not sure if that's the right abstraction... return meta_.statvfs(stbuf); } diff --git a/src/dwarfs/metadata_v2.cpp b/src/dwarfs/metadata_v2.cpp index 49742396..5e93131d 100644 --- a/src/dwarfs/metadata_v2.cpp +++ b/src/dwarfs/metadata_v2.cpp @@ -47,12 +47,14 @@ #include #include "dwarfs/error.h" +#include "dwarfs/file_stat.h" #include "dwarfs/fstypes.h" #include "dwarfs/logger.h" #include "dwarfs/metadata_v2.h" #include "dwarfs/options.h" #include "dwarfs/string_table.h" #include "dwarfs/util.h" +#include "dwarfs/vfs_stat.h" #include "dwarfs/gen-cpp2/metadata_layouts.h" #include "dwarfs/gen-cpp2/metadata_types_custom_protocol.h" @@ -403,7 +405,7 @@ class metadata_ final : public metadata_v2::impl { std::optional find(int inode) const override; std::optional find(int inode, const char* name) const override; - int getattr(inode_view iv, struct ::stat* stbuf) const override; + int getattr(inode_view iv, file_stat* stbuf) const override; std::optional opendir(inode_view iv) const override; @@ -422,7 +424,7 @@ class metadata_ final : public metadata_v2::impl { folly::Expected readlink(inode_view iv) const override; - int statvfs(struct ::statvfs* stbuf) const override; + int statvfs(vfs_stat* stbuf) const override; std::optional get_chunks(int inode) const override; @@ -799,7 +801,7 @@ template void metadata_::dump( std::ostream& os, int detail_level, filesystem_info const& fsinfo, std::function const& icb) const { - struct ::statvfs stbuf; + vfs_stat stbuf; statvfs(&stbuf); if (auto version = meta_.dwarfs_version()) { @@ -815,10 +817,10 @@ void metadata_::dump( } if (detail_level > 0) { - os << "block size: " << size_with_unit(stbuf.f_bsize) << std::endl; + os << "block size: " << size_with_unit(stbuf.bsize) << std::endl; os << "block count: " << fsinfo.block_count << std::endl; - os << "inode count: " << stbuf.f_files << std::endl; - os << "original filesystem size: " << size_with_unit(stbuf.f_blocks) + os << "inode count: " << stbuf.files << std::endl; + os << "original filesystem size: " << size_with_unit(stbuf.blocks) << std::endl; os << "compressed block size: " << size_with_unit(fsinfo.compressed_block_size) @@ -966,11 +968,11 @@ template folly::dynamic metadata_::as_dynamic() const { folly::dynamic obj = folly::dynamic::object; - struct ::statvfs stbuf; + vfs_stat stbuf; statvfs(&stbuf); - obj["statvfs"] = folly::dynamic::object("f_bsize", stbuf.f_bsize)( - "f_files", stbuf.f_files)("f_blocks", stbuf.f_blocks); + obj["statvfs"] = folly::dynamic::object("f_bsize", stbuf.bsize)( + "f_files", stbuf.files)("f_blocks", stbuf.blocks); obj["root"] = as_dynamic(root_); @@ -1211,8 +1213,7 @@ metadata_::find(int inode, const char* name) const { } template -int metadata_::getattr(inode_view iv, - struct ::stat* stbuf) const { +int metadata_::getattr(inode_view iv, file_stat* stbuf) const { ::memset(stbuf, 0, sizeof(*stbuf)); auto mode = iv.mode(); @@ -1227,31 +1228,32 @@ int metadata_::getattr(inode_view iv, } } - stbuf->st_mode = mode; + stbuf->mode = mode; if (options_.readonly) { - stbuf->st_mode &= READ_ONLY_MASK; + stbuf->mode &= READ_ONLY_MASK; } - stbuf->st_size = S_ISDIR(mode) ? make_directory_view(iv).entry_count() - : file_size(iv, mode); - stbuf->st_ino = inode + inode_offset_; - stbuf->st_blocks = (stbuf->st_size + 511) / 512; - stbuf->st_uid = iv.getuid(); - stbuf->st_gid = iv.getgid(); - stbuf->st_mtime = resolution * (timebase + iv.mtime_offset()); + stbuf->size = stbuf->is_directory() ? make_directory_view(iv).entry_count() + : file_size(iv, mode); + stbuf->ino = inode + inode_offset_; + stbuf->blksize = 512; + stbuf->blocks = (stbuf->size + 511) / 512; + stbuf->uid = iv.getuid(); + stbuf->gid = iv.getgid(); + stbuf->mtime = resolution * (timebase + iv.mtime_offset()); if (mtime_only) { - stbuf->st_atime = stbuf->st_ctime = stbuf->st_mtime; + stbuf->atime = stbuf->ctime = stbuf->mtime; } else { - stbuf->st_atime = resolution * (timebase + iv.atime_offset()); - stbuf->st_ctime = resolution * (timebase + iv.ctime_offset()); + stbuf->atime = resolution * (timebase + iv.atime_offset()); + stbuf->ctime = resolution * (timebase + iv.ctime_offset()); } - stbuf->st_nlink = options_.enable_nlink && S_ISREG(mode) - ? DWARFS_NOTHROW(nlinks_.at(inode - file_inode_offset_)) - : 1; + stbuf->nlink = options_.enable_nlink && stbuf->is_regular_file() + ? DWARFS_NOTHROW(nlinks_.at(inode - file_inode_offset_)) + : 1; - if (S_ISBLK(mode) || S_ISCHR(mode)) { - stbuf->st_rdev = get_device_id(inode); + if (stbuf->is_device()) { + stbuf->rdev = get_device_id(inode); } return 0; @@ -1358,20 +1360,20 @@ metadata_::readlink(inode_view iv) const { } template -int metadata_::statvfs(struct ::statvfs* stbuf) const { +int metadata_::statvfs(vfs_stat* stbuf) const { ::memset(stbuf, 0, sizeof(*stbuf)); - stbuf->f_bsize = meta_.block_size(); - stbuf->f_frsize = 1UL; - stbuf->f_blocks = meta_.total_fs_size(); + stbuf->bsize = meta_.block_size(); + stbuf->frsize = 1UL; + stbuf->blocks = meta_.total_fs_size(); if (!options_.enable_nlink) { if (auto ths = meta_.total_hardlink_size()) { - stbuf->f_blocks += *ths; + stbuf->blocks += *ths; } } - stbuf->f_files = inode_count_; - stbuf->f_flag = ST_RDONLY; - stbuf->f_namemax = PATH_MAX; + stbuf->files = inode_count_; + stbuf->readonly = true; + stbuf->namemax = PATH_MAX; return 0; } diff --git a/src/dwarfs/os_access_generic.cpp b/src/dwarfs/os_access_generic.cpp new file mode 100644 index 00000000..cf8359b9 --- /dev/null +++ b/src/dwarfs/os_access_generic.cpp @@ -0,0 +1,182 @@ +/* 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 + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#include +#include + +#include + +#include "dwarfs/error.h" +#include "dwarfs/mmap.h" +#include "dwarfs/os_access_generic.h" + +namespace dwarfs { + +namespace fs = std::filesystem; + +namespace { + +#ifdef _WIN32 + +uint64_t time_from_filetime(FILETIME const& ft) { + static constexpr uint64_t FT_TICKS_PER_SECOND = UINT64_C(10000000); + static constexpr uint64_t FT_EPOCH_OFFSET = UINT64_C(11644473600); + uint64_t ticks = + (static_cast(ft.dwHighDateTime) << 32) + ft.dwLowDateTime; + return (ticks / FT_TICKS_PER_SECOND) - FT_EPOCH_OFFSET; +} + +file_stat make_file_stat(std::string const& path) { + auto status = fs::symlink_status(path); + + file_stat rv; + rv.mode = file_status_to_mode(status); + + if (status.type() == fs::file_type::symlink) { + ::WIN32_FILE_ATTRIBUTE_DATA info; + if (::GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &info) == + 0) { + throw std::system_error(::GetLastError(), std::system_category(), + "GetFileAttributesExA"); + } + 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.blksize = 0; + rv.blocks = 0; + 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 (::_stat64(path.c_str(), &st) != 0) { + throw std::system_error(errno, std::generic_category(), "_stat64"); + } + rv.dev = st.st_dev; + rv.ino = st.st_ino; + rv.nlink = st.st_nlink; + rv.uid = st.st_uid; + rv.gid = st.st_gid; + rv.rdev = st.st_rdev; + rv.size = st.st_size; + rv.blksize = st.st_blksize; + rv.blocks = st.st_blocks; + rv.atime = st.st_atime; + rv.mtime = st.st_mtime; + rv.ctime = st.st_ctime; + } + + return rv; +} + +#else + +file_stat make_file_stat(std::string const& path) { + struct ::stat st; + + if (::lstat(path.c_str(), &st) != 0) { + throw std::system_error(errno, std::generic_category(), "lstat"); + } + + file_stat rv; + rv.dev = st.st_dev; + rv.ino = st.st_ino; + rv.nlink = st.st_nlink; + rv.mode = st.st_mode; + rv.uid = st.st_uid; + rv.gid = st.st_gid; + rv.rdev = st.st_rdev; + rv.size = st.st_size; + rv.blksize = st.st_blksize; + rv.blocks = st.st_blocks; + rv.atime = st.st_atim.tv_sec; + rv.mtime = st.st_mtim.tv_sec; + rv.ctime = st.st_ctim.tv_sec; + + return rv; +} + +#endif + +class generic_dir_reader final : public dir_reader { + public: + explicit generic_dir_reader(const std::string& path) + : it_(fs::directory_iterator(path)) {} + + bool read(std::string& name) override { + if (it_ != fs::directory_iterator()) { + name.assign(it_->path().filename().string()); + ++it_; + return true; + } + + return false; + } + + private: + fs::directory_iterator it_; +}; + +} // namespace + +std::shared_ptr +os_access_generic::opendir(std::string const& path) const { + return std::make_shared(path); +} + +file_stat os_access_generic::symlink_info(std::string const& path) const { + return make_file_stat(path); +} + +std::string os_access_generic::read_symlink(std::string const& path) const { + return fs::read_symlink(path).string(); +} + +std::shared_ptr +os_access_generic::map_file(std::string const& path, size_t size) const { + return std::make_shared(path, size); +} + +int os_access_generic::access(std::string const& path, int mode) const { + // TODO +#ifndef _WIN32 + return ::access(path.c_str(), mode); +#endif +} +} // namespace dwarfs diff --git a/src/dwarfs/os_access_posix.cpp b/src/dwarfs/os_access_posix.cpp deleted file mode 100644 index 866f3da7..00000000 --- a/src/dwarfs/os_access_posix.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* 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 "dwarfs/error.h" -#include "dwarfs/mmap.h" -#include "dwarfs/os_access_posix.h" - -namespace dwarfs { - -class posix_dir_reader final : public dir_reader { - public: - explicit posix_dir_reader(const std::string& path) - : dir_(::opendir(path.c_str())) { - if (!dir_) { - DWARFS_THROW(system_error, fmt::format("opendir('{}')", path)); - } - } - - ~posix_dir_reader() noexcept override { - if (dir_) { - ::closedir(dir_); - } - } - - bool read(std::string& name) const override { - errno = 0; - - auto ent = readdir(dir_); - - if (ent) { - name.assign(ent->d_name); - return true; - } - - if (errno == 0) { - return false; - } - - DWARFS_THROW(system_error, "readdir"); - } - - private: - DIR* dir_; -}; - -std::shared_ptr -os_access_posix::opendir(const std::string& path) const { - return std::make_shared(path); -} - -void os_access_posix::lstat(const std::string& path, struct ::stat* st) const { - if (::lstat(path.c_str(), st) == -1) { - DWARFS_THROW(system_error, fmt::format("lstat('{}')", path)); - } -} - -std::string -os_access_posix::readlink(const std::string& path, size_t size) const { - std::vector linkname(size); - ssize_t rv = ::readlink(path.c_str(), &linkname[0], size); - - if (rv == static_cast(size)) { - return std::string(linkname.begin(), linkname.end()); - } - - DWARFS_THROW(system_error, fmt::format("readlink('{}')", path)); -} - -std::shared_ptr -os_access_posix::map_file(const std::string& path, size_t size) const { - return std::make_shared(path, size); -} - -int os_access_posix::access(const std::string& path, int mode) const { - return ::access(path.c_str(), mode); -} -} // namespace dwarfs diff --git a/src/dwarfs_main.cpp b/src/dwarfs_main.cpp index e9e3bca4..cb957a31 100644 --- a/src/dwarfs_main.cpp +++ b/src/dwarfs_main.cpp @@ -44,6 +44,7 @@ #endif #include "dwarfs/error.h" +#include "dwarfs/file_stat.h" #include "dwarfs/filesystem_v2.h" #include "dwarfs/fstypes.h" #include "dwarfs/logger.h" @@ -53,6 +54,7 @@ #include "dwarfs/tool.h" #include "dwarfs/util.h" #include "dwarfs/version.h" +#include "dwarfs/vfs_stat.h" #include "dwarfs_tool_main.h" namespace dwarfs { @@ -159,11 +161,14 @@ void op_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) { auto entry = userdata->fs.find(parent, name); if (entry) { - struct ::fuse_entry_param e; + file_stat stbuf; - err = userdata->fs.getattr(*entry, &e.attr); + err = userdata->fs.getattr(*entry, &stbuf); if (err == 0) { + struct ::fuse_entry_param e; + + copy_file_stat(&e.attr, stbuf); e.generation = 1; e.ino = e.attr.st_ino; e.attr_timeout = std::numeric_limits::max(); @@ -199,12 +204,17 @@ void op_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info*) { auto entry = userdata->fs.find(ino); if (entry) { - struct ::stat stbuf; + file_stat stbuf; err = userdata->fs.getattr(*entry, &stbuf); if (err == 0) { - fuse_reply_attr(req, &stbuf, std::numeric_limits::max()); + struct ::stat st; + + ::memset(&st, 0, sizeof(st)); + copy_file_stat(&st, stbuf); + + fuse_reply_attr(req, &st, std::numeric_limits::max()); return; } @@ -380,10 +390,13 @@ void op_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, if (dir) { off_t lastoff = userdata->fs.dirsize(*dir); - struct stat stbuf; + file_stat stbuf; + struct ::stat st; std::vector buf(size); size_t written = 0; + ::memset(&st, 0, sizeof(st)); + while (off < lastoff && written < size) { auto res = userdata->fs.readdir(*dir, off); assert(res); @@ -392,12 +405,13 @@ void op_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, std::string name(name_view); userdata->fs.getattr(entry, &stbuf); + copy_file_stat(&st, stbuf); assert(written < buf.size()); size_t needed = fuse_add_direntry(req, &buf[written], buf.size() - written, - name.c_str(), &stbuf, off + 1); + name.c_str(), &st, off + 1); if (written + needed > buf.size()) { break; @@ -435,12 +449,21 @@ void op_statfs(fuse_req_t req, fuse_ino_t /*ino*/) { int err = EIO; try { - struct ::statvfs buf; + vfs_stat stbuf; - err = userdata->fs.statvfs(&buf); + err = userdata->fs.statvfs(&stbuf); if (err == 0) { - fuse_reply_statfs(req, &buf); + struct ::statvfs st; + + ::memset(&st, 0, sizeof(st)); + copy_vfs_stat(&st, stbuf); + + if (stbuf.readonly) { + st.f_flag |= ST_RDONLY; + } + + fuse_reply_statfs(req, &st); return; } diff --git a/src/dwarfsbench_main.cpp b/src/dwarfsbench_main.cpp index f361284d..788dc2e6 100644 --- a/src/dwarfsbench_main.cpp +++ b/src/dwarfsbench_main.cpp @@ -26,6 +26,7 @@ #include #include +#include "dwarfs/file_stat.h" #include "dwarfs/filesystem_v2.h" #include "dwarfs/fstypes.h" #include "dwarfs/logger.h" @@ -109,9 +110,9 @@ int dwarfsbench_main(int argc, char** argv) { if (S_ISREG(inode_data.mode())) { wg.add_job([&fs, inode_data] { try { - struct ::stat stbuf; + file_stat stbuf; if (fs.getattr(inode_data, &stbuf) == 0) { - std::vector buf(stbuf.st_size); + std::vector buf(stbuf.size); int fh = fs.open(inode_data); fs.read(fh, buf.data(), buf.size()); } diff --git a/src/mkdwarfs_main.cpp b/src/mkdwarfs_main.cpp index 82335be0..ab6dc0d2 100644 --- a/src/mkdwarfs_main.cpp +++ b/src/mkdwarfs_main.cpp @@ -66,7 +66,7 @@ #include "dwarfs/mmap.h" #include "dwarfs/options.h" #include "dwarfs/options_interface.h" -#include "dwarfs/os_access_posix.h" +#include "dwarfs/os_access_generic.h" #include "dwarfs/progress.h" #include "dwarfs/scanner.h" #include "dwarfs/script.h" @@ -1076,7 +1076,7 @@ int mkdwarfs_main(int argc, char** argv) { options.file_order.mode == file_order_mode::NILSIMSA; scanner s(lgr, wg_scanner, cfg, entry_factory::create(), - std::make_shared(), std::move(script), + std::make_shared(), std::move(script), options); if (input_list) { diff --git a/test/dwarfs.cpp b/test/dwarfs.cpp index 927e9dc0..87aaa790 100644 --- a/test/dwarfs.cpp +++ b/test/dwarfs.cpp @@ -20,6 +20,7 @@ */ #include +#include #include #include #include @@ -36,6 +37,8 @@ #include "dwarfs/block_compressor.h" #include "dwarfs/builtin_script.h" #include "dwarfs/entry.h" +#include "dwarfs/file_stat.h" +#include "dwarfs/file_type.h" #include "dwarfs/filesystem_v2.h" #include "dwarfs/filesystem_writer.h" #include "dwarfs/logger.h" @@ -43,6 +46,7 @@ #include "dwarfs/options.h" #include "dwarfs/progress.h" #include "dwarfs/scanner.h" +#include "dwarfs/vfs_stat.h" #include "filter_test_data.h" #include "loremipsum.h" @@ -51,6 +55,8 @@ using namespace dwarfs; +namespace fs = std::filesystem; + namespace { std::string const default_file_hash_algo{"xxh3-128"}; @@ -199,19 +205,19 @@ void basic_end_to_end_test(std::string const& compressor, // fs.dump(std::cerr, 9); - struct ::statvfs vfsbuf; + vfs_stat vfsbuf; fs.statvfs(&vfsbuf); - EXPECT_EQ(1 << block_size_bits, vfsbuf.f_bsize); - EXPECT_EQ(1, vfsbuf.f_frsize); + EXPECT_EQ(1 << block_size_bits, vfsbuf.bsize); + EXPECT_EQ(1, vfsbuf.frsize); if (enable_nlink) { - EXPECT_EQ(access_fail ? 2046934 : 2056934, vfsbuf.f_blocks); + EXPECT_EQ(access_fail ? 2046934 : 2056934, vfsbuf.blocks); } else { - EXPECT_EQ(access_fail ? 2070390 : 2080390, vfsbuf.f_blocks); + EXPECT_EQ(access_fail ? 2070390 : 2080390, vfsbuf.blocks); } - EXPECT_EQ(11 + 2 * with_devices + with_specials, vfsbuf.f_files); - EXPECT_EQ(ST_RDONLY, vfsbuf.f_flag); - EXPECT_GT(vfsbuf.f_namemax, 0); + EXPECT_EQ(11 + 2 * with_devices + with_specials, vfsbuf.files); + EXPECT_TRUE(vfsbuf.readonly); + EXPECT_GT(vfsbuf.namemax, 0); std::ostringstream dumpss; @@ -220,36 +226,36 @@ void basic_end_to_end_test(std::string const& compressor, EXPECT_GT(dumpss.str().size(), 1000) << dumpss.str(); auto entry = fs.find("/foo.pl"); - struct ::stat st; + file_stat st; ASSERT_TRUE(entry); EXPECT_EQ(fs.getattr(*entry, &st), 0); - EXPECT_EQ(st.st_size, 23456); - EXPECT_EQ(st.st_uid, set_uid ? 0 : 1337); - EXPECT_EQ(st.st_gid, 0); - EXPECT_EQ(st.st_atime, set_time ? 4711 : keep_all_times ? 4001 : 4002); - EXPECT_EQ(st.st_mtime, set_time ? 4711 : keep_all_times ? 4002 : 4002); - EXPECT_EQ(st.st_ctime, set_time ? 4711 : keep_all_times ? 4003 : 4002); + EXPECT_EQ(st.size, 23456); + EXPECT_EQ(st.uid, set_uid ? 0 : 1337); + EXPECT_EQ(st.gid, 0); + EXPECT_EQ(st.atime, set_time ? 4711 : keep_all_times ? 4001 : 4002); + EXPECT_EQ(st.mtime, set_time ? 4711 : keep_all_times ? 4002 : 4002); + EXPECT_EQ(st.ctime, set_time ? 4711 : keep_all_times ? 4003 : 4002); int inode = fs.open(*entry); EXPECT_GE(inode, 0); - std::vector buf(st.st_size); - ssize_t rv = fs.read(inode, &buf[0], st.st_size, 0); - EXPECT_EQ(rv, st.st_size); - EXPECT_EQ(std::string(buf.begin(), buf.end()), test::loremipsum(st.st_size)); + std::vector buf(st.size); + ssize_t rv = fs.read(inode, &buf[0], st.size, 0); + EXPECT_EQ(rv, st.size); + EXPECT_EQ(std::string(buf.begin(), buf.end()), test::loremipsum(st.size)); entry = fs.find("/somelink"); ASSERT_TRUE(entry); EXPECT_EQ(fs.getattr(*entry, &st), 0); - EXPECT_EQ(st.st_size, 16); - EXPECT_EQ(st.st_uid, set_uid ? 0 : 1000); - EXPECT_EQ(st.st_gid, set_gid ? 0 : 100); - EXPECT_EQ(st.st_rdev, 0); - EXPECT_EQ(st.st_atime, set_time ? 4711 : keep_all_times ? 2001 : 2002); - EXPECT_EQ(st.st_mtime, set_time ? 4711 : keep_all_times ? 2002 : 2002); - EXPECT_EQ(st.st_ctime, set_time ? 4711 : keep_all_times ? 2003 : 2002); + EXPECT_EQ(st.size, 16); + EXPECT_EQ(st.uid, set_uid ? 0 : 1000); + EXPECT_EQ(st.gid, set_gid ? 0 : 100); + EXPECT_EQ(st.rdev, 0); + EXPECT_EQ(st.atime, set_time ? 4711 : keep_all_times ? 2001 : 2002); + EXPECT_EQ(st.mtime, set_time ? 4711 : keep_all_times ? 2002 : 2002); + EXPECT_EQ(st.ctime, set_time ? 4711 : keep_all_times ? 2003 : 2002); std::string link; EXPECT_EQ(fs.readlink(*entry, &link), 0); @@ -261,7 +267,7 @@ void basic_end_to_end_test(std::string const& compressor, ASSERT_TRUE(entry); EXPECT_EQ(fs.getattr(*entry, &st), 0); - EXPECT_EQ(st.st_size, 6); + EXPECT_EQ(st.size, 6); EXPECT_EQ(fs.readlink(*entry, &link), 0); EXPECT_EQ(link, "../foo"); @@ -271,14 +277,14 @@ void basic_end_to_end_test(std::string const& compressor, if (with_specials) { ASSERT_TRUE(entry); EXPECT_EQ(fs.getattr(*entry, &st), 0); - EXPECT_EQ(st.st_size, 0); - EXPECT_EQ(st.st_uid, set_uid ? 0 : 1000); - EXPECT_EQ(st.st_gid, set_gid ? 0 : 100); - EXPECT_TRUE(S_ISFIFO(st.st_mode)); - EXPECT_EQ(st.st_rdev, 0); - EXPECT_EQ(st.st_atime, set_time ? 4711 : keep_all_times ? 8001 : 8002); - EXPECT_EQ(st.st_mtime, set_time ? 4711 : keep_all_times ? 8002 : 8002); - EXPECT_EQ(st.st_ctime, set_time ? 4711 : keep_all_times ? 8003 : 8002); + EXPECT_EQ(st.size, 0); + EXPECT_EQ(st.uid, set_uid ? 0 : 1000); + EXPECT_EQ(st.gid, set_gid ? 0 : 100); + EXPECT_EQ(st.type(), posix_file_type::fifo); + EXPECT_EQ(st.rdev, 0); + EXPECT_EQ(st.atime, set_time ? 4711 : keep_all_times ? 8001 : 8002); + EXPECT_EQ(st.mtime, set_time ? 4711 : keep_all_times ? 8002 : 8002); + EXPECT_EQ(st.ctime, set_time ? 4711 : keep_all_times ? 8003 : 8002); } else { EXPECT_FALSE(entry); } @@ -288,11 +294,11 @@ void basic_end_to_end_test(std::string const& compressor, if (with_devices) { ASSERT_TRUE(entry); EXPECT_EQ(fs.getattr(*entry, &st), 0); - EXPECT_EQ(st.st_size, 0); - EXPECT_EQ(st.st_uid, 0); - EXPECT_EQ(st.st_gid, 0); - EXPECT_TRUE(S_ISCHR(st.st_mode)); - EXPECT_EQ(st.st_rdev, 259); + EXPECT_EQ(st.size, 0); + EXPECT_EQ(st.uid, 0); + EXPECT_EQ(st.gid, 0); + EXPECT_EQ(st.type(), posix_file_type::character); + EXPECT_EQ(st.rdev, 259); } else { EXPECT_FALSE(entry); } @@ -302,20 +308,20 @@ void basic_end_to_end_test(std::string const& compressor, if (with_devices) { ASSERT_TRUE(entry); EXPECT_EQ(fs.getattr(*entry, &st), 0); - EXPECT_EQ(st.st_size, 0); - EXPECT_EQ(st.st_uid, 0); - EXPECT_EQ(st.st_gid, 0); - EXPECT_TRUE(S_ISCHR(st.st_mode)); - EXPECT_EQ(st.st_rdev, 261); - EXPECT_EQ(st.st_atime, set_time ? 4711 - : keep_all_times ? 4000010001 - : 4000020002); - EXPECT_EQ(st.st_mtime, set_time ? 4711 - : keep_all_times ? 4000020002 - : 4000020002); - EXPECT_EQ(st.st_ctime, set_time ? 4711 - : keep_all_times ? 4000030003 - : 4000020002); + EXPECT_EQ(st.size, 0); + EXPECT_EQ(st.uid, 0); + EXPECT_EQ(st.gid, 0); + EXPECT_EQ(st.type(), posix_file_type::character); + EXPECT_EQ(st.rdev, 261); + EXPECT_EQ(st.atime, set_time ? 4711 + : keep_all_times ? 4000010001 + : 4000020002); + EXPECT_EQ(st.mtime, set_time ? 4711 + : keep_all_times ? 4000020002 + : 4000020002); + EXPECT_EQ(st.ctime, set_time ? 4711 + : keep_all_times ? 4000030003 + : 4000020002); } else { EXPECT_FALSE(entry); } @@ -368,14 +374,14 @@ void basic_end_to_end_test(std::string const& compressor, EXPECT_EQ(entry->inode_num(), e2->inode_num()); - struct ::stat st1, st2; + file_stat st1, st2; ASSERT_EQ(0, fs.getattr(*entry, &st1)); ASSERT_EQ(0, fs.getattr(*e2, &st2)); - EXPECT_EQ(st1.st_ino, st2.st_ino); + EXPECT_EQ(st1.ino, st2.ino); if (enable_nlink) { - EXPECT_EQ(2, st1.st_nlink); - EXPECT_EQ(2, st2.st_nlink); + EXPECT_EQ(2, st1.nlink); + EXPECT_EQ(2, st2.nlink); } entry = fs.find("/"); @@ -388,27 +394,27 @@ void basic_end_to_end_test(std::string const& compressor, ASSERT_TRUE(entry); EXPECT_GT(entry->inode_num(), 0); ASSERT_EQ(0, fs.getattr(*entry, &st1)); - EXPECT_EQ(23456, st1.st_size); + EXPECT_EQ(23456, st1.size); e2 = fs.find(0, "somedir"); ASSERT_TRUE(e2); ASSERT_EQ(0, fs.getattr(*e2, &st2)); - entry = fs.find(st2.st_ino, "ipsum.py"); + entry = fs.find(st2.ino, "ipsum.py"); ASSERT_TRUE(entry); ASSERT_EQ(0, fs.getattr(*entry, &st1)); - EXPECT_EQ(access_fail ? 0 : 10000, st1.st_size); + EXPECT_EQ(access_fail ? 0 : 10000, st1.size); EXPECT_EQ(0, fs.access(*entry, R_OK, 1000, 100)); entry = fs.find(0, "baz.pl"); ASSERT_TRUE(entry); EXPECT_EQ(set_uid ? EACCES : 0, fs.access(*entry, R_OK, 1337, 0)); for (auto mp : {&filesystem_v2::walk, &filesystem_v2::walk_data_order}) { - std::map entries; + std::map entries; std::vector inodes; (fs.*mp)([&](dir_entry_view e) { - struct ::stat stbuf; + file_stat stbuf; ASSERT_EQ(0, fs.getattr(e.inode(), &stbuf)); - inodes.push_back(stbuf.st_ino); + inodes.push_back(stbuf.ino); auto path = e.path(); if (!path.empty()) { path = "/" + path; @@ -420,16 +426,15 @@ void basic_end_to_end_test(std::string const& compressor, input->size() + 2 * with_devices + with_specials - 3); for (auto const& [p, st] : entries) { - struct ::stat ref; - input->lstat(p, &ref); - EXPECT_EQ(ref.st_mode, st.st_mode) << p; - EXPECT_EQ(set_uid ? 0 : ref.st_uid, st.st_uid) << p; - EXPECT_EQ(set_gid ? 0 : ref.st_gid, st.st_gid) << p; - if (!S_ISDIR(st.st_mode)) { + auto ref = input->symlink_info(p); + EXPECT_EQ(ref.mode, st.mode) << p; + EXPECT_EQ(set_uid ? 0 : ref.uid, st.uid) << p; + EXPECT_EQ(set_gid ? 0 : ref.gid, st.gid) << p; + if (!st.is_directory()) { if (input->access(p, R_OK) == 0) { - EXPECT_EQ(ref.st_size, st.st_size) << p; + EXPECT_EQ(ref.size, st.size) << p; } else { - EXPECT_EQ(0, st.st_size) << p; + EXPECT_EQ(0, st.size) << p; } } } @@ -571,19 +576,19 @@ TEST_P(packing_test, regression_empty_fs) { filesystem_v2 fs(lgr, mm, opts); - struct ::statvfs vfsbuf; + vfs_stat vfsbuf; fs.statvfs(&vfsbuf); - EXPECT_EQ(1, vfsbuf.f_files); - EXPECT_EQ(0, vfsbuf.f_blocks); + EXPECT_EQ(1, vfsbuf.files); + EXPECT_EQ(0, vfsbuf.blocks); size_t num = 0; fs.walk([&](dir_entry_view e) { ++num; - struct ::stat stbuf; + file_stat stbuf; ASSERT_EQ(0, fs.getattr(e.inode(), &stbuf)); - EXPECT_TRUE(S_ISDIR(stbuf.st_mode)); + EXPECT_TRUE(stbuf.is_directory()); }); EXPECT_EQ(1, num); @@ -649,11 +654,11 @@ TEST(block_manager, regression_block_boundary) { filesystem_v2 fs(lgr, mm, opts); - struct ::statvfs vfsbuf; + vfs_stat vfsbuf; fs.statvfs(&vfsbuf); - EXPECT_EQ(2, vfsbuf.f_files); - EXPECT_EQ(size, vfsbuf.f_blocks); + EXPECT_EQ(2, vfsbuf.files); + EXPECT_EQ(size, vfsbuf.blocks); } EXPECT_TRUE(std::is_sorted(fs_sizes.begin(), fs_sizes.end())) @@ -721,26 +726,26 @@ TEST_P(compression_regression, github45) { filesystem_v2 fs(lgr, mm, opts); - struct ::statvfs vfsbuf; + vfs_stat vfsbuf; fs.statvfs(&vfsbuf); - EXPECT_EQ(3, vfsbuf.f_files); - EXPECT_EQ(2 * file_size, vfsbuf.f_blocks); + EXPECT_EQ(3, vfsbuf.files); + EXPECT_EQ(2 * file_size, vfsbuf.blocks); auto check_file = [&](char const* name, std::string const& contents) { auto entry = fs.find(name); - struct ::stat st; + file_stat st; ASSERT_TRUE(entry); EXPECT_EQ(fs.getattr(*entry, &st), 0); - EXPECT_EQ(st.st_size, file_size); + EXPECT_EQ(st.size, file_size); int inode = fs.open(*entry); EXPECT_GE(inode, 0); - std::vector buf(st.st_size); - ssize_t rv = fs.read(inode, &buf[0], st.st_size, 0); - EXPECT_EQ(rv, st.st_size); + std::vector buf(st.size); + ssize_t rv = fs.read(inode, &buf[0], st.size, 0); + EXPECT_EQ(rv, st.size); EXPECT_EQ(std::string(buf.begin(), buf.end()), contents); }; @@ -824,30 +829,19 @@ TEST_P(filter, filesystem) { auto input = std::make_shared(); for (auto const& [stat, name] : dwarfs::test::test_dirtree()) { - struct ::stat st; - - std::memset(&st, 0, sizeof(st)); - - st.st_ino = stat.st_ino; - st.st_mode = stat.st_mode; - st.st_nlink = stat.st_nlink; - st.st_uid = stat.st_uid; - st.st_gid = stat.st_gid; - st.st_size = stat.st_size; - st.st_atime = stat.atime; - st.st_mtime = stat.mtime; - st.st_ctime = stat.ctime; - st.st_rdev = stat.st_rdev; - auto path = name.substr(name.size() == 5 ? 5 : 6); - if (S_ISREG(st.st_mode)) { - input->add(path, st, - [size = st.st_size] { return test::loremipsum(size); }); - } else if (S_ISLNK(st.st_mode)) { - input->add(path, st, test::loremipsum(st.st_size)); - } else { - input->add(path, st); + switch (stat.type()) { + case posix_file_type::regular: + input->add(path, stat, + [size = stat.size] { return test::loremipsum(size); }); + break; + case posix_file_type::symlink: + input->add(path, stat, test::loremipsum(stat.size)); + break; + default: + input->add(path, stat); + break; } } diff --git a/test/dwarfs_compat.cpp b/test/dwarfs_compat.cpp index 7dd74d2a..6d9cca0e 100644 --- a/test/dwarfs_compat.cpp +++ b/test/dwarfs_compat.cpp @@ -31,13 +31,12 @@ #include #include -#include - #include #include #include #include "dwarfs/block_compressor.h" +#include "dwarfs/file_stat.h" #include "dwarfs/filesystem_extractor.h" #include "dwarfs/filesystem_v2.h" #include "dwarfs/filesystem_writer.h" @@ -45,6 +44,7 @@ #include "dwarfs/mmap.h" #include "dwarfs/options.h" #include "dwarfs/progress.h" +#include "dwarfs/vfs_stat.h" #include "dwarfs/worker_group.h" #include "mmap_mock.h" @@ -797,11 +797,12 @@ std::vector headers_v2{ "DWARFS\x02" + format_sh + "DWARFS\x02", }; -struct ::stat make_stat(::mode_t mode, ::off_t size) { - struct ::stat st; +file_stat make_stat(posix_file_type::value type, file_stat::perms_type perms, + file_stat::off_type size) { + file_stat st; std::memset(&st, 0, sizeof(st)); - st.st_mode = mode; - st.st_size = size; + st.mode = type | perms; + st.size = size; return st; } @@ -810,15 +811,15 @@ void check_compat(logger& lgr, filesystem_v2 const& fs, bool has_devices = not(version == "0.2.0" or version == "0.2.3"); bool has_ac_time = version == "0.2.0" or version == "0.2.3"; - struct ::statvfs vfsbuf; + vfs_stat vfsbuf; fs.statvfs(&vfsbuf); - EXPECT_EQ(1024, vfsbuf.f_bsize); - EXPECT_EQ(1, vfsbuf.f_frsize); - EXPECT_EQ(10614, vfsbuf.f_blocks); - EXPECT_EQ(33 + 3 * has_devices, vfsbuf.f_files); - EXPECT_EQ(ST_RDONLY, vfsbuf.f_flag); - EXPECT_GT(vfsbuf.f_namemax, 0); + EXPECT_EQ(1024, vfsbuf.bsize); + EXPECT_EQ(1, vfsbuf.frsize); + EXPECT_EQ(10614, vfsbuf.blocks); + EXPECT_EQ(33 + 3 * has_devices, vfsbuf.files); + EXPECT_TRUE(vfsbuf.readonly); + EXPECT_GT(vfsbuf.namemax, 0); auto json = fs.serialize_metadata_as_json(true); EXPECT_GT(json.size(), 1000) << json; @@ -828,18 +829,18 @@ void check_compat(logger& lgr, filesystem_v2 const& fs, EXPECT_GT(dumpss.str().size(), 1000) << dumpss.str(); auto entry = fs.find("/format.sh"); - struct ::stat st; + file_stat st; ASSERT_TRUE(entry); EXPECT_EQ(0, fs.getattr(*entry, &st)); - EXPECT_EQ(94, st.st_size); - EXPECT_EQ(S_IFREG | 0755, st.st_mode); - EXPECT_EQ(1000, st.st_uid); - EXPECT_EQ(100, st.st_gid); - EXPECT_EQ(1606256045, st.st_mtime); + EXPECT_EQ(94, st.size); + EXPECT_EQ(S_IFREG | 0755, st.mode); + EXPECT_EQ(1000, st.uid); + EXPECT_EQ(100, st.gid); + EXPECT_EQ(1606256045, st.mtime); if (has_ac_time) { - EXPECT_EQ(1616013831, st.st_atime); - EXPECT_EQ(1616013816, st.st_ctime); + EXPECT_EQ(1616013831, st.atime); + EXPECT_EQ(1616013816, st.ctime); } EXPECT_EQ(0, fs.access(*entry, R_OK, 1000, 0)); @@ -847,9 +848,9 @@ void check_compat(logger& lgr, filesystem_v2 const& fs, auto inode = fs.open(*entry); EXPECT_GE(inode, 0); - std::vector buf(st.st_size); - auto rv = fs.read(inode, &buf[0], st.st_size, 0); - EXPECT_EQ(rv, st.st_size); + std::vector buf(st.size); + auto rv = fs.read(inode, &buf[0], st.size, 0); + EXPECT_EQ(rv, st.size); EXPECT_EQ(format_sh, std::string(buf.begin(), buf.end())); entry = fs.find("/foo/bad"); @@ -883,69 +884,71 @@ void check_compat(logger& lgr, filesystem_v2 const& fs, EXPECT_EQ(expected, names); - std::map ref_entries{ - {"", make_stat(S_IFDIR | 0755, 8)}, - {"bench.sh", make_stat(S_IFREG | 0644, 1517)}, - {"dev", make_stat(S_IFDIR | 0755, 2)}, - {"dev/null", make_stat(S_IFCHR | 0666, 0)}, - {"dev/zero", make_stat(S_IFCHR | 0666, 0)}, - {"empty", make_stat(S_IFDIR | 0755, 1)}, - {"empty/alsoempty", make_stat(S_IFDIR | 0755, 0)}, - {"foo", make_stat(S_IFDIR | 0755, 5)}, - {"foo/1", make_stat(S_IFDIR | 0755, 2)}, - {"foo/1/2", make_stat(S_IFDIR | 0755, 2)}, - {"foo/1/2/3", make_stat(S_IFDIR | 0755, 3)}, - {"foo/1/2/3/4", make_stat(S_IFDIR | 0755, 2)}, - {"foo/1/2/3/4/5", make_stat(S_IFDIR | 0755, 2)}, - {"foo/1/2/3/4/5/6", make_stat(S_IFDIR | 0755, 1)}, - {"foo/1/2/3/4/5/6/7", make_stat(S_IFDIR | 0755, 1)}, - {"foo/1/2/3/4/5/6/7/8", make_stat(S_IFDIR | 0755, 1)}, - {"foo/1/2/3/4/5/6/7/8/9", make_stat(S_IFDIR | 0755, 13)}, - {"foo/1/2/3/4/5/6/7/8/9/a", make_stat(S_IFREG | 0644, 2)}, - {"foo/1/2/3/4/5/6/7/8/9/b", make_stat(S_IFREG | 0644, 2)}, - {"foo/1/2/3/4/5/6/7/8/9/blubb", make_stat(S_IFREG | 0644, 1517)}, - {"foo/1/2/3/4/5/6/7/8/9/c", make_stat(S_IFREG | 0644, 2)}, - {"foo/1/2/3/4/5/6/7/8/9/d", make_stat(S_IFREG | 0644, 2)}, - {"foo/1/2/3/4/5/6/7/8/9/e", make_stat(S_IFREG | 0644, 2)}, - {"foo/1/2/3/4/5/6/7/8/9/f", make_stat(S_IFREG | 0644, 2)}, - {"foo/1/2/3/4/5/6/7/8/9/g", make_stat(S_IFREG | 0644, 2)}, - {"foo/1/2/3/4/5/6/7/8/9/h", make_stat(S_IFREG | 0644, 2)}, - {"foo/1/2/3/4/5/6/7/8/9/i", make_stat(S_IFREG | 0644, 2)}, - {"foo/1/2/3/4/5/6/7/8/9/j", make_stat(S_IFREG | 0644, 2)}, - {"foo/1/2/3/4/5/6/7/8/9/k", make_stat(S_IFREG | 0644, 2)}, - {"foo/1/2/3/4/5/6/7/8/9/l", make_stat(S_IFREG | 0644, 2)}, - {"foo/1/2/3/4/5/z", make_stat(S_IFREG | 0644, 1517)}, - {"foo/1/2/3/4/y", make_stat(S_IFREG | 0644, 1517)}, - {"foo/1/2/3/copy.sh", make_stat(S_IFREG | 0755, 94)}, - {"foo/1/2/3/x", make_stat(S_IFREG | 0644, 1517)}, - {"foo/1/2/xxx.sh", make_stat(S_IFREG | 0755, 94)}, - {"foo/1/fmt.sh", make_stat(S_IFREG | 0755, 94)}, - {"foo/bad", make_stat(S_IFLNK | 0777, 6)}, - {"foo/bar", make_stat(S_IFREG | 0644, 0)}, - {"foo/bla.sh", make_stat(S_IFREG | 0644, 1517)}, - {"foo/pipe", make_stat(S_IFIFO | 0644, 0)}, - {"foobar", make_stat(S_IFLNK | 0777, 7)}, - {"format.sh", make_stat(S_IFREG | 0755, 94)}, - {"perl-exec.sh", make_stat(S_IFREG | 0644, 87)}, - {"test.py", make_stat(S_IFREG | 0644, 1012)}, + std::map ref_entries{ + {"", make_stat(posix_file_type::directory, 0755, 8)}, + {"bench.sh", make_stat(posix_file_type::regular, 0644, 1517)}, + {"dev", make_stat(posix_file_type::directory, 0755, 2)}, + {"dev/null", make_stat(posix_file_type::character, 0666, 0)}, + {"dev/zero", make_stat(posix_file_type::character, 0666, 0)}, + {"empty", make_stat(posix_file_type::directory, 0755, 1)}, + {"empty/alsoempty", make_stat(posix_file_type::directory, 0755, 0)}, + {"foo", make_stat(posix_file_type::directory, 0755, 5)}, + {"foo/1", make_stat(posix_file_type::directory, 0755, 2)}, + {"foo/1/2", make_stat(posix_file_type::directory, 0755, 2)}, + {"foo/1/2/3", make_stat(posix_file_type::directory, 0755, 3)}, + {"foo/1/2/3/4", make_stat(posix_file_type::directory, 0755, 2)}, + {"foo/1/2/3/4/5", make_stat(posix_file_type::directory, 0755, 2)}, + {"foo/1/2/3/4/5/6", make_stat(posix_file_type::directory, 0755, 1)}, + {"foo/1/2/3/4/5/6/7", make_stat(posix_file_type::directory, 0755, 1)}, + {"foo/1/2/3/4/5/6/7/8", make_stat(posix_file_type::directory, 0755, 1)}, + {"foo/1/2/3/4/5/6/7/8/9", + make_stat(posix_file_type::directory, 0755, 13)}, + {"foo/1/2/3/4/5/6/7/8/9/a", make_stat(posix_file_type::regular, 0644, 2)}, + {"foo/1/2/3/4/5/6/7/8/9/b", make_stat(posix_file_type::regular, 0644, 2)}, + {"foo/1/2/3/4/5/6/7/8/9/blubb", + make_stat(posix_file_type::regular, 0644, 1517)}, + {"foo/1/2/3/4/5/6/7/8/9/c", make_stat(posix_file_type::regular, 0644, 2)}, + {"foo/1/2/3/4/5/6/7/8/9/d", make_stat(posix_file_type::regular, 0644, 2)}, + {"foo/1/2/3/4/5/6/7/8/9/e", make_stat(posix_file_type::regular, 0644, 2)}, + {"foo/1/2/3/4/5/6/7/8/9/f", make_stat(posix_file_type::regular, 0644, 2)}, + {"foo/1/2/3/4/5/6/7/8/9/g", make_stat(posix_file_type::regular, 0644, 2)}, + {"foo/1/2/3/4/5/6/7/8/9/h", make_stat(posix_file_type::regular, 0644, 2)}, + {"foo/1/2/3/4/5/6/7/8/9/i", make_stat(posix_file_type::regular, 0644, 2)}, + {"foo/1/2/3/4/5/6/7/8/9/j", make_stat(posix_file_type::regular, 0644, 2)}, + {"foo/1/2/3/4/5/6/7/8/9/k", make_stat(posix_file_type::regular, 0644, 2)}, + {"foo/1/2/3/4/5/6/7/8/9/l", make_stat(posix_file_type::regular, 0644, 2)}, + {"foo/1/2/3/4/5/z", make_stat(posix_file_type::regular, 0644, 1517)}, + {"foo/1/2/3/4/y", make_stat(posix_file_type::regular, 0644, 1517)}, + {"foo/1/2/3/copy.sh", make_stat(posix_file_type::regular, 0755, 94)}, + {"foo/1/2/3/x", make_stat(posix_file_type::regular, 0644, 1517)}, + {"foo/1/2/xxx.sh", make_stat(posix_file_type::regular, 0755, 94)}, + {"foo/1/fmt.sh", make_stat(posix_file_type::regular, 0755, 94)}, + {"foo/bad", make_stat(posix_file_type::symlink, 0777, 6)}, + {"foo/bar", make_stat(posix_file_type::regular, 0644, 0)}, + {"foo/bla.sh", make_stat(posix_file_type::regular, 0644, 1517)}, + {"foo/pipe", make_stat(posix_file_type::fifo, 0644, 0)}, + {"foobar", make_stat(posix_file_type::symlink, 0777, 7)}, + {"format.sh", make_stat(posix_file_type::regular, 0755, 94)}, + {"perl-exec.sh", make_stat(posix_file_type::regular, 0644, 87)}, + {"test.py", make_stat(posix_file_type::regular, 0644, 1012)}, }; if (!has_devices) { for (auto special : {"dev/null", "dev/zero", "foo/pipe"}) { ref_entries.erase(special); } - ref_entries["dev"].st_size -= 2; - ref_entries["foo"].st_size -= 1; + ref_entries["dev"].size -= 2; + ref_entries["foo"].size -= 1; } for (auto mp : {&filesystem_v2::walk, &filesystem_v2::walk_data_order}) { - std::map entries; + std::map entries; std::vector inodes; (fs.*mp)([&](dir_entry_view e) { - struct ::stat stbuf; + file_stat stbuf; ASSERT_EQ(0, fs.getattr(e.inode(), &stbuf)); - inodes.push_back(stbuf.st_ino); + inodes.push_back(stbuf.ino); EXPECT_TRUE(entries.emplace(e.path(), stbuf).second); }); @@ -955,15 +958,15 @@ void check_compat(logger& lgr, filesystem_v2 const& fs, auto it = ref_entries.find(p); EXPECT_TRUE(it != ref_entries.end()) << p; if (it != ref_entries.end()) { - EXPECT_EQ(it->second.st_mode, st.st_mode) << p; - if (S_ISCHR(st.st_mode)) { - EXPECT_EQ(0, st.st_uid) << p; - EXPECT_EQ(0, st.st_gid) << p; + EXPECT_EQ(it->second.mode, st.mode) << p; + if (st.type() == posix_file_type::character) { + EXPECT_EQ(0, st.uid) << p; + EXPECT_EQ(0, st.gid) << p; } else { - EXPECT_EQ(1000, st.st_uid) << p; - EXPECT_EQ(100, st.st_gid) << p; + EXPECT_EQ(1000, st.uid) << p; + EXPECT_EQ(100, st.gid) << p; } - EXPECT_EQ(it->second.st_size, st.st_size) << p; + EXPECT_EQ(it->second.size, st.size) << p; } } } @@ -1005,12 +1008,12 @@ void check_compat(logger& lgr, filesystem_v2 const& fs, if (ri != ref_entries.end()) { auto const& st = ri->second; - EXPECT_EQ(kv["mode"], fmt::format("{0:o}", st.st_mode & 0777)); + EXPECT_EQ(kv["mode"], fmt::format("{0:o}", st.mode & 0777)); EXPECT_EQ(std::stoi(kv["uid"]), kv["type"] == "char" ? 0 : 1000); EXPECT_EQ(std::stoi(kv["gid"]), kv["type"] == "char" ? 0 : 100); if (kv["type"] == "file") { - EXPECT_EQ(std::stoi(kv["size"]), st.st_size); + EXPECT_EQ(std::stoi(kv["size"]), st.size); } } } diff --git a/test/dwarfs_tools.cpp b/test/dwarfs_tools.cpp index b7f57b1f..f125bac3 100644 --- a/test/dwarfs_tools.cpp +++ b/test/dwarfs_tools.cpp @@ -188,6 +188,7 @@ bool wait_until_file_ready(std::filesystem::path const& path, } bool check_readonly(std::filesystem::path const& p, bool readonly = false) { + // TODO: std::filesystem struct ::stat buf; if (::stat(p.c_str(), &buf) != 0) { throw std::runtime_error("could not stat " + p.string()); @@ -211,6 +212,7 @@ bool check_readonly(std::filesystem::path const& p, bool readonly = false) { } ::nlink_t num_hardlinks(std::filesystem::path const& p) { + // TODO: std::filesystem struct ::stat buf; if (::stat(p.c_str(), &buf) != 0) { throw std::runtime_error("could not stat " + p.string()); diff --git a/test/test_dirtree.cpp b/test/test_dirtree.cpp index 4fb508dd..57518b71 100644 --- a/test/test_dirtree.cpp +++ b/test/test_dirtree.cpp @@ -1,10 +1,12 @@ +#include + #include "test_helpers.h" namespace dwarfs::test { std::span const> test_dirtree() { // clang-format off - static std::vector> const dt{ + static std::array, 337> const dt{{ {{46193947, 040755, 1, 0, 0, 10, 27, 1667125429, 1666544836, 1666894334}, "stage"}, {{46193949, 040755, 1, 0, 0, 10, 27, 1667125429, 1666544836, 1666894334}, "stage/run"}, {{46193951, 040755, 1, 0, 0, 10, 27, 1667125429, 1666544836, 1666894334}, "stage/boot"}, @@ -342,7 +344,7 @@ std::span const> test_dirtree() { {{46255143, 060640, 1, 0, 6, 0, 27, 1666894345, 1666544853, 1666894345}, "stage/dev/hda20"}, {{46255250, 060640, 1, 0, 6, 0, 27, 1666894345, 1666544853, 1666894345}, "stage/dev/sdd"}, {{46255234, 020600, 1, 0, 0, 0, 27, 1666894345, 1666544853, 1666894345}, "stage/dev/input/event23"}, - }; + }}; // clang-format on return dt; } diff --git a/test/test_helpers.cpp b/test/test_helpers.cpp index 718c4d78..a5bfc6b0 100644 --- a/test/test_helpers.cpp +++ b/test/test_helpers.cpp @@ -25,9 +25,6 @@ #include #include -#include -#include - #include #include "dwarfs/overloaded.h" @@ -38,9 +35,31 @@ namespace dwarfs { namespace test { +namespace fs = std::filesystem; + +namespace { + +file_stat make_file_stat(simplestat const& ss) { + file_stat rv; + ::memset(&rv, 0, sizeof(rv)); + rv.ino = ss.ino; + rv.nlink = ss.nlink; + rv.mode = ss.mode; + rv.uid = ss.uid; + rv.gid = ss.gid; + rv.rdev = ss.rdev; + rv.size = ss.size; + rv.atime = ss.atime; + rv.mtime = ss.mtime; + rv.ctime = ss.ctime; + return rv; +} + +} // namespace + struct os_access_mock::mock_dirent { std::string name; - struct ::stat stat; + simplestat status; std::variant, std::unique_ptr> v; @@ -50,7 +69,7 @@ struct os_access_mock::mock_dirent { mock_dirent* find(std::string const& name); void - add(std::string const& name, struct ::stat const& st, + add(std::string const& name, simplestat const& st, std::variant, std::unique_ptr> var); @@ -64,7 +83,7 @@ struct os_access_mock::mock_directory { mock_dirent* find(std::string const& name); void - add(std::string const& name, struct ::stat const& st, + add(std::string const& name, simplestat const& st, std::variant, std::unique_ptr> var); @@ -84,7 +103,7 @@ auto os_access_mock::mock_dirent::find(std::string const& name) } void os_access_mock::mock_dirent::add( - std::string const& name, struct ::stat const& st, + std::string const& name, simplestat const& st, std::variant, std::unique_ptr> var) { @@ -108,13 +127,13 @@ auto os_access_mock::mock_directory::find(std::string const& name) } void os_access_mock::mock_directory::add( - std::string const& name, struct ::stat const& st, + std::string const& name, simplestat const& st, std::variant, std::unique_ptr> var) { assert(!find(name)); - if (S_ISDIR(st.st_mode)) { + if (st.type() == posix_file_type::directory) { assert(std::holds_alternative>(var)); } else { assert(!std::holds_alternative>(var)); @@ -122,7 +141,7 @@ void os_access_mock::mock_directory::add( auto& de = ent.emplace_back(); de.name = name; - de.stat = st; + de.status = st; de.v = std::move(var); } @@ -132,7 +151,7 @@ class dir_reader_mock : public dir_reader { : files_(files) , index_(0) {} - bool read(std::string& name) const override { + bool read(std::string& name) override { if (index_ < files_.size()) { name = files_[index_++]; return true; @@ -143,7 +162,7 @@ class dir_reader_mock : public dir_reader { private: std::vector files_; - mutable size_t index_; + size_t index_; }; os_access_mock::os_access_mock() = default; @@ -151,28 +170,49 @@ os_access_mock::~os_access_mock() = default; std::shared_ptr os_access_mock::create_test_instance() { static const std::vector> statmap{ - {"", {1, S_IFDIR | 0777, 1, 1000, 100, 0, 0, 1, 2, 3}}, - {"test.pl", {3, S_IFREG | 0644, 2, 1000, 100, 0, 0, 1001, 1002, 1003}}, - {"somelink", {4, S_IFLNK | 0777, 1, 1000, 100, 16, 0, 2001, 2002, 2003}}, - {"somedir", {5, S_IFDIR | 0777, 1, 1000, 100, 0, 0, 3001, 3002, 3003}}, - {"foo.pl", {6, S_IFREG | 0600, 2, 1337, 0, 23456, 0, 4001, 4002, 4003}}, - {"bar.pl", {6, S_IFREG | 0600, 2, 1337, 0, 23456, 0, 4001, 4002, 4003}}, - {"baz.pl", {16, S_IFREG | 0600, 2, 1337, 0, 23456, 0, 8001, 8002, 8003}}, + {"", {1, posix_file_type::directory | 0777, 1, 1000, 100, 0, 0, 1, 2, 3}}, + {"test.pl", + {3, posix_file_type::regular | 0644, 2, 1000, 100, 0, 0, 1001, 1002, + 1003}}, + {"somelink", + {4, posix_file_type::symlink | 0777, 1, 1000, 100, 16, 0, 2001, 2002, + 2003}}, + {"somedir", + {5, posix_file_type::directory | 0777, 1, 1000, 100, 0, 0, 3001, 3002, + 3003}}, + {"foo.pl", + {6, posix_file_type::regular | 0600, 2, 1337, 0, 23456, 0, 4001, 4002, + 4003}}, + {"bar.pl", + {6, posix_file_type::regular | 0600, 2, 1337, 0, 23456, 0, 4001, 4002, + 4003}}, + {"baz.pl", + {16, posix_file_type::regular | 0600, 2, 1337, 0, 23456, 0, 8001, 8002, + 8003}}, {"ipsum.txt", - {7, S_IFREG | 0644, 1, 1000, 100, 2000000, 0, 5001, 5002, 5003}}, + {7, posix_file_type::regular | 0644, 1, 1000, 100, 2000000, 0, 5001, + 5002, 5003}}, {"somedir/ipsum.py", - {9, S_IFREG | 0644, 1, 1000, 100, 10000, 0, 6001, 6002, 6003}}, + {9, posix_file_type::regular | 0644, 1, 1000, 100, 10000, 0, 6001, 6002, + 6003}}, {"somedir/bad", - {10, S_IFLNK | 0777, 1, 1000, 100, 6, 0, 7001, 7002, 7003}}, + {10, posix_file_type::symlink | 0777, 1, 1000, 100, 6, 0, 7001, 7002, + 7003}}, {"somedir/pipe", - {12, S_IFIFO | 0644, 1, 1000, 100, 0, 0, 8001, 8002, 8003}}, - {"somedir/null", {13, S_IFCHR | 0666, 1, 0, 0, 0, 259, 9001, 9002, 9003}}, + {12, posix_file_type::fifo | 0644, 1, 1000, 100, 0, 0, 8001, 8002, + 8003}}, + {"somedir/null", + {13, posix_file_type::character | 0666, 1, 0, 0, 0, 259, 9001, 9002, + 9003}}, {"somedir/zero", - {14, S_IFCHR | 0666, 1, 0, 0, 0, 261, 4000010001, 4000020002, - 4000030003}}, + {14, posix_file_type::character | 0666, 1, 0, 0, 0, 261, 4000010001, + 4000020002, 4000030003}}, {"somedir/empty", - {212, S_IFREG | 0644, 1, 1000, 100, 0, 0, 8101, 8102, 8103}}, - {"empty", {210, S_IFREG | 0644, 3, 1337, 0, 0, 0, 8201, 8202, 8203}}, + {212, posix_file_type::regular | 0644, 1, 1000, 100, 0, 0, 8101, 8102, + 8103}}, + {"empty", + {210, posix_file_type::regular | 0644, 3, 1337, 0, 0, 0, 8201, 8202, + 8203}}, }; static std::map linkmap{ @@ -183,91 +223,78 @@ std::shared_ptr os_access_mock::create_test_instance() { auto m = std::make_shared(); for (auto const& kv : statmap) { - const auto& sst = kv.second; - struct ::stat st; + const auto& stat = kv.second; - std::memset(&st, 0, sizeof(st)); - - st.st_ino = sst.st_ino; - st.st_mode = sst.st_mode; - st.st_nlink = sst.st_nlink; - st.st_uid = sst.st_uid; - st.st_gid = sst.st_gid; - st.st_size = sst.st_size; - st.st_atime = sst.atime; - st.st_mtime = sst.mtime; - st.st_ctime = sst.ctime; - st.st_rdev = sst.st_rdev; - - if (S_ISREG(st.st_mode)) { - m->add(kv.first, st, [size = st.st_size] { return loremipsum(size); }); - } else if (S_ISLNK(st.st_mode)) { - m->add(kv.first, st, linkmap.at(kv.first)); - } else { - m->add(kv.first, st); + switch (stat.type()) { + case posix_file_type::regular: + m->add(kv.first, stat, [size = stat.size] { return loremipsum(size); }); + break; + case posix_file_type::symlink: + m->add(kv.first, stat, linkmap.at(kv.first)); + break; + default: + m->add(kv.first, stat); + break; } } return m; } -void os_access_mock::add(std::filesystem::path const& path, - struct ::stat const& st) { +void os_access_mock::add(fs::path const& path, simplestat const& st) { add_internal(path, st, std::monostate{}); } -void os_access_mock::add(std::filesystem::path const& path, - struct ::stat const& st, std::string const& contents) { +void os_access_mock::add(fs::path const& path, simplestat const& st, + std::string const& contents) { add_internal(path, st, contents); } -void os_access_mock::add(std::filesystem::path const& path, - struct ::stat const& st, +void os_access_mock::add(fs::path const& path, simplestat const& st, std::function generator) { add_internal(path, st, generator); } -void os_access_mock::add_dir(std::filesystem::path const& path) { - struct ::stat st; +void os_access_mock::add_dir(fs::path const& path) { + simplestat st; std::memset(&st, 0, sizeof(st)); - st.st_ino = ino_++; - st.st_mode = S_IFDIR | 0755; - st.st_uid = 1000; - st.st_gid = 100; + st.ino = ino_++; + st.mode = posix_file_type::directory | 0755; + st.uid = 1000; + st.gid = 100; add(path, st); } -void os_access_mock::add_file(std::filesystem::path const& path, size_t size) { - struct ::stat st; +void os_access_mock::add_file(fs::path const& path, size_t size) { + simplestat st; std::memset(&st, 0, sizeof(st)); - st.st_ino = ino_++; - st.st_mode = S_IFREG | 0644; - st.st_uid = 1000; - st.st_gid = 100; - st.st_size = size; + st.ino = ino_++; + st.mode = posix_file_type::regular | 0644; + st.uid = 1000; + st.gid = 100; + st.size = size; add(path, st, [size] { return loremipsum(size); }); } -void os_access_mock::add_file(std::filesystem::path const& path, +void os_access_mock::add_file(fs::path const& path, std::string const& contents) { - struct ::stat st; + simplestat st; std::memset(&st, 0, sizeof(st)); - st.st_ino = ino_++; - st.st_mode = S_IFREG | 0644; - st.st_uid = 1000; - st.st_gid = 100; - st.st_size = contents.size(); + st.ino = ino_++; + st.mode = posix_file_type::regular | 0644; + st.uid = 1000; + st.gid = 100; + st.size = contents.size(); add(path, st, contents); } -void os_access_mock::set_access_fail(std::filesystem::path const& path) { +void os_access_mock::set_access_fail(fs::path const& path) { access_fail_set_.emplace(path.string()); } size_t os_access_mock::size() const { return root_ ? root_->size() : 0; } -std::vector -os_access_mock::splitpath(std::filesystem::path const& path) { +std::vector os_access_mock::splitpath(fs::path const& path) { std::vector parts; folly::split('/', path.native(), parts); while (!parts.empty() && parts.front().empty()) { @@ -276,8 +303,7 @@ os_access_mock::splitpath(std::filesystem::path const& path) { return parts; } -auto os_access_mock::find(std::filesystem::path const& path) const - -> mock_dirent* { +auto os_access_mock::find(fs::path const& path) const -> mock_dirent* { return find(splitpath(path)); } @@ -286,7 +312,7 @@ auto os_access_mock::find(std::vector parts) const assert(root_); auto* de = root_.get(); while (!parts.empty()) { - if (!S_ISDIR(de->stat.st_mode)) { + if (de->status.type() != posix_file_type::directory) { return nullptr; } de = de->find(parts.front()); @@ -299,22 +325,23 @@ auto os_access_mock::find(std::vector parts) const } void os_access_mock::add_internal( - std::filesystem::path const& path, struct ::stat const& st, + fs::path const& path, simplestat const& st, std::variant, std::unique_ptr> var) { auto parts = splitpath(path); - if (S_ISDIR(st.st_mode) && std::holds_alternative(var)) { + if (st.type() == posix_file_type::directory && + std::holds_alternative(var)) { var = std::make_unique(); } if (parts.empty()) { assert(!root_); - assert(S_ISDIR(st.st_mode)); + assert(st.type() == posix_file_type::directory); assert(std::holds_alternative>(var)); root_ = std::make_unique(); - root_->stat = st; + root_->status = st; root_->v = std::move(var); } else { auto name = parts.back(); @@ -326,8 +353,9 @@ void os_access_mock::add_internal( } std::shared_ptr -os_access_mock::opendir(const std::string& path) const { - if (auto de = find(path); de && S_ISDIR(de->stat.st_mode)) { +os_access_mock::opendir(std::string const& path) const { + if (auto de = find(path); + de && de->status.type() == posix_file_type::directory) { std::vector files{".", ".."}; for (auto const& e : std::get>(de->v)->ent) { @@ -339,15 +367,17 @@ os_access_mock::opendir(const std::string& path) const { throw std::runtime_error("oops"); } -void os_access_mock::lstat(const std::string& path, struct ::stat* st) const { +file_stat os_access_mock::symlink_info(std::string const& path) const { if (auto de = find(path)) { - std::memcpy(st, &de->stat, sizeof(*st)); + return make_file_stat(de->status); } + + throw std::runtime_error("oops"); } -std::string -os_access_mock::readlink(const std::string& path, size_t size) const { - if (auto de = find(path); de && S_ISLNK(de->stat.st_mode)) { +std::string os_access_mock::read_symlink(std::string const& path) const { + if (auto de = find(path); + de && de->status.type() == posix_file_type::symlink) { return std::get(de->v); } @@ -355,8 +385,9 @@ os_access_mock::readlink(const std::string& path, size_t size) const { } std::shared_ptr -os_access_mock::map_file(const std::string& path, size_t size) const { - if (auto de = find(path); de && S_ISREG(de->stat.st_mode)) { +os_access_mock::map_file(std::string const& path, size_t size) const { + if (auto de = find(path); + de && de->status.type() == posix_file_type::regular) { return std::make_shared(std::visit( overloaded{ [this](std::string const& str) { return str; }, @@ -371,11 +402,11 @@ os_access_mock::map_file(const std::string& path, size_t size) const { throw std::runtime_error("oops"); } -int os_access_mock::access(const std::string& path, int) const { +int os_access_mock::access(std::string const& path, int) const { return access_fail_set_.count(path) ? -1 : 0; } -std::optional find_binary(std::string_view name) { +std::optional find_binary(std::string_view name) { auto path_str = std::getenv("PATH"); if (!path_str) { return std::nullopt; @@ -385,8 +416,8 @@ std::optional find_binary(std::string_view name) { folly::split(':', path_str, path); for (auto dir : path) { - auto cand = std::filesystem::path(dir) / name; - if (std::filesystem::exists(cand) and ::access(cand.c_str(), X_OK) == 0) { + auto cand = fs::path(dir) / name; + if (fs::exists(cand) and ::access(cand.c_str(), X_OK) == 0) { return cand; } } diff --git a/test/test_helpers.h b/test/test_helpers.h index 4087ea2c..c01915b9 100644 --- a/test/test_helpers.h +++ b/test/test_helpers.h @@ -33,13 +33,29 @@ #include #include +#include "dwarfs/file_stat.h" #include "dwarfs/os_access.h" #include "dwarfs/script.h" -struct stat; - namespace dwarfs::test { +struct simplestat { + file_stat::ino_type ino; + file_stat::mode_type mode; + file_stat::nlink_type nlink; + file_stat::uid_type uid; + file_stat::gid_type gid; + file_stat::off_type size; + file_stat::dev_type rdev; + file_stat::time_type atime; + file_stat::time_type mtime; + file_stat::time_type ctime; + + posix_file_type::value type() const { + return static_cast(mode & posix_file_type::mask); + } +}; + class os_access_mock : public os_access { public: os_access_mock(); @@ -49,10 +65,10 @@ class os_access_mock : public os_access { size_t size() const; - void add(std::filesystem::path const& path, struct ::stat const& st); - void add(std::filesystem::path const& path, struct ::stat const& st, + void add(std::filesystem::path const& path, simplestat const& st); + void add(std::filesystem::path const& path, simplestat const& st, std::string const& contents); - void add(std::filesystem::path const& path, struct ::stat const& st, + void add(std::filesystem::path const& path, simplestat const& st, std::function generator); void add_dir(std::filesystem::path const& path); @@ -61,16 +77,15 @@ class os_access_mock : public os_access { void set_access_fail(std::filesystem::path const& path); - std::shared_ptr opendir(const std::string& path) const override; + std::shared_ptr opendir(std::string const& path) const override; - void lstat(const std::string& path, struct ::stat* st) const override; - - std::string readlink(const std::string& path, size_t size) const override; + file_stat symlink_info(std::string const& path) const override; + std::string read_symlink(std::string const& path) const override; std::shared_ptr - map_file(const std::string& path, size_t size) const override; + map_file(std::string const& path, size_t size) const override; - int access(const std::string&, int) const override; + int access(std::string const&, int) const override; private: struct mock_directory; @@ -80,7 +95,7 @@ class os_access_mock : public os_access { struct mock_dirent* find(std::filesystem::path const& path) const; struct mock_dirent* find(std::vector parts) const; void add_internal( - std::filesystem::path const& path, struct ::stat const& st, + std::filesystem::path const& path, simplestat const& st, std::variant, std::unique_ptr> var); @@ -110,19 +125,6 @@ class script_mock : public script { } }; -struct simplestat { - ::ino_t st_ino; - ::mode_t st_mode; - ::nlink_t st_nlink; - ::uid_t st_uid; - ::gid_t st_gid; - ::off_t st_size; - ::dev_t st_rdev; - uint64_t atime; - uint64_t mtime; - uint64_t ctime; -}; - extern std::map statmap; std::optional find_binary(std::string_view name);