diff --git a/CMakeLists.txt b/CMakeLists.txt index 612b79e..be9fe3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,7 @@ if(NOT(WITHOUT_TEST)) test-src/integration/test_chown.cpp test-src/integration/test_truncate.cpp test-src/integration/test_fsync.cpp + test-src/integration/test_utimens.cpp ) target_include_directories(integration_tests PRIVATE test-src/integration ${GTEST_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS}) diff --git a/src/provider_main.cpp b/src/provider_main.cpp index d3b779f..d8fa8eb 100644 --- a/src/provider_main.cpp +++ b/src/provider_main.cpp @@ -90,7 +90,7 @@ public: return -ENOENT; } - int utimens(std::string const &path, struct timespec tv[2], uint64_t handle) override + int utimens(std::string const &path, struct timespec const tv[2], uint64_t handle) override { return -ENOENT; } diff --git a/src/webfuse/filesystem.cpp b/src/webfuse/filesystem.cpp index 517b73f..41bc258 100644 --- a/src/webfuse/filesystem.cpp +++ b/src/webfuse/filesystem.cpp @@ -190,7 +190,7 @@ int filesystem::fsync(std::string const & path, bool is_datasync, uint64_t handl } } -int filesystem::utimens(std::string const &path, struct timespec tv[2], uint64_t handle) +int filesystem::utimens(std::string const &path, struct timespec const tv[2], uint64_t handle) { try { diff --git a/src/webfuse/filesystem.hpp b/src/webfuse/filesystem.hpp index b20aca8..b2f7002 100644 --- a/src/webfuse/filesystem.hpp +++ b/src/webfuse/filesystem.hpp @@ -30,7 +30,7 @@ public: int chown(std::string const & path, uid_t uid, gid_t gid) override; int truncate(std::string const & path, uint64_t size, uint64_t handle) override; int fsync(std::string const & path, bool is_datasync, uint64_t handle) override; - int utimens(std::string const &path, struct timespec tv[2], uint64_t handle) override; + int utimens(std::string const &path, struct timespec const tv[2], uint64_t handle) override; int open(std::string const & path, int flags, uint64_t & handle) override; int mknod(std::string const & path, mode_t mode, dev_t rdev) override; diff --git a/src/webfuse/filesystem/empty_filesystem.cpp b/src/webfuse/filesystem/empty_filesystem.cpp index 71b4fb3..3b37e3a 100644 --- a/src/webfuse/filesystem/empty_filesystem.cpp +++ b/src/webfuse/filesystem/empty_filesystem.cpp @@ -71,7 +71,7 @@ int empty_filesystem::fsync(std::string const & path, bool is_datasync, uint64_t return 0; } -int empty_filesystem::utimens(std::string const &path, struct timespec tv[2], uint64_t handle) +int empty_filesystem::utimens(std::string const &path, struct timespec const tv[2], uint64_t handle) { return -ENOSYS; } diff --git a/src/webfuse/filesystem/empty_filesystem.hpp b/src/webfuse/filesystem/empty_filesystem.hpp index 97ff3ef..2b7400b 100644 --- a/src/webfuse/filesystem/empty_filesystem.hpp +++ b/src/webfuse/filesystem/empty_filesystem.hpp @@ -23,7 +23,7 @@ public: int chown(std::string const & path, uid_t uid, gid_t gid) override; int truncate(std::string const & path, uint64_t size, uint64_t handle) override; int fsync(std::string const & path, bool is_datasync, uint64_t handle) override; - int utimens(std::string const &path, struct timespec tv[2], uint64_t handle) override; + int utimens(std::string const &path, struct timespec const tv[2], uint64_t handle) override; int open(std::string const & path, int flags, uint64_t & handle) override; int mknod(std::string const & path, mode_t mode, dev_t rdev) override; diff --git a/src/webfuse/filesystem/filesystem_i.hpp b/src/webfuse/filesystem/filesystem_i.hpp index 2e3f3c7..fe94ed8 100644 --- a/src/webfuse/filesystem/filesystem_i.hpp +++ b/src/webfuse/filesystem/filesystem_i.hpp @@ -30,7 +30,7 @@ public: virtual int chown(std::string const & path, uid_t uid, gid_t gid) = 0; virtual int truncate(std::string const & path, uint64_t size, uint64_t handle) = 0; virtual int fsync(std::string const & path, bool is_datasync, uint64_t handle) = 0; - virtual int utimens(std::string const &path, struct timespec tv[2], uint64_t handle) = 0; + virtual int utimens(std::string const &path, struct timespec const tv[2], uint64_t handle) = 0; virtual int open(std::string const & path, int flags, uint64_t & handle) = 0; virtual int mknod(std::string const & path, mode_t mode, dev_t rdev) = 0; diff --git a/src/webfuse/fuse.cpp b/src/webfuse/fuse.cpp index 6d856a0..8600d02 100644 --- a/src/webfuse/fuse.cpp +++ b/src/webfuse/fuse.cpp @@ -210,6 +210,13 @@ static int fs_statfs(char const * path, struct statvfs * buffer) return fs->statfs(path, buffer); } +static int fs_utimes(const char * path, const struct timespec tv[2], struct fuse_file_info * info) +{ + auto * const fs = fs_get_filesystem(); + auto handle = fs_get_handle(info); + return fs->utimens(path, tv, handle); +} + } namespace webfuse @@ -277,6 +284,7 @@ int fuse::run(int argc, char * argv[]) operations.readdir = fs_readdir; operations.rmdir = fs_rmdir; operations.statfs = fs_statfs; + operations.utimens = fs_utimes; return fuse_main(argc, argv, &operations, context); } diff --git a/src/webfuse/provider.cpp b/src/webfuse/provider.cpp index 512baac..ae92eb7 100644 --- a/src/webfuse/provider.cpp +++ b/src/webfuse/provider.cpp @@ -85,6 +85,9 @@ public: case request_type::fsync: fs_fsync(reader, writer); break; + case request_type::utimens: + fs_utimens(reader, writer); + break; case request_type::create: fs_create(reader, writer); break; @@ -204,6 +207,18 @@ private: writer.write_i32(result); } + void fs_utimens(messagereader & reader, messagewriter & writer) + { + auto const path = reader.read_str(); + struct timespec times[2]; + reader.read_time(times[0]); + reader.read_time(times[1]); + auto const handle = reader.read_u64(); + + auto const result = fs_.utimens(path, times, handle); + writer.write_i32(result); + } + void fs_create(messagereader & reader, messagewriter & writer) { auto const path = reader.read_str(); diff --git a/src/webfuse/ws/messagereader.cpp b/src/webfuse/ws/messagereader.cpp index 6b28a67..1c22a85 100644 --- a/src/webfuse/ws/messagereader.cpp +++ b/src/webfuse/ws/messagereader.cpp @@ -180,5 +180,11 @@ void messagereader::read_strings(std::vector &entries) } } +void messagereader::read_time(struct timespec &time) +{ + time.tv_sec = static_cast(read_u64()); + time.tv_nsec = static_cast(read_u32()); +} + } \ No newline at end of file diff --git a/src/webfuse/ws/messagereader.hpp b/src/webfuse/ws/messagereader.hpp index f7c490f..22ad119 100644 --- a/src/webfuse/ws/messagereader.hpp +++ b/src/webfuse/ws/messagereader.hpp @@ -40,7 +40,7 @@ public: std::string read_bytes(); void read_strings(std::vector &entries); - + void read_time(struct timespec &time); private: diff --git a/test-src/integration/test_utimens.cpp b/test-src/integration/test_utimens.cpp new file mode 100644 index 0000000..24d04c3 --- /dev/null +++ b/test-src/integration/test_utimens.cpp @@ -0,0 +1,46 @@ +#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; + +TEST(utimens, success) +{ + 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; + } + if (path == "/some_file") + { + attr->st_nlink = 1; + attr->st_mode = S_IFREG | 0644; + return 0; + } + else + { + std::cout << "getattr: failed" << std::endl; + return -ENOENT; + } + })); + EXPECT_CALL(fs, utimens("/some_file", _, _)).WillOnce(Return(0)); + + webfuse::fixture fixture(fs); + auto const path = fixture.get_path() + "/some_file"; + + timeval times[2] = {42,42}; + ASSERT_EQ(0, ::utimes(path.c_str(), times)); +} diff --git a/test-src/integration/webfuse/test/filesystem_mock.hpp b/test-src/integration/webfuse/test/filesystem_mock.hpp index 56a6f55..844160f 100644 --- a/test-src/integration/webfuse/test/filesystem_mock.hpp +++ b/test-src/integration/webfuse/test/filesystem_mock.hpp @@ -24,7 +24,7 @@ public: MOCK_METHOD(int, chown, (std::string const & path, uid_t uid, gid_t gid)); MOCK_METHOD(int, truncate, (std::string const & path, uint64_t size, uint64_t handle)); MOCK_METHOD(int, fsync, (std::string const & path, bool is_datasync, uint64_t handle)); - MOCK_METHOD(int, utimens, (std::string const &path, struct timespec tv[2], uint64_t handle)); + MOCK_METHOD(int, utimens, (std::string const &path, struct timespec const tv[2], uint64_t handle)); MOCK_METHOD(int, open, (std::string const & path, int flags, uint64_t & handle)); MOCK_METHOD(int, mknod, (std::string const & path, mode_t mode, dev_t rdev));