Skip to content

Commit

Permalink
Merge pull request #11 from lesomnus/feature/mem-fs
Browse files Browse the repository at this point in the history
implement mem fs
  • Loading branch information
lesomnus committed Jul 8, 2023
2 parents b64eb43 + 3a8d414 commit 08deb49
Show file tree
Hide file tree
Showing 12 changed files with 292 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM ghcr.io/lesomnus/dev-gcc:13
FROM ghcr.io/lesomnus/dev-gcc:12



Expand Down
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/Debug/tests/mount-test",
"program": "${workspaceFolder}/build/Debug/tests/mem_file",
// "program": "${workspaceFolder}/build/Debug/tests/os_fs-test",
"args": [],
"stopAtEntry": false,
Expand Down
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ add_library(
internal/vfs/impl/file.hpp
internal/vfs/impl/fs_proxy.hpp
internal/vfs/impl/fs.hpp
internal/vfs/impl/mem_file.hpp
internal/vfs/impl/os_file.hpp
internal/vfs/impl/os_fs.hpp
internal/vfs/impl/utils.hpp
Expand All @@ -37,6 +38,8 @@ add_library(
src/entry.cpp
src/file.cpp
src/fs.cpp
src/mem_file.cpp
src/mem_fs.cpp
src/mount.cpp
src/os_file.cpp
src/os_fs.cpp
Expand Down
8 changes: 8 additions & 0 deletions include/vfs/fs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1134,4 +1134,12 @@ std::shared_ptr<Fs> make_os_fs();
*/
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.
* @return New empty `Fs` that is virtual.
*/
std::shared_ptr<Fs> make_mem_fs(std::filesystem::path const& temp_dir = "/tmp");

} // namespace vfs
67 changes: 67 additions & 0 deletions internal/vfs/impl/mem_file.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#pragma once

#include <cstdint>
#include <filesystem>
#include <iosfwd>
#include <memory>
#include <string>

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

namespace vfs {
namespace impl {

class MemRegularFile
: public VFile
, public RegularFile
, public std::enable_shared_from_this<MemRegularFile> {
public:
MemRegularFile(
std::intmax_t owner,
std::intmax_t group,
std::filesystem::perms perms = DefaultPerms);

MemRegularFile(MemRegularFile const& other);
MemRegularFile(MemRegularFile&& other) = default;

[[nodiscard]] std::filesystem::file_time_type last_write_time() const override {
return this->last_write_time_;
}

void last_write_time(std::filesystem::file_time_type new_time) override {
this->last_write_time_ = new_time;
}

[[nodiscard]] std::uintmax_t size() const override;

void resize(std::uintmax_t new_size) override;

[[nodiscard]] std::shared_ptr<std::istream> open_read(std::ios_base::openmode mode) const override;

std::shared_ptr<std::ostream> open_write(std::ios_base::openmode mode) override;

MemRegularFile& operator=(MemRegularFile const& other);
MemRegularFile& operator=(MemRegularFile&& other) = default;

private:
std::shared_ptr<std::string> data_;
std::filesystem::file_time_type last_write_time_ = std::filesystem::file_time_type::clock::now();
};

class MemDirectory
: public VDirectory {
public:
MemDirectory(std::intmax_t owner, std::intmax_t group, std::filesystem::perms perms = DefaultPerms)
: VDirectory(owner, group, perms) { }

MemDirectory(MemDirectory const& other) = default;
MemDirectory(MemDirectory&& other) = default;

std::pair<std::shared_ptr<RegularFile>, bool> emplace_regular_file(std::string const& name) override;

std::pair<std::shared_ptr<Directory>, bool> emplace_directory(std::string const& name) override;
};

} // namespace impl
} // namespace vfs
5 changes: 4 additions & 1 deletion internal/vfs/impl/vfile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ class VFile: virtual public File {
this->last_write_time_ = new_time;
}

VFile& operator=(VFile const& other) = default;
VFile& operator=(VFile&& other) = default;

protected:
std::intmax_t owner_;
std::intmax_t group_;
Expand Down Expand Up @@ -197,7 +200,7 @@ class VDirectory

[[nodiscard]] std::shared_ptr<Cursor> cursor() const override;

private:
protected:
std::unordered_map<std::string, std::shared_ptr<File>> files_;
};

Expand Down
115 changes: 115 additions & 0 deletions src/mem_file.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#include "vfs/impl/mem_file.hpp"

#include <cassert>
#include <cstdint>
#include <filesystem>
#include <iostream>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <utility>

namespace fs = std::filesystem;

namespace vfs {
namespace impl {

MemRegularFile::MemRegularFile(
std::intmax_t owner,
std::intmax_t group,
fs::perms perms)
: VFile(owner, group, perms)
, data_(std::make_shared<std::string>()) { }

MemRegularFile::MemRegularFile(MemRegularFile const& other)
: VFile(other)
, data_(std::make_shared<std::string>(*other.data_)) { }

std::uintmax_t MemRegularFile::size() const {
assert(nullptr != this->data_);
return this->data_->size();
}

void MemRegularFile::resize(std::uintmax_t new_size) {
assert(nullptr != this->data_);
return this->data_->resize(new_size);
}

std::shared_ptr<std::istream> MemRegularFile::open_read(std::ios_base::openmode mode) const {
return std::make_shared<std::istringstream>(*this->data_, mode);
}

std::shared_ptr<std::ostream> MemRegularFile::open_write(std::ios_base::openmode mode) {
using ios = std::ios_base;

mode &= ios::out | ios::trunc | ios::app;
switch(int(mode)) {
case int(ios::out):
case int(ios::trunc):
case int(ios::app):
case int(ios::out | ios::trunc):
case int(ios::out | ios::app): {
break;
}

default: {
auto s = std::make_shared<std::stringstream>();
s->setstate(ios::failbit);
return s;
}
}

return std::shared_ptr<std::ostringstream>(new std::ostringstream(), [mode, self = std::weak_ptr(this->shared_from_this())](std::ostringstream* p) {
auto data = std::move(*p).str();
delete p;

auto f = self.lock();
if(!f) {
return;
}

switch(int(mode)) {
case int(ios::out):
case int(ios::trunc):
case int(ios::out | ios::trunc): {
f->data_ = std::make_shared<std::string>(std::move(data));
break;
}

case int(ios::app):
case int(ios::out | ios::app): {
f->data_->append(std::move(data));
break;
}

default: {
throw std::logic_error("unreachable");
}
}

f->last_write_time_ = fs::file_time_type::clock::now();
});
}

MemRegularFile& MemRegularFile::operator=(MemRegularFile const& other) {
assert(nullptr != this->data_);
assert(nullptr != other.data_);

*this->data_ = *other.data_;
this->last_write_time_ = fs::file_time_type::clock::now();
return *this;
}

std::pair<std::shared_ptr<RegularFile>, bool> MemDirectory::emplace_regular_file(std::string const& name) {
auto [it, ok] = this->files_.emplace(name, std::make_shared<MemRegularFile>(0, 0));
return std::make_pair(std::dynamic_pointer_cast<RegularFile>(it->second), ok);
}

std::pair<std::shared_ptr<Directory>, bool> MemDirectory::emplace_directory(std::string const& name) {
auto [it, ok] = this->files_.emplace(name, std::make_shared<MemDirectory>(0, 0));
return std::make_pair(std::dynamic_pointer_cast<Directory>(it->second), ok);
}

} // namespace impl
} // namespace vfs
17 changes: 17 additions & 0 deletions src/mem_fs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include <filesystem>
#include <memory>
#include <utility>

#include "vfs/impl/mem_file.hpp"
#include "vfs/impl/vfs.hpp"

namespace fs = std::filesystem;

namespace vfs {

std::shared_ptr<Fs> make_mem_fs(fs::path const& temp_dir) {
auto d = std::make_shared<impl::DirectoryEntry>("/", nullptr, std::make_shared<impl::MemDirectory>(0, 0));
return std::make_shared<impl::Vfs>(std::move(d), temp_dir);
}

} // namespace vfs
2 changes: 2 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ endmacro()
vfs_SIMPLE_TEST(copy)
vfs_SIMPLE_TEST(directory_entry)
vfs_SIMPLE_TEST(entry)
vfs_SIMPLE_TEST(mem_file)
vfs_SIMPLE_TEST(mem_fs)
vfs_SIMPLE_TEST(mount)
vfs_SIMPLE_TEST(os_file)
vfs_SIMPLE_TEST(os_fs)
Expand Down
29 changes: 29 additions & 0 deletions tests/include/testing/suites/file.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,35 @@ class TestFile {
f->last_write_time(t0);
CHECK(t0 == f->last_write_time());
}

SECTION("::open_write") {
using ios = std::ios_base;

auto const [f, ok] = sandbox->emplace_regular_file("foo");
REQUIRE(ok);

*f->open_write() << testing::QuoteA;
REQUIRE(testing::QuoteA == testing::read_all(*f->open_read()));

SECTION("trunc") {
*f->open_write(ios::trunc) << testing::QuoteB;
CHECK(testing::QuoteB == testing::read_all(*f->open_read()));
}

SECTION("app") {
*f->open_write(ios::app) << testing::QuoteB;
CHECK((std::string(testing::QuoteA) + std::string(testing::QuoteB)) == testing::read_all(*f->open_read()));
}

SECTION("fails if") {
std::shared_ptr<std::ostream> os;
SECTION("trunc|app") {
os = f->open_write(ios::trunc | ios::app);
}

CHECK(os->fail());
}
}
}

SECTION("Directory") {
Expand Down
16 changes: 16 additions & 0 deletions tests/src/mem_file.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include <memory>

#include <catch2/catch_template_test_macros.hpp>

#include <vfs/impl/mem_file.hpp>

#include "testing/suites/file.hpp"

class TestMemFile: public testing::suites::TestFileFixture {
public:
std::shared_ptr<vfs::impl::Directory> make() {
return std::make_shared<vfs::impl::MemDirectory>(0, 0);
}
};

METHOD_AS_TEST_CASE(testing::suites::TestFile<TestMemFile>::test, "MemFile");
29 changes: 29 additions & 0 deletions tests/src/mem_fs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include <memory>

#include <catch2/catch_template_test_macros.hpp>

#include <vfs/fs.hpp>

#include "testing/suites/fs.hpp"

class TestMemFs: public testing::suites::TestFsFixture {
public:
std::shared_ptr<vfs::Fs> make() override {
return vfs::make_mem_fs();
}
};

METHOD_AS_TEST_CASE(testing::suites::TestFsBasic<TestMemFs>::test, "MemFs");

class TestChRootedVfs: public testing::suites::TestFsFixture {
public:
std::shared_ptr<vfs::Fs> make() override {
auto fs = vfs::make_mem_fs();
fs->create_directories("root_/tmp");
REQUIRE(fs->is_directory("root_/tmp"));

return fs->change_root("root_");
}
};

METHOD_AS_TEST_CASE(testing::suites::TestFsBasic<TestChRootedVfs>::test, "MemFs with chroot");

0 comments on commit 08deb49

Please sign in to comment.