diff --git a/CMakeLists.txt b/CMakeLists.txt index dbf302a..859d8e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,7 @@ if(NOT(WITHOUT_TEST)) test-src/integration/test_access.cpp test-src/integration/test_readdir.cpp test-src/integration/test_readlink.cpp + test-src/integration/test_symlink.cpp ) target_include_directories(integration_tests PRIVATE test-src/integration ${GTEST_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS}) diff --git a/src/webfuse/provider.cpp b/src/webfuse/provider.cpp index d22f8e0..721f6c7 100644 --- a/src/webfuse/provider.cpp +++ b/src/webfuse/provider.cpp @@ -64,6 +64,9 @@ public: case request_type::readlink: fs_readlink(reader, writer); break; + case request_type::symlink: + fs_symlink(reader, writer); + break; case request_type::readdir: fs_readdir(reader, writer); break; @@ -97,29 +100,38 @@ private: } } - void fs_readdir(messagereader & reader, messagewriter & writer) + void fs_readlink(messagereader & reader, messagewriter & writer) { auto const path = reader.read_str(); - std::vector entries; + std::string out; - auto const result = fs_.readdir(path, entries, static_cast(-1)); + auto const result = fs_.readlink(path, out); writer.write_i32(result); if (0 == result) { - writer.write_strings(entries); + writer.write_str(out); } } - void fs_readlink(messagereader & reader, messagewriter & writer) + void fs_symlink(messagereader & reader, messagewriter & writer) + { + auto const from = reader.read_str(); + auto const to = reader.read_str(); + + auto const result = fs_.symlink(from, to); + writer.write_i32(result); + } + + void fs_readdir(messagereader & reader, messagewriter & writer) { auto const path = reader.read_str(); - std::string out; + std::vector entries; - auto const result = fs_.readlink(path, out); + auto const result = fs_.readdir(path, entries, static_cast(-1)); writer.write_i32(result); if (0 == result) { - writer.write_str(out); + writer.write_strings(entries); } } diff --git a/test-src/integration/test_readlink.cpp b/test-src/integration/test_readlink.cpp index da7e370..ce0bf6b 100644 --- a/test-src/integration/test_readlink.cpp +++ b/test-src/integration/test_readlink.cpp @@ -2,6 +2,9 @@ #include "webfuse/test/fixture.hpp" #include "webfuse/test/filesystem_mock.hpp" +#include + + using testing::_; using testing::Return; using testing::Invoke; diff --git a/test-src/integration/test_symlink.cpp b/test-src/integration/test_symlink.cpp new file mode 100644 index 0000000..3ed881e --- /dev/null +++ b/test-src/integration/test_symlink.cpp @@ -0,0 +1,74 @@ +#include "webfuse/webfuse.hpp" +#include "webfuse/test/fixture.hpp" +#include "webfuse/test/filesystem_mock.hpp" + +#include + +using testing::_; +using testing::Return; +using testing::Invoke; +using testing::AnyNumber; + +TEST(symlink, sucessfully_create_symlink) +{ + bool link_created = false; + + webfuse::filesystem_mock fs; + EXPECT_CALL(fs, access("/",_)).Times(AnyNumber()).WillRepeatedly(Return(0)); + EXPECT_CALL(fs, getattr(_,_)).WillRepeatedly(Invoke([&link_created](std::string const & path, struct stat * attr){ + memset(reinterpret_cast(attr),0, sizeof(struct stat)); + + if (path == "/") + { + attr->st_nlink = 1; + attr->st_mode = S_IFDIR | 0755; + return 0; + } + else if ((path == "/some_link") && (link_created)) + { + attr->st_nlink = 1; + attr->st_mode = S_IFLNK | 0755; + return 0; + } + else + { + return -ENOENT; + } + })); + EXPECT_CALL(fs, symlink("link-target", "/some_link")).WillOnce(Invoke([&link_created](auto const &, auto const &){ + link_created = true; + return 0; + })); + + webfuse::fixture fixture(fs); + auto const path = fixture.get_path() + "/some_link"; + + ASSERT_EQ(0, ::symlink("link-target", path.c_str())); +} + +TEST(symlink, failed_to_create_symlink) +{ + webfuse::filesystem_mock fs; + EXPECT_CALL(fs, access("/",_)).Times(AnyNumber()).WillRepeatedly(Return(0)); + EXPECT_CALL(fs, getattr(_,_)).WillRepeatedly(Invoke([](std::string const & path, struct stat * attr){ + memset(reinterpret_cast(attr),0, sizeof(struct stat)); + + if (path == "/") + { + attr->st_nlink = 1; + attr->st_mode = S_IFDIR | 0755; + return 0; + } + else + { + return -ENOENT; + } + })); + EXPECT_CALL(fs, symlink("link-target", "/some_link")).WillOnce(Return(-EDQUOT)); + + webfuse::fixture fixture(fs); + auto const path = fixture.get_path() + "/some_link"; + + ASSERT_NE(0, ::symlink("link-target", path.c_str())); + ASSERT_EQ(EDQUOT, errno); +}