diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fec69b..ae5f8e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,7 @@ if(NOT(WITHOUT_TEST)) test-src/integration/test_open.cpp test-src/integration/test_mknod.cpp test-src/integration/test_unlink.cpp + test-src/integration/test_read.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 3a3fa0e..6abe535 100644 --- a/src/webfuse/provider.cpp +++ b/src/webfuse/provider.cpp @@ -103,6 +103,9 @@ public: case request_type::unlink: fs_unlink(reader, writer); break; + case request_type::read: + fs_read(reader, writer); + break; case request_type::readdir: fs_readdir(reader, writer); break; @@ -283,6 +286,23 @@ private: writer.write_i32(result); } + void fs_read(messagereader & reader, messagewriter & writer) + { + auto const path = reader.read_str(); + auto const size = reader.read_u32(); + auto const offset = reader.read_u64(); + auto const handle = reader.read_u64(); + + std::vector buffer(size); + + auto const result = fs_.read(path, buffer.data(), size, offset, handle); + writer.write_i32(result); + if (0 < result) + { + writer.write_data(buffer.data(), result); + } + } + void fs_readdir(messagereader & reader, messagewriter & writer) { auto const path = reader.read_str(); diff --git a/test-src/integration/test_read.cpp b/test-src/integration/test_read.cpp new file mode 100644 index 0000000..eee59da --- /dev/null +++ b/test-src/integration/test_read.cpp @@ -0,0 +1,70 @@ +#include "webfuse/webfuse.hpp" +#include "webfuse/test/fixture.hpp" +#include "webfuse/test/filesystem_mock.hpp" + +#include +#include +#include + + +using testing::_; +using testing::Return; +using testing::Invoke; +using testing::AnyNumber; +using testing::AtMost; + +TEST(read, success) +{ + std::string expected = "Hello, World!"; + + webfuse::filesystem_mock fs; + EXPECT_CALL(fs, access("/",_)).Times(AnyNumber()).WillRepeatedly(Return(0)); + EXPECT_CALL(fs, getattr(_,_)).WillRepeatedly(Invoke([&expected](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; + } + if (path == "/some_file") + { + attr->st_nlink = 1; + attr->st_mode = S_IFREG | 0644; + attr->st_size = expected.size(); + return 0; + } + else + { + return -ENOENT; + } + })); + EXPECT_CALL(fs, open("/some_file", _, _)).WillOnce(Return(0)); + EXPECT_CALL(fs, read("/some_file", _, _, _, _)).Times(AnyNumber()).WillRepeatedly(Invoke([&expected](auto const &, char * buffer, size_t buffer_size, uint64_t offset, auto){ + if (offset < expected.size()) + { + auto const count = std::min(buffer_size, expected.size() - offset); + memcpy(reinterpret_cast(buffer), reinterpret_cast(&expected[offset]), count); + return (int) count; + } + else + { + return 0; + } + })); + EXPECT_CALL(fs, release("/some_file", _)).Times(AtMost(1)).WillOnce(Return(0)); + + webfuse::fixture fixture(fs); + auto const path = fixture.get_path() + "/some_file"; + + std::string contents; + { + std::ifstream in(path); + std::stringstream buffer; + buffer << in.rdbuf(); + contents = buffer.str(); + } + + ASSERT_EQ(expected, contents); +}