Skip to content

Commit

Permalink
Merge pull request #14 from lesomnus/feature/readonly-fs
Browse files Browse the repository at this point in the history
Feature/readonly fs
  • Loading branch information
lesomnus committed Jul 29, 2023
2 parents 70351b5 + 3c32f29 commit 6d0ab6a
Show file tree
Hide file tree
Showing 12 changed files with 327 additions and 333 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ int main(int argc, char* argv[]) {
- `vfs::make_vfs` Basic file system.
- `vfs::make_mem_fs` Files are stored on the memory.
- `vfs::make_union_fs` Provides a single coherent file system over multiple file systems.
- `vfs::make_read_only_fs` Makes the given file system read-only.
### Utilities
- `vfs::Fs::change_root` Changes the root directory.
Expand Down
8 changes: 4 additions & 4 deletions include/vfs/directory_entry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ class directory_entry {
directory_entry(directory_entry&&) noexcept = default;

directory_entry(Fs const& fs) noexcept
: fs_(fs.current_path(fs.current_path())) { }
: fs_(fs.shared_from_this()) { }

explicit directory_entry(Fs const& fs, std::filesystem::path p)
: fs_(fs.current_path(fs.current_path()))
: fs_(fs.shared_from_this())
, path_(std::move(p)) {
this->refresh();
}

directory_entry(Fs const& fs, std::filesystem::path p, std::error_code& ec)
: fs_(fs.current_path(fs.current_path()))
: fs_(fs.shared_from_this())
, path_(std::move(p)) {
this->refresh(ec);
}
Expand Down Expand Up @@ -415,7 +415,7 @@ class directory_entry {
return this->status().type();
}

std::shared_ptr<Fs> fs_;
std::shared_ptr<Fs const> fs_;
std::filesystem::path path_;
std::filesystem::file_type type_ = std::filesystem::file_type::none;
};
Expand Down
35 changes: 30 additions & 5 deletions include/vfs/fs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class directory_entry;
class directory_iterator;
class recursive_directory_iterator;

class Fs {
class Fs: public std::enable_shared_from_this<Fs> {
public:
virtual ~Fs() = default;

Expand Down Expand Up @@ -481,7 +481,7 @@ class Fs {
* @param[in] p Path to change the current working directory to.
* @return Same file system where the working directory is \p p.
*/
[[nodiscard]] virtual std::shared_ptr<Fs> current_path(std::filesystem::path const& p) const = 0;
[[nodiscard]] virtual std::shared_ptr<Fs const> current_path(std::filesystem::path const& p) const = 0;

/**
* @brief Creates a new Fs that share the same file system but have different working directory.
Expand All @@ -490,7 +490,24 @@ class Fs {
* @param[out] ec Error code to store error status to.
* @return Same file system where the working directory is \p p.
*/
[[nodiscard]] virtual std::shared_ptr<Fs> current_path(std::filesystem::path const& p, std::error_code& ec) const noexcept = 0;
[[nodiscard]] virtual std::shared_ptr<Fs const> current_path(std::filesystem::path const& p, std::error_code& ec) const noexcept = 0;

/**
* @brief Creates a new Fs that share the same file system but have different working directory.
*
* @param[in] p Path to change the current working directory to.
* @return Same file system where the working directory is \p p.
*/
[[nodiscard]] virtual std::shared_ptr<Fs> current_path(std::filesystem::path const& p) = 0;

/**
* @brief Creates a new Fs that share the same file system but have different working directory.
*
* @param[in] p Path to change the current working directory to.
* @param[out] ec Error code to store error status to.
* @return Same file system where the working directory is \p p.
*/
[[nodiscard]] virtual std::shared_ptr<Fs> current_path(std::filesystem::path const& p, std::error_code& ec) noexcept = 0;

/**
* @brief Checks whether the path refers to an existing file system object
Expand Down Expand Up @@ -1139,19 +1156,27 @@ std::shared_ptr<Fs> make_os_fs();
/**
* @brief Makes empty `Fs` that is virtual. Regular files are written to the temporary directory of the OS and are deleted when the `Fs` is destructed.
*
* @param temp_dir Path to the temporary directory in the created Fs.
* @param temp_dir Path to the temporary directory in the created `Fs`.
* @return New empty `Fs` that is virtual.
*/
std::shared_ptr<Fs> make_vfs(std::filesystem::path const& temp_dir = "/tmp");

/**
* @brief Makes empty `Fs` that is virtual. Regular files are stored on the memory.
*
* @param temp_dir Path to the temporary directory in the created Fs.
* @param temp_dir Path to the temporary directory in the created `Fs`.
* @return New empty `Fs` that is virtual.
*/
std::shared_ptr<Fs> make_mem_fs(std::filesystem::path const& temp_dir = "/tmp");

std::shared_ptr<Fs> make_union_fs(Fs& upper, Fs const& lower);

/**
* @brief Makes `Fs` read-only. Non-const methods of resulting `Fs` throw `std::filesystem::filesystem_error` with `std::errc::read_only_file_system`.
*
* @param fs `Fs` to make read-only.
* @return `Fs` that is the same as the given `Fs` but read-only.
*/
std::shared_ptr<Fs> make_read_only_fs(Fs const& fs);

} // namespace vfs
171 changes: 151 additions & 20 deletions internal/vfs/impl/fs.hpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,161 @@
#pragma once

#include <concepts>
#include <filesystem>
#include <memory>
#include <stdexcept>
#include <type_traits>

#include "vfs/impl/file.hpp"
#include "vfs/impl/utils.hpp"

#include "vfs/fs.hpp"

#define VFS_FS_METHOD_NAMES \
Fs::canonical, \
Fs::weakly_canonical, \
Fs::copy, \
Fs::copy_file, \
Fs::create_directory, \
Fs::create_directories, \
Fs::create_hard_link, \
Fs::create_symlink, \
Fs::current_path, \
Fs::equivalent, \
Fs::file_size, \
Fs::hard_link_count, \
Fs::last_write_time, \
Fs::permissions, \
Fs::read_symlink, \
Fs::remove, \
Fs::remove_all, \
Fs::rename, \
Fs::resize_file, \
Fs::space, \
Fs::status, \
Fs::symlink_status, \
Fs::temp_directory_path, \
Fs::is_empty

namespace vfs {
namespace impl {

class FsBase: public Fs {
public:
using VFS_FS_METHOD_NAMES;

std::filesystem::path canonical(std::filesystem::path const& p, std::error_code& ec) const override {
return handle_error([&] { return this->canonical(p); }, ec);
}

std::filesystem::path weakly_canonical(std::filesystem::path const& p, std::error_code& ec) const override {
return handle_error([&] { return this->weakly_canonical(p); }, ec);
}

void copy(std::filesystem::path const& src, std::filesystem::path const& dst, std::filesystem::copy_options opts, std::error_code& ec) override {
handle_error([&] { this->copy(src, dst, opts); return 0; }, ec);
}

bool copy_file(std::filesystem::path const& src, std::filesystem::path const& dst, std::filesystem::copy_options opts, std::error_code& ec) override {
return handle_error([&] { return this->copy_file(src, dst, opts); }, ec);
}

bool create_directory(std::filesystem::path const& p, std::error_code& ec) noexcept override {
return handle_error([&] { return this->create_directory(p); }, ec);
}

bool create_directory(std::filesystem::path const& p, std::filesystem::path const& attr, std::error_code& ec) noexcept override {
return handle_error([&] { return this->create_directory(p, attr); }, ec);
}

bool create_directories(std::filesystem::path const& p, std::error_code& ec) override {
return handle_error([&] { return this->create_directories(p); }, ec);
}

void create_hard_link(std::filesystem::path const& target, std::filesystem::path const& link, std::error_code& ec) noexcept override {
handle_error([&] { this->create_hard_link(target, link); return 0; }, ec);
}

void create_symlink(std::filesystem::path const& target, std::filesystem::path const& link, std::error_code& ec) noexcept override {
handle_error([&] { this->create_symlink(target, link); return 0; }, ec);
}

[[nodiscard]] std::filesystem::path current_path(std::error_code& ec) const override {
return handle_error([&] { return this->current_path(); }, ec);
}

[[nodiscard]] std::shared_ptr<Fs const> current_path(std::filesystem::path const& p, std::error_code& ec) const noexcept override {
return handle_error([&] { return this->current_path(p); }, ec);
}

[[nodiscard]] std::shared_ptr<Fs> current_path(std::filesystem::path const& p, std::error_code& ec) noexcept override {
return handle_error([&] { return this->current_path(p); }, ec);
}

[[nodiscard]] bool equivalent(std::filesystem::path const& p1, std::filesystem::path const& p2, std::error_code& ec) const noexcept override {
return handle_error([&] { return this->equivalent(p1, p2); }, ec);
}

[[nodiscard]] std::uintmax_t file_size(std::filesystem::path const& p, std::error_code& ec) const noexcept override {
return handle_error([&] { return this->file_size(p); }, ec, static_cast<std::uintmax_t>(-1));
}

[[nodiscard]] std::uintmax_t hard_link_count(std::filesystem::path const& p, std::error_code& ec) const noexcept override {
return handle_error([&] { return this->hard_link_count(p); }, ec, static_cast<std::uintmax_t>(-1));
}

[[nodiscard]] std::filesystem::file_time_type last_write_time(std::filesystem::path const& p, std::error_code& ec) const noexcept override {
return handle_error([&] { return this->last_write_time(p); }, ec);
}

void last_write_time(std::filesystem::path const& p, std::filesystem::file_time_type t, std::error_code& ec) noexcept override {
handle_error([&] { this->last_write_time(p, t); return 0; }, ec);
}

void permissions(std::filesystem::path const& p, std::filesystem::perms prms, std::filesystem::perm_options opts, std::error_code& ec) override {
handle_error([&] { this->permissions(p, prms, opts); return 0; }, ec);
}

[[nodiscard]] std::filesystem::path read_symlink(std::filesystem::path const& p, std::error_code& ec) const override {
return handle_error([&] { return this->read_symlink(p); }, ec);
}

bool remove(std::filesystem::path const& p, std::error_code& ec) noexcept override {
return handle_error([&] { return this->remove(p); }, ec);
}

std::uintmax_t remove_all(std::filesystem::path const& p, std::error_code& ec) override {
return handle_error([&] { return this->remove_all(p); }, ec, static_cast<std::uintmax_t>(-1));
}

void rename(std::filesystem::path const& src, std::filesystem::path const& dst, std::error_code& ec) noexcept override {
handle_error([&] { this->rename(src, dst); return 0; }, ec);
}

void resize_file(std::filesystem::path const& p, std::uintmax_t n, std::error_code& ec) noexcept override {
handle_error([&] { this->resize_file(p, n); return 0; }, ec);
}

[[nodiscard]] std::filesystem::space_info space(std::filesystem::path const& p, std::error_code& ec) const noexcept override {
return handle_error([&] { return this->space(p); }, ec);
}

[[nodiscard]] std::filesystem::file_status status(std::filesystem::path const& p, std::error_code& ec) const noexcept override {
return handle_error([&] { return this->status(p); }, ec);
}

[[nodiscard]] std::filesystem::file_status symlink_status(std::filesystem::path const& p, std::error_code& ec) const noexcept override {
return handle_error([&] { return this->symlink_status(p); }, ec);
}

[[nodiscard]] std::filesystem::path temp_directory_path(std::error_code& ec) const override {
return handle_error([&] { return this->temp_directory_path(); }, ec);
}

[[nodiscard]] bool is_empty(std::filesystem::path const& p, std::error_code& ec) const override {
return handle_error([&] { return this->is_empty(p); }, ec);
}

[[nodiscard]] virtual std::shared_ptr<File const> file_at(std::filesystem::path const& p) const = 0;

virtual std::shared_ptr<File> file_at(std::filesystem::path const& p) = 0;
Expand All @@ -34,32 +177,20 @@ std::logic_error err_unknown_fs_() {

} // namespace

inline FsBase& fs_base(Fs& fs) {
auto* b = dynamic_cast<FsBase*>(&fs);
template<typename T>
requires std::same_as<std::remove_const_t<T>, Fs>
inline auto& fs_base(T& fs) {
auto* b = dynamic_cast<std::conditional_t<std::is_const_v<T>, FsBase const, FsBase>*>(&fs);
if(b == nullptr) {
throw err_unknown_fs_();
}
return *b;
}

inline FsBase const& fs_base(Fs const& fs) {
auto const* b = dynamic_cast<FsBase const*>(&fs);
if(b == nullptr) {
throw err_unknown_fs_();
}
return *b;
}

inline std::shared_ptr<FsBase> fs_base(std::shared_ptr<Fs>&& fs) {
auto b = std::dynamic_pointer_cast<FsBase>(std::move(fs));
if(!b) {
throw err_unknown_fs_();
}
return b;
}

inline std::shared_ptr<FsBase> fs_base(std::shared_ptr<Fs> const& fs) {
auto b = std::dynamic_pointer_cast<FsBase>(fs);
template<typename T>
requires std::same_as<std::remove_const_t<T>, Fs>
inline auto fs_base(std::shared_ptr<T> fs) {
auto b = std::dynamic_pointer_cast<std::conditional_t<std::is_const_v<T>, FsBase const, FsBase>>(std::move(fs));
if(!b) {
throw err_unknown_fs_();
}
Expand Down
Loading

0 comments on commit 6d0ab6a

Please sign in to comment.