From 9f300ec2d4e8f6f135968ac7a9b31593b5525ab4 Mon Sep 17 00:00:00 2001 From: Falk Werner Date: Sun, 13 Nov 2022 14:17:47 +0100 Subject: [PATCH] added filesystem interface --- CMakeLists.txt | 20 ++- src/webfuse/filesystem/accessmode.cpp | 41 ++++++ src/webfuse/filesystem/accessmode.hpp | 28 ++++ src/webfuse/filesystem/fileattributes.cpp | 46 +++++++ src/webfuse/filesystem/fileattributes.hpp | 37 ++++++ src/webfuse/filesystem/filehandle.hpp | 16 +++ src/webfuse/filesystem/filemode.cpp | 50 ++++++++ src/webfuse/filesystem/filemode.hpp | 75 +++++++++++ src/webfuse/filesystem/filesystem_i.hpp | 56 ++++++++ .../filesystem/filesystem_statistics.hpp | 31 +++++ src/webfuse/filesystem/filetime.cpp | 43 +++++++ src/webfuse/filesystem/filetime.hpp | 25 ++++ src/webfuse/filesystem/groupid.cpp | 27 ++++ src/webfuse/filesystem/groupid.hpp | 25 ++++ src/webfuse/filesystem/openflags.cpp | 69 ++++++++++ src/webfuse/filesystem/openflags.hpp | 43 +++++++ src/webfuse/filesystem/status.cpp | 120 ++++++++++++++++++ src/webfuse/filesystem/status.hpp | 62 +++++++++ src/webfuse/filesystem/userid.cpp | 28 ++++ src/webfuse/filesystem/userid.hpp | 25 ++++ src/webfuse/fuse.hpp | 26 ++++ .../webfuse/filesystem/test_accessmode.cpp | 26 ++++ .../filesystem/test_fileattributes.cpp | 101 +++++++++++++++ test-src/webfuse/filesystem/test_filemode.cpp | 25 ++++ test-src/webfuse/filesystem/test_groupid.cpp | 27 ++++ .../webfuse/filesystem/test_openflags.cpp | 24 ++++ test-src/webfuse/filesystem/test_status.cpp | 28 ++++ test-src/webfuse/filesystem/test_userid.cpp | 27 ++++ {test => test-src}/webfuse/test_app.cpp | 0 29 files changed, 1149 insertions(+), 2 deletions(-) create mode 100644 src/webfuse/filesystem/accessmode.cpp create mode 100644 src/webfuse/filesystem/accessmode.hpp create mode 100644 src/webfuse/filesystem/fileattributes.cpp create mode 100644 src/webfuse/filesystem/fileattributes.hpp create mode 100644 src/webfuse/filesystem/filehandle.hpp create mode 100644 src/webfuse/filesystem/filemode.cpp create mode 100644 src/webfuse/filesystem/filemode.hpp create mode 100644 src/webfuse/filesystem/filesystem_i.hpp create mode 100644 src/webfuse/filesystem/filesystem_statistics.hpp create mode 100644 src/webfuse/filesystem/filetime.cpp create mode 100644 src/webfuse/filesystem/filetime.hpp create mode 100644 src/webfuse/filesystem/groupid.cpp create mode 100644 src/webfuse/filesystem/groupid.hpp create mode 100644 src/webfuse/filesystem/openflags.cpp create mode 100644 src/webfuse/filesystem/openflags.hpp create mode 100644 src/webfuse/filesystem/status.cpp create mode 100644 src/webfuse/filesystem/status.hpp create mode 100644 src/webfuse/filesystem/userid.cpp create mode 100644 src/webfuse/filesystem/userid.hpp create mode 100644 src/webfuse/fuse.hpp create mode 100644 test-src/webfuse/filesystem/test_accessmode.cpp create mode 100644 test-src/webfuse/filesystem/test_fileattributes.cpp create mode 100644 test-src/webfuse/filesystem/test_filemode.cpp create mode 100644 test-src/webfuse/filesystem/test_groupid.cpp create mode 100644 test-src/webfuse/filesystem/test_openflags.cpp create mode 100644 test-src/webfuse/filesystem/test_status.cpp create mode 100644 test-src/webfuse/filesystem/test_userid.cpp rename {test => test-src}/webfuse/test_app.cpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d6503e..8c5bf3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,16 @@ pkg_check_modules(FUSE REQUIRED IMPORTED_TARGET fuse3) pkg_check_modules(LWS REQUIRED IMPORTED_TARGET libwebsockets) add_library(webfuse_static STATIC - src/webfuse/webfuse.cpp) + src/webfuse/webfuse.cpp + src/webfuse/filesystem/status.cpp + src/webfuse/filesystem/accessmode.cpp + src/webfuse/filesystem/openflags.cpp + src/webfuse/filesystem/userid.cpp + src/webfuse/filesystem/groupid.cpp + src/webfuse/filesystem/filemode.cpp + src/webfuse/filesystem/filetime.cpp + src/webfuse/filesystem/fileattributes.cpp +) target_include_directories(webfuse_static PUBLIC src) target_link_libraries(webfuse_static PUBLIC PkgConfig::FUSE PkgConfig::LWS) @@ -22,7 +31,14 @@ if(NOT(WITHOUT_TEST)) pkg_check_modules(GMOCK REQUIRED gmock) add_executable(alltests - test/webfuse/test_app.cpp + test-src/webfuse/test_app.cpp + test-src/webfuse/filesystem/test_status.cpp + test-src/webfuse/filesystem/test_accessmode.cpp + test-src/webfuse/filesystem/test_openflags.cpp + test-src/webfuse/filesystem/test_filemode.cpp + test-src/webfuse/filesystem/test_userid.cpp + test-src/webfuse/filesystem/test_groupid.cpp + test-src/webfuse/filesystem/test_fileattributes.cpp ) target_include_directories(alltests PRIVATE ${GTEST_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS}) diff --git a/src/webfuse/filesystem/accessmode.cpp b/src/webfuse/filesystem/accessmode.cpp new file mode 100644 index 0000000..6753a5c --- /dev/null +++ b/src/webfuse/filesystem/accessmode.cpp @@ -0,0 +1,41 @@ +#include "webfuse/filesystem/accessmode.hpp" +#include + +namespace webfuse +{ + +access_mode::access_mode(int8_t value) +: value_(value) +{ + +} + +access_mode::operator int8_t() const +{ + return value_; +} + +access_mode access_mode::from_int(int value) +{ + int8_t result = 0; + + if (0 != (value & R_OK)) { result |= r_ok; } + if (0 != (value & W_OK)) { result |= w_ok; } + if (0 != (value & X_OK)) { result |= x_ok; } + + return access_mode(result); +} + +int access_mode::to_int() const +{ + int result = 0; + + if (0 != (value_ & r_ok)) { result |= R_OK; } + if (0 != (value_ & w_ok)) { result |= W_OK; } + if (0 != (value_ & x_ok)) { result |= X_OK; } + + return result; +} + + +} \ No newline at end of file diff --git a/src/webfuse/filesystem/accessmode.hpp b/src/webfuse/filesystem/accessmode.hpp new file mode 100644 index 0000000..57b8139 --- /dev/null +++ b/src/webfuse/filesystem/accessmode.hpp @@ -0,0 +1,28 @@ +#ifndef WEBFUSE_ACCESSMODE_HPP +#define WEBFUSE_ACCESSMODE_HPP + +#include + +namespace webfuse +{ + +class access_mode +{ +public: + static constexpr int8_t const f_ok = 0; // F_OK + static constexpr int8_t const r_ok = 4; // R_OK + static constexpr int8_t const w_ok = 2; // W_OK + static constexpr int8_t const x_ok = 1; // X_OK + + access_mode(int8_t value = f_ok); + operator int8_t() const; + + static access_mode from_int(int value); + int to_int() const; +private: + int8_t value_; +}; + +} + +#endif diff --git a/src/webfuse/filesystem/fileattributes.cpp b/src/webfuse/filesystem/fileattributes.cpp new file mode 100644 index 0000000..d6fb3b7 --- /dev/null +++ b/src/webfuse/filesystem/fileattributes.cpp @@ -0,0 +1,46 @@ +#include "webfuse/filesystem/fileattributes.hpp" + +namespace webfuse +{ + +file_attributes::file_attributes() +: inode(0) +, nlink(0) +, rdev(0) +, size(0) +, blocks(0) +{ + +} + +file_attributes::file_attributes(struct stat const & other) +{ + inode = static_cast(other.st_ino); + nlink = static_cast(other.st_nlink); + mode = filemode::from_mode(other.st_mode); + uid = user_id::from_uid(other.st_uid); + gid = group_id::from_gid(other.st_gid); + rdev = static_cast(other.st_rdev); + size = static_cast(other.st_size); + blocks = static_cast(other.st_blocks); + atime = filetime::from_timespec(other.st_atim); + mtime = filetime::from_timespec(other.st_mtim); + ctime = filetime::from_timespec(other.st_ctim); +} + +void file_attributes::to_stat(struct stat & other) const +{ + other.st_ino = inode; + other.st_nlink = nlink; + other.st_mode = mode.to_mode(); + other.st_uid = uid.to_uid(); + other.st_gid = gid.to_gid(); + other.st_rdev = rdev; + other.st_size = size; + other.st_blocks = blocks; + atime.to_timespec(other.st_atim); + mtime.to_timespec(other.st_mtim); + ctime.to_timespec(other.st_ctim); +} + +} \ No newline at end of file diff --git a/src/webfuse/filesystem/fileattributes.hpp b/src/webfuse/filesystem/fileattributes.hpp new file mode 100644 index 0000000..ba8f7fe --- /dev/null +++ b/src/webfuse/filesystem/fileattributes.hpp @@ -0,0 +1,37 @@ +#ifndef WEBFUSE_FILEATTRIBUTES_HPP +#define WEBFUSE_FILEATTRIBUTES_HPP + +#include "webfuse/filesystem/filemode.hpp" +#include "webfuse/filesystem/filetime.hpp" +#include "webfuse/filesystem/userid.hpp" +#include "webfuse/filesystem/groupid.hpp" +#include +#include + +namespace webfuse +{ + +class file_attributes +{ +public: + file_attributes(); + + explicit file_attributes(struct stat const & other); + void to_stat(struct stat & other) const; + + uint64_t inode; + uint64_t nlink; + filemode mode; + user_id uid; + group_id gid; + uint64_t rdev; + uint64_t size; + uint64_t blocks; + filetime atime; + filetime mtime; + filetime ctime; +}; + +} + +#endif diff --git a/src/webfuse/filesystem/filehandle.hpp b/src/webfuse/filesystem/filehandle.hpp new file mode 100644 index 0000000..620074e --- /dev/null +++ b/src/webfuse/filesystem/filehandle.hpp @@ -0,0 +1,16 @@ +#ifndef WEBFUSE_FILEHANDLE_HPP +#define WEBFUSE_FILEHANDLE_HPP + +#include + +namespace webfuse +{ + +using filehandle = uint64_t; + +constexpr filehandle const invalid_handle = (filehandle) -1; + + +} + +#endif diff --git a/src/webfuse/filesystem/filemode.cpp b/src/webfuse/filesystem/filemode.cpp new file mode 100644 index 0000000..6d1213f --- /dev/null +++ b/src/webfuse/filesystem/filemode.cpp @@ -0,0 +1,50 @@ +#include "webfuse/filesystem/filemode.hpp" +#include + +namespace webfuse +{ + +filemode::filemode(uint32_t value) +: value_(value) +{ + +} + +filemode::operator uint32_t() const +{ + return value_; +} + + +filemode filemode::from_mode(mode_t value) +{ + uint32_t result = value & 07777; + + if (S_ISREG(value) ) { result |= filemode::reg; } + if (S_ISDIR(value) ) { result |= filemode::dir; } + if (S_ISCHR(value) ) { result |= filemode::chr; } + if (S_ISBLK(value) ) { result |= filemode::blk; } + if (S_ISFIFO(value)) { result |= filemode::fifo; } + if (S_ISLNK(value) ) { result |= filemode::lnk; } + if (S_ISSOCK(value)) { result |= filemode::sock; } + + return filemode(result); +} + +mode_t filemode::to_mode() const +{ + mode_t result = value_ & 07777; + + if (is_reg() ) { result |= S_IFREG; } + if (is_dir() ) { result |= S_IFDIR; } + if (is_chr() ) { result |= S_IFCHR; } + if (is_blk() ) { result |= S_IFBLK; } + if (is_fifo()) { result |= S_IFIFO; } + if (is_lnk() ) { result |= S_IFLNK; } + if (is_sock()) { result |= S_IFSOCK; } + + return result; +} + + +} \ No newline at end of file diff --git a/src/webfuse/filesystem/filemode.hpp b/src/webfuse/filesystem/filemode.hpp new file mode 100644 index 0000000..64af918 --- /dev/null +++ b/src/webfuse/filesystem/filemode.hpp @@ -0,0 +1,75 @@ +#ifndef WEBFUSE_FILEMODE_HPP +#define WEBFUSE_FILEMODE_HPP + +#include +#include + +namespace webfuse +{ + +class filemode +{ +public: + static constexpr uint32_t const protection_mask = 0000777; + static constexpr uint32_t const sticky_mask = 0007000; + static constexpr uint32_t const filetype_mask = 0170000; + + static constexpr uint32_t const suid = 04000; // S_ISUID + static constexpr uint32_t const sgid = 02000; // S_ISGID + static constexpr uint32_t const svtx = 01000; // S_ISVTX + + static constexpr uint32_t const reg = 0100000; // S_IFREG + static constexpr uint32_t const dir = 0040000; // S_IFDIR + static constexpr uint32_t const chr = 0020000; // S_IFCHR + static constexpr uint32_t const blk = 0060000; // S_IFBLK + static constexpr uint32_t const fifo = 0010000; // S_IFIFO + static constexpr uint32_t const lnk = 0120000; // S_IFLNK + static constexpr uint32_t const sock = 0140000; // S_IFSOCK + + explicit filemode(uint32_t value = 0); + operator uint32_t() const; + static filemode from_mode(mode_t value); + mode_t to_mode() const; + + inline bool is_reg() const + { + return (filemode::reg == (value_ & filemode::filetype_mask)); + } + + inline bool is_dir() const + { + return (filemode::dir == (value_ & filemode::filetype_mask)); + } + + inline bool is_chr() const + { + return (filemode::chr == (value_ & filemode::filetype_mask)); + } + + inline bool is_blk() const + { + return (filemode::blk == (value_ & filemode::filetype_mask)); + } + + inline bool is_fifo() const + { + return (filemode::fifo == (value_ & filemode::filetype_mask)); + } + + inline bool is_lnk() const + { + return (filemode::lnk == (value_ & filemode::filetype_mask)); + } + + inline bool is_sock() const + { + return (filemode::sock == (value_ & filemode::filetype_mask)); + } + +private: + uint32_t value_; +}; + +} + +#endif diff --git a/src/webfuse/filesystem/filesystem_i.hpp b/src/webfuse/filesystem/filesystem_i.hpp new file mode 100644 index 0000000..03f2e58 --- /dev/null +++ b/src/webfuse/filesystem/filesystem_i.hpp @@ -0,0 +1,56 @@ +#ifndef WEBFUSE_FILESYSTEM_I_HPP +#define WEBFUSE_FILESYSTEM_I_HPP + +#include "webfuse/filesystem/filehandle.hpp" +#include "webfuse/filesystem/accessmode.hpp" +#include "webfuse/filesystem/filemode.hpp" +#include "webfuse/filesystem/fileattributes.hpp" +#include "webfuse/filesystem/openflags.hpp" +#include "webfuse/filesystem/userid.hpp" +#include "webfuse/filesystem/groupid.hpp" +#include "webfuse/filesystem/status.hpp" +#include "webfuse/filesystem/filesystem_statistics.hpp" + +#include +#include +#include + +namespace webfuse +{ + +class filesystem_i +{ +public: + virtual ~filesystem_i() = default; + + virtual status access(std::string const & path, access_mode mode) = 0; + virtual status getattr(std::string const & path, file_attributes & attr) = 0; + + virtual status readlink(std::string const & path, std::string & out) = 0; + virtual status symlink(std::string const & target, std::string const & linkpath) = 0; + virtual status link(std::string const & old_path, std::string const & new_path) = 0; + + virtual status rename(std::string const & old_path, std::string const & new_path) = 0; + virtual status chmod(std::string const & path, filemode mode) = 0; + virtual status chown(std::string const & path, user_id uid, group_id gid); + virtual status truncate(std::string const & path, uint64_t offset, filehandle handle) = 0; + virtual status fsync(std::string const & path, bool is_datasync, filehandle handle) = 0; + + virtual status open(std::string const & path, openflags flags, filehandle & handle) = 0; + virtual status mknod(std::string const & path, filemode mode, uint64_t rdev) = 0; + virtual status create(std::string const & path, filemode mode, filehandle & handle) = 0; + virtual status release(std::string const & path, filehandle handle) = 0; + virtual status unlink(std::string const & path) = 0; + + virtual status read(std::string const & path, char * buffer, size_t buffer_size, uint64_t offset, filehandle handle) = 0; + virtual status write(std::string const & path, char const * buffer, size_t buffer_size, uint64_t offset, filehandle handle) = 0; + + virtual status readdir(std::string const & path, std::vector entries, filehandle handle) = 0; + virtual status rmdir(std::string const & path) = 0; + + virtual status statfs(std::string const & path, filesystem_statistics & statistics) = 0; +}; + +} + +#endif diff --git a/src/webfuse/filesystem/filesystem_statistics.hpp b/src/webfuse/filesystem/filesystem_statistics.hpp new file mode 100644 index 0000000..2534a09 --- /dev/null +++ b/src/webfuse/filesystem/filesystem_statistics.hpp @@ -0,0 +1,31 @@ +#ifndef WEBFUSE_FILESYSTEMSTATISTICS_HPP +#define WEBFUSE_FILESYSTEMSTATISTICS_HPP + +#include +#include + +namespace webfuse +{ + +class filesystem_statistics +{ +public: + filesystem_statistics(); + explicit filesystem_statistics(struct statvfs const & other); + void copy_to(struct statvfs & other) const; + + uint64_t bsize; + uint64_t frsize; + uint64_t blocks; + uint64_t bfree; + uint64_t bavail; + uint64_t files; + uint64_t ffree; + uint64_t f_namemax; +}; + + + +} + +#endif diff --git a/src/webfuse/filesystem/filetime.cpp b/src/webfuse/filesystem/filetime.cpp new file mode 100644 index 0000000..a10f14e --- /dev/null +++ b/src/webfuse/filesystem/filetime.cpp @@ -0,0 +1,43 @@ +#include "webfuse/filesystem/filetime.hpp" + +namespace webfuse +{ + +filetime::filetime() +: seconds(0) +, nsec(0) +{ + +} + +filetime filetime::from_timespec(timespec const & other) +{ + filetime result; + result.seconds = static_cast(other.tv_sec); + result.nsec = static_cast(other.tv_nsec); + + return result; +} + +filetime filetime::from_time(time_t const & other) +{ + filetime result; + result.seconds = static_cast(other); + result.nsec = 0; + + return result; +} + +void filetime::to_timespec(timespec & other) const +{ + other.tv_sec = seconds; + other.tv_nsec = static_cast(nsec); +} + +time_t filetime::to_time() const +{ + return static_cast(seconds); +} + + +} \ No newline at end of file diff --git a/src/webfuse/filesystem/filetime.hpp b/src/webfuse/filesystem/filetime.hpp new file mode 100644 index 0000000..8f77ff8 --- /dev/null +++ b/src/webfuse/filesystem/filetime.hpp @@ -0,0 +1,25 @@ +#ifndef WEBFUSE_FILETIME_HPP +#define WEBFUSE_FILETIME_HPP + +#include +#include + +namespace webfuse +{ + +class filetime +{ +public: + filetime(); + static filetime from_timespec(timespec const & other); + static filetime from_time(time_t const & other); + void to_timespec(timespec & other) const; + time_t to_time() const; + + uint64_t seconds; + uint32_t nsec; +}; + +} + +#endif diff --git a/src/webfuse/filesystem/groupid.cpp b/src/webfuse/filesystem/groupid.cpp new file mode 100644 index 0000000..84583e5 --- /dev/null +++ b/src/webfuse/filesystem/groupid.cpp @@ -0,0 +1,27 @@ +#include "webfuse/filesystem/groupid.hpp" + +namespace webfuse +{ + +group_id::group_id(uint32_t value) +: value_(value) +{ + +} + +group_id::operator uint32_t() const +{ + return value_; +} + +group_id group_id::from_gid(gid_t value) +{ + return group_id(static_cast(value)); +} + +gid_t group_id::to_gid() const +{ + return static_cast(value_); +} + +} \ No newline at end of file diff --git a/src/webfuse/filesystem/groupid.hpp b/src/webfuse/filesystem/groupid.hpp new file mode 100644 index 0000000..e3b8ac0 --- /dev/null +++ b/src/webfuse/filesystem/groupid.hpp @@ -0,0 +1,25 @@ +#ifndef WEBFUSE_GROUPID_HPP +#define WEBFUSE_GROUPID_HPP + +#include +#include + +namespace webfuse +{ + +class group_id +{ +public: + static constexpr uint32_t const invalid = (uint32_t) -1; + + explicit group_id(uint32_t value = invalid); + operator uint32_t() const; + static group_id from_gid(gid_t value); + gid_t to_gid() const; +private: + uint32_t value_; +}; + +} + +#endif diff --git a/src/webfuse/filesystem/openflags.cpp b/src/webfuse/filesystem/openflags.cpp new file mode 100644 index 0000000..7a0502c --- /dev/null +++ b/src/webfuse/filesystem/openflags.cpp @@ -0,0 +1,69 @@ +#include "webfuse/filesystem/openflags.hpp" +#include + +namespace webfuse +{ + +openflags::openflags(int32_t value) +: value_(value) +{ + +} + +openflags::operator int32_t() const +{ + return value_; +} + +openflags openflags::from_int(int value) +{ + int32_t result = 0; + + if (O_RDONLY == (value & O_RDONLY )) { result |= openflags::rdonly; } + if (O_WRONLY == (value & O_WRONLY )) { result |= openflags::wronly; } + if (O_RDWR == (value & O_RDWR )) { result |= openflags::rdwr; } + if (O_CLOEXEC == (value & O_CLOEXEC )) { result |= openflags::cloexec; } + if (O_CREAT == (value & O_CREAT )) { result |= openflags::creat; } + if (O_DIRECT == (value & O_DIRECT )) { result |= openflags::direct; } + if (O_DIRECTORY == (value & O_DIRECTORY)) { result |= openflags::directory; } + if (O_EXCL == (value & O_EXCL )) { result |= openflags::excl; } + if (O_NOCTTY == (value & O_NOCTTY )) { result |= openflags::noctty; } + if (O_NOFOLLOW == (value & O_NOFOLLOW )) { result |= openflags::nofollow; } + if (O_TRUNC == (value & O_TRUNC )) { result |= openflags::trunc; } + if (O_ASYNC == (value & O_ASYNC )) { result |= openflags::async; } + if (O_LARGEFILE == (value & O_LARGEFILE)) { result |= openflags::largefile; } + if (O_NOATIME == (value & O_NOATIME )) { result |= openflags::noatime; } + if (O_NONBLOCK == (value & O_NONBLOCK )) { result |= openflags::nonblock; } + if (O_NDELAY == (value & O_NDELAY )) { result |= openflags::ndelay; } + if (O_SYNC == (value & O_SYNC )) { result |= openflags::sync; } + + return openflags(result); +} + +int openflags::to_int() const +{ + int result = 0; + + if (openflags::rdonly == (value_ & openflags::rdonly )) { result |= O_RDONLY; } + if (openflags::wronly == (value_ & openflags::wronly )) { result |= O_WRONLY; } + if (openflags::rdwr == (value_ & openflags::rdwr )) { result |= O_RDWR; } + if (openflags::cloexec == (value_ & openflags::cloexec )) { result |= O_CLOEXEC; } + if (openflags::creat == (value_ & openflags::creat )) { result |= O_CREAT; } + if (openflags::direct == (value_ & openflags::direct )) { result |= O_DIRECT; } + if (openflags::directory == (value_ & openflags::directory)) { result |= O_DIRECTORY; } + if (openflags::excl == (value_ & openflags::excl )) { result |= O_EXCL; } + if (openflags::noctty == (value_ & openflags::noctty )) { result |= O_NOCTTY; } + if (openflags::nofollow == (value_ & openflags::nofollow )) { result |= O_NOFOLLOW; } + if (openflags::trunc == (value_ & openflags::trunc )) { result |= O_TRUNC; } + if (openflags::async == (value_ & openflags::async )) { result |= O_ASYNC; } + if (openflags::largefile == (value_ & openflags::largefile)) { result |= O_LARGEFILE; } + if (openflags::noatime == (value_ & openflags::noatime )) { result |= O_NOATIME; } + if (openflags::nonblock == (value_ & openflags::nonblock )) { result |= O_NONBLOCK; } + if (openflags::ndelay == (value_ & openflags::ndelay )) { result |= O_NDELAY; } + if (openflags::sync == (value_ & openflags::sync )) { result |= O_SYNC; } + + return result; +} + + +} \ No newline at end of file diff --git a/src/webfuse/filesystem/openflags.hpp b/src/webfuse/filesystem/openflags.hpp new file mode 100644 index 0000000..44541f1 --- /dev/null +++ b/src/webfuse/filesystem/openflags.hpp @@ -0,0 +1,43 @@ +#ifndef WEBFUSE_OPENFLAGS_HPP +#define WEBFUSE_OPENFLAGS_HPP + +#include + +namespace webfuse +{ + +class openflags +{ +public: + static constexpr int32_t const accessmode_mask = 3; // O_ACCMODE + + static constexpr int32_t const rdonly = 00000000; // O_RDONLY + static constexpr int32_t const wronly = 00000001; // O_WRONLY + static constexpr int32_t const rdwr = 00000002; // O_RDWR + static constexpr int32_t const cloexec = 02000000; // O_CLOEXEC + static constexpr int32_t const creat = 00000100; // O_CREAT + static constexpr int32_t const direct = 00040000; // O_DIRECT + static constexpr int32_t const directory = 00200000; // O_DIRECTORY + static constexpr int32_t const excl = 00000200; // O_EXCL + static constexpr int32_t const noctty = 00000400; // O_NOCTTY + static constexpr int32_t const nofollow = 00400000; // O_NOFOLLOW + static constexpr int32_t const trunc = 00001000; // O_TRUNC + static constexpr int32_t const async = 00002000; // O_ASYNC + static constexpr int32_t const largefile = 00000000; // O_LARGEFILE + static constexpr int32_t const noatime = 01000000; // O_NOATIME + static constexpr int32_t const nonblock = 00004000; // O_NONBLOCK + static constexpr int32_t const ndelay = 00004000; // O_NDELAY + static constexpr int32_t const sync = 04010000; // O_SYNC + + explicit openflags(int32_t value = 0); + operator int32_t() const; + + static openflags from_int(int value); + int to_int() const; +private: + int32_t value_; +}; + +} + +#endif diff --git a/src/webfuse/filesystem/status.cpp b/src/webfuse/filesystem/status.cpp new file mode 100644 index 0000000..cda71cc --- /dev/null +++ b/src/webfuse/filesystem/status.cpp @@ -0,0 +1,120 @@ +#include "webfuse/filesystem/status.hpp" +#include + +namespace webfuse +{ + +status::status(int32_t value) +: value_(value) +{ + +} + +status::operator int32_t() const +{ + return value_; +} + +status status::from_fusestatus(int value) +{ + if (value >= 0) + { + return static_cast(value); + } + else + { + switch(value) + { + case -E2BIG: return status::bad_e2big; + case -EACCES: return status::bad_eacces; + case -EAGAIN: return status::bad_eagain; + case -EBADF: return status::bad_ebadf; + case -EBUSY: return status::bad_ebusy; + case -EDESTADDRREQ: return status::bad_edestaddrreq; + case -EDQUOT: return status::bad_edquot; + case -EEXIST: return status::bad_eexist; + case -EFAULT: return status::bad_efault; + case -EFBIG: return status::bad_efbig; + case -EINTR: return status::bad_eintr; + case -EINVAL: return status::bad_einval; + case -EIO: return status::bad_eio; + case -EISDIR: return status::bad_eisdir; + case -ELOOP: return status::bad_eloop; + case -EMFILE: return status::bad_emfile; + case -EMLINK: return status::bad_emlink; + case -ENAMETOOLONG: return status::bad_enametoolong; + case -ENFILE: return status::bad_enfile; + case -ENODATA: return status::bad_enodata; + case -ENODEV: return status::bad_enodev; + case -ENOENT: return status::bad_enoent; + case -ENOMEM: return status::bad_enomem; + case -ENOSPC: return status::bad_enospc; + case -ENOSYS: return status::bad_enosys; + case -ENOTDIR: return status::bad_enotdir; + case -ENOTEMPTY: return status::bad_enotempty; + case -ENOTSUP: return status::bad_enotsup; + case -ENXIO: return status::bad_enxio; + case -EOVERFLOW: return status::bad_eoverflow; + case -EPERM: return status ::bad_eperm; + case -EPIPE: return status::bad_epipe; + case -ERANGE: return status::bad_erange; + case -EROFS: return status::bad_erofs; + case -ETXTBSY: return status::bad_etxtbsy; + case -EXDEV: return status::bad_exdev; + default: return static_cast(value); + } + } +} + +int status::to_fusestatus() const +{ + if (value_ >= 0) + { + return static_cast(value_); + } + else + { + switch(value_) + { + case status::bad_e2big: return -E2BIG; + case status::bad_eacces: return -EACCES; + case status::bad_eagain: return -EAGAIN; + case status::bad_ebadf: return -EBADF; + case status::bad_ebusy: return -EBUSY; + case status::bad_edestaddrreq: return -EDESTADDRREQ; + case status::bad_edquot: return -EDQUOT; + case status::bad_eexist: return -EEXIST; + case status::bad_efault: return -EFAULT; + case status::bad_efbig: return -EFBIG; + case status::bad_eintr: return -EINTR; + case status::bad_einval: return -EINVAL; + case status::bad_eio: return -EIO; + case status::bad_eisdir: return -EISDIR; + case status::bad_eloop: return -ELOOP; + case status::bad_emfile: return -EMFILE; + case status::bad_emlink: return -EMLINK; + case status::bad_enametoolong: return -ENAMETOOLONG; + case status::bad_enfile: return -ENFILE; + case status::bad_enodata: return -ENODATA; + case status::bad_enodev: return -ENODEV; + case status::bad_enoent: return -ENOENT; + case status::bad_enomem: return -ENOMEM; + case status::bad_enospc: return -ENOSPC; + case status::bad_enosys: return -ENOSYS; + case status::bad_enotdir: return -ENOTDIR; + case status::bad_enotempty: return -ENOTEMPTY; + case status::bad_enotsup: return -ENOTSUP; + case status::bad_enxio: return -ENXIO; + case status::bad_eoverflow: return -EOVERFLOW; + case status::bad_eperm: return -EPERM; + case status::bad_epipe: return -EPIPE; + case status::bad_erange: return -ERANGE; + case status::bad_erofs: return -EROFS; + case status::bad_etxtbsy: return -ETXTBSY; + case status::bad_exdev: return -EXDEV; + default: return static_cast(value_); + } + } +} + +} \ No newline at end of file diff --git a/src/webfuse/filesystem/status.hpp b/src/webfuse/filesystem/status.hpp new file mode 100644 index 0000000..ee322a1 --- /dev/null +++ b/src/webfuse/filesystem/status.hpp @@ -0,0 +1,62 @@ +#ifndef WEBFUSE_STATUS_HPP +#define WEBFUSE_STATUS_HPP + +#include + +namespace webfuse +{ + +class status +{ +public: + static constexpr int32_t const good = 0; + static constexpr int32_t const bad_e2big = -7; // -E2BIG + static constexpr int32_t const bad_eacces = -13; // -EACCES + static constexpr int32_t const bad_eagain = -11; // -EAGAIN + static constexpr int32_t const bad_ebadf = -9; // -EBADF + static constexpr int32_t const bad_ebusy = -16; // -EBUSY + static constexpr int32_t const bad_edestaddrreq = -89; // -EDESTADDRREQ + static constexpr int32_t const bad_edquot = -122; // -EDQUOT + static constexpr int32_t const bad_eexist = -17; // -EEXIST + static constexpr int32_t const bad_efault = -14; // -EFAULT + static constexpr int32_t const bad_efbig = -27; // -EFBIG + static constexpr int32_t const bad_eintr = -4; // -EINTR + static constexpr int32_t const bad_einval = -22; // -EINVAL + static constexpr int32_t const bad_eio = -5; // -EIO + static constexpr int32_t const bad_eisdir = -21; // -EISDIR + static constexpr int32_t const bad_eloop = -40; // -ELOOP + static constexpr int32_t const bad_emfile = -24; // -EMFILE + static constexpr int32_t const bad_emlink = -31; // -EMLINK + static constexpr int32_t const bad_enametoolong = -36; // -ENAMETOOLONG + static constexpr int32_t const bad_enfile = -23; // -ENFILE + static constexpr int32_t const bad_enodata = -61; // -ENODATA + static constexpr int32_t const bad_enodev = -19; // -ENODEV + static constexpr int32_t const bad_enoent = -2; // -ENOENT + static constexpr int32_t const bad_enomem = -12; // -ENOMEM + static constexpr int32_t const bad_enospc = -28; // -ENOSPC + static constexpr int32_t const bad_enosys = -38; // -ENOSYS + static constexpr int32_t const bad_enotdir = -20; // -ENOTDIR + static constexpr int32_t const bad_enotempty = -39; // -ENOTEMPTY + static constexpr int32_t const bad_enotsup = -95; // -ENOTSUP + static constexpr int32_t const bad_enxio = -6; // -ENXIO + static constexpr int32_t const bad_eoverflow = -75; // -EOVERFLOW + static constexpr int32_t const bad_eperm = -1; // -EPERM + static constexpr int32_t const bad_epipe = -32; // -EPIPE + static constexpr int32_t const bad_erange = -34; // -ERANGE + static constexpr int32_t const bad_erofs = -30; // -EROFS + static constexpr int32_t const bad_etxtbsy = -26; // -ETXTBSY + static constexpr int32_t const bad_exdev = -18; // -EXDEV + static constexpr int32_t const bad_ewouldblock = -11; // -EWOULDBLOCK + + status(int32_t value = status::good); + operator int32_t() const; + + static status from_fusestatus(int value); + int to_fusestatus() const; +private: + int32_t value_; +}; + +} + +#endif \ No newline at end of file diff --git a/src/webfuse/filesystem/userid.cpp b/src/webfuse/filesystem/userid.cpp new file mode 100644 index 0000000..36f37f0 --- /dev/null +++ b/src/webfuse/filesystem/userid.cpp @@ -0,0 +1,28 @@ +#include "webfuse/filesystem/userid.hpp" + +namespace webfuse +{ + +user_id::user_id(uint32_t value) +: value_(value) +{ + +} + +user_id::operator uint32_t() const +{ + return value_; +} + +user_id user_id::from_uid(uid_t value) +{ + return user_id(static_cast(value)); +} + +uid_t user_id::to_uid() const +{ + return static_cast(value_); +} + + +} \ No newline at end of file diff --git a/src/webfuse/filesystem/userid.hpp b/src/webfuse/filesystem/userid.hpp new file mode 100644 index 0000000..dc43673 --- /dev/null +++ b/src/webfuse/filesystem/userid.hpp @@ -0,0 +1,25 @@ +#ifndef WEBFUSE_USERID_HPP +#define WEBFUSE_USERID_HPP + +#include +#include + +namespace webfuse +{ + +class user_id +{ +public: + static constexpr uint32_t const invalid = (uint32_t) -1; + + explicit user_id(uint32_t value = invalid); + operator uint32_t() const; + static user_id from_uid(uid_t value); + uid_t to_uid() const; +private: + uint32_t value_; +}; + +} + +#endif diff --git a/src/webfuse/fuse.hpp b/src/webfuse/fuse.hpp new file mode 100644 index 0000000..39acb9e --- /dev/null +++ b/src/webfuse/fuse.hpp @@ -0,0 +1,26 @@ +#ifndef WEBFUSE_FUSE_HPP +#define WEBFUSE_FUSE_HPP + +#include "webfuse/filesystem/filesystem_i.hpp" + +namespace webfuse +{ + +class fuse +{ + fuse (fuse const &) = delete; + fuse& operator=(fuse const &) = delete; +public: + explicit fuse(filesystem_i & filesystem); + ~fuse(); + fuse (fuse &&) = delete; + fuse& operator=(fuse &&) = delete; + void run(int argc, char * argv[]); +private: + class detail; + detail * d; +}; + +} + +#endif diff --git a/test-src/webfuse/filesystem/test_accessmode.cpp b/test-src/webfuse/filesystem/test_accessmode.cpp new file mode 100644 index 0000000..1f265cc --- /dev/null +++ b/test-src/webfuse/filesystem/test_accessmode.cpp @@ -0,0 +1,26 @@ +#include "webfuse/filesystem/accessmode.hpp" +#include + +using webfuse::access_mode; + +TEST(accessmode, f_ok) +{ + ASSERT_EQ(0, access_mode::f_ok); + ASSERT_EQ(F_OK, access_mode::f_ok); +} + +class accessmode_test: public testing::TestWithParam { }; + +TEST_P(accessmode_test, conversion) +{ + int const expected = GetParam(); + auto mode = access_mode::from_int(expected); + ASSERT_EQ(expected, mode.to_int()); +} + +INSTANTIATE_TEST_CASE_P(accesmode_values, accessmode_test, + testing::Values( + F_OK, R_OK, W_OK, X_OK, + R_OK | W_OK, R_OK | X_OK, W_OK | X_OK, + R_OK | W_OK | X_OK) +); diff --git a/test-src/webfuse/filesystem/test_fileattributes.cpp b/test-src/webfuse/filesystem/test_fileattributes.cpp new file mode 100644 index 0000000..f0ecd2e --- /dev/null +++ b/test-src/webfuse/filesystem/test_fileattributes.cpp @@ -0,0 +1,101 @@ +#include "webfuse/filesystem/fileattributes.hpp" + +#include + +using webfuse::file_attributes; +using webfuse::user_id; +using webfuse::group_id; +using webfuse::filemode; + +TEST(file_attibutes, create_empty) +{ + file_attributes attributes; + + ASSERT_EQ(0, attributes.inode); + ASSERT_EQ(0, attributes.nlink); + ASSERT_EQ(0, attributes.mode); + ASSERT_EQ(user_id::invalid, attributes.uid); + ASSERT_EQ(group_id::invalid, attributes.gid); + ASSERT_EQ(0, attributes.rdev); + ASSERT_EQ(0, attributes.size); + ASSERT_EQ(0, attributes.blocks); + ASSERT_EQ(0, attributes.atime.seconds); + ASSERT_EQ(0, attributes.atime.nsec); + ASSERT_EQ(0, attributes.mtime.seconds); + ASSERT_EQ(0, attributes.mtime.nsec); + ASSERT_EQ(0, attributes.ctime.seconds); + ASSERT_EQ(0, attributes.ctime.nsec); +} + +TEST(file_attibutes, from_stat) +{ + struct stat info; + info.st_ino = 1; + info.st_nlink = 2; + info.st_mode = S_IFREG | 0644; + info.st_uid = 1000; + info.st_gid = 1234; + info.st_rdev = 0; + info.st_size = 21 * 1024; + info.st_blocks = 42; + info.st_atim.tv_sec = 1; + info.st_atim.tv_nsec = 2; + info.st_mtim.tv_sec = 3; + info.st_mtim.tv_nsec = 4; + info.st_ctim.tv_sec = 5; + info.st_ctim.tv_nsec = 6; + + file_attributes attributes(info); + + ASSERT_EQ(info.st_ino, attributes.inode); + ASSERT_EQ(info.st_nlink, attributes.nlink); + ASSERT_EQ(info.st_mode, attributes.mode.to_mode()); + ASSERT_EQ(info.st_uid, attributes.uid.to_uid()); + ASSERT_EQ(info.st_gid, attributes.gid.to_gid()); + ASSERT_EQ(info.st_rdev, attributes.rdev); + ASSERT_EQ(info.st_size, attributes.size); + ASSERT_EQ(info.st_blocks, attributes.blocks); + ASSERT_EQ(info.st_atim.tv_sec, attributes.atime.seconds); + ASSERT_EQ(info.st_atim.tv_nsec, attributes.atime.nsec); + ASSERT_EQ(info.st_mtim.tv_sec, attributes.mtime.seconds); + ASSERT_EQ(info.st_mtim.tv_nsec, attributes.mtime.nsec); + ASSERT_EQ(info.st_ctim.tv_sec, attributes.ctime.seconds); + ASSERT_EQ(info.st_ctim.tv_nsec, attributes.ctime.nsec); +} + +TEST(file_attibutes, to_stat) +{ + file_attributes attributes; + attributes.inode = 1; + attributes.nlink = 2; + attributes.mode = filemode(S_IFREG | 0644); + attributes.uid = user_id(1000); + attributes.gid = group_id(1234); + attributes.rdev = 0; + attributes.size = 21 * 1024; + attributes.blocks = 42; + attributes.atime.seconds = 1; + attributes.atime.nsec = 2; + attributes.mtime.seconds = 3; + attributes.mtime.nsec = 4; + attributes.ctime.seconds = 5; + attributes.ctime.nsec = 6; + + struct stat info; + attributes.to_stat(info); + + ASSERT_EQ(attributes.inode, info.st_ino); + ASSERT_EQ(attributes.nlink, info.st_nlink); + ASSERT_EQ(attributes.mode.to_mode(), info.st_mode); + ASSERT_EQ(attributes.uid.to_uid(), info.st_uid); + ASSERT_EQ(attributes.gid.to_gid(), info.st_gid); + ASSERT_EQ(attributes.rdev, info.st_rdev); + ASSERT_EQ(attributes.size, info.st_size); + ASSERT_EQ(attributes.blocks, info.st_blocks); + ASSERT_EQ(attributes.atime.seconds, info.st_atim.tv_sec); + ASSERT_EQ(attributes.atime.nsec, info.st_atim.tv_nsec); + ASSERT_EQ(attributes.mtime.seconds, info.st_mtim.tv_sec); + ASSERT_EQ(attributes.mtime.nsec, info.st_mtim.tv_nsec); + ASSERT_EQ(attributes.ctime.seconds, info.st_ctim.tv_sec); + ASSERT_EQ(attributes.ctime.nsec, info.st_ctim.tv_nsec); +} \ No newline at end of file diff --git a/test-src/webfuse/filesystem/test_filemode.cpp b/test-src/webfuse/filesystem/test_filemode.cpp new file mode 100644 index 0000000..7c72af8 --- /dev/null +++ b/test-src/webfuse/filesystem/test_filemode.cpp @@ -0,0 +1,25 @@ +#include "webfuse/filesystem/filemode.hpp" +#include + +using webfuse::filemode; + +class filemode_test: public testing::TestWithParam { }; + +TEST_P(filemode_test, conversion) +{ + mode_t const expected = GetParam(); + auto value = filemode::from_mode(expected); + ASSERT_EQ(expected, value.to_mode()); +} + +INSTANTIATE_TEST_CASE_P(filemode_value, filemode_test, + testing::Values( + S_IROTH, S_IWOTH, S_IXOTH, + S_IRGRP, S_IWGRP, S_IXGRP, + S_IRUSR, S_IWUSR, S_IXUSR, + S_ISUID, S_ISGID, S_ISVTX, + S_IFREG, S_IFCHR, S_IFBLK, S_IFDIR, S_IFIFO, S_IFLNK, S_IFSOCK, + S_IFREG | 0644, + S_IFDIR | 0755 + ) +); \ No newline at end of file diff --git a/test-src/webfuse/filesystem/test_groupid.cpp b/test-src/webfuse/filesystem/test_groupid.cpp new file mode 100644 index 0000000..0b091f5 --- /dev/null +++ b/test-src/webfuse/filesystem/test_groupid.cpp @@ -0,0 +1,27 @@ +#include "webfuse/filesystem/groupid.hpp" +#include + +using webfuse::group_id; + +TEST(group_id, invalid) +{ + group_id invalid_group; + + ASSERT_EQ(group_id::invalid, invalid_group); +} + +TEST(group_id, to_gid) +{ + group_id group(69); + gid_t id = group.to_gid(); + + ASSERT_EQ(69, id); +} + +TEST(group_id, from_gid) +{ + gid_t id = 99; + auto group = group_id::from_gid(id); + + ASSERT_EQ(99, group); +} diff --git a/test-src/webfuse/filesystem/test_openflags.cpp b/test-src/webfuse/filesystem/test_openflags.cpp new file mode 100644 index 0000000..f36d9e1 --- /dev/null +++ b/test-src/webfuse/filesystem/test_openflags.cpp @@ -0,0 +1,24 @@ +#include "webfuse/filesystem/openflags.hpp" +#include +#include + +using webfuse::openflags; + +class openflags_test: public testing::TestWithParam { }; + +TEST_P(openflags_test, conversion) +{ + int const expected = GetParam(); + auto flags = openflags::from_int(expected); + ASSERT_EQ(expected, flags.to_int()); +} + +INSTANTIATE_TEST_CASE_P(openflags_values, openflags_test, + testing::Values<>( + O_RDONLY, O_WRONLY, O_RDWR, O_CLOEXEC, O_CREAT, + O_DIRECT, O_DIRECTORY, O_EXCL, O_NOCTTY, O_NOFOLLOW, + O_TRUNC, O_ASYNC, O_LARGEFILE, O_NOATIME, O_NONBLOCK, + O_NDELAY, O_SYNC, + O_WRONLY | O_CREAT | O_TRUNC + ) +); \ No newline at end of file diff --git a/test-src/webfuse/filesystem/test_status.cpp b/test-src/webfuse/filesystem/test_status.cpp new file mode 100644 index 0000000..52dcc7b --- /dev/null +++ b/test-src/webfuse/filesystem/test_status.cpp @@ -0,0 +1,28 @@ +#include "webfuse/filesystem/status.hpp" +#include +#include + +using webfuse::status; + +class status_test: public testing::TestWithParam { }; + +TEST_P(status_test, conversion) +{ + int const expected = GetParam(); + auto status = status::from_fusestatus(expected); + ASSERT_EQ(expected, status.to_fusestatus()); +} + +INSTANTIATE_TEST_CASE_P(status_values, status_test, + testing::Values( + 0, 1, 2, 3, 42, + -E2BIG, -EACCES, -EBADF, -EBUSY, -EDESTADDRREQ, + -EDQUOT, -EEXIST, -EFAULT, -EFBIG, -EINTR, + -EINVAL, -EIO, -EISDIR, -ELOOP, -EMFILE, + -ENAMETOOLONG, -ENFILE, -ENODATA, -ENODEV, + -ENOENT, -ENOMEM, -ENOSPC, -ENOSYS, -ENOTDIR, + -ENOTEMPTY, -ENOTSUP, -ENXIO, -EOVERFLOW, -EPERM, + -EPIPE, -ERANGE, -EROFS, -EXDEV, -EWOULDBLOCK, + -EAGAIN, -12345 + ) +); diff --git a/test-src/webfuse/filesystem/test_userid.cpp b/test-src/webfuse/filesystem/test_userid.cpp new file mode 100644 index 0000000..cb555d4 --- /dev/null +++ b/test-src/webfuse/filesystem/test_userid.cpp @@ -0,0 +1,27 @@ +#include "webfuse/filesystem/userid.hpp" +#include + +using webfuse::user_id; + +TEST(user_id, invalid) +{ + user_id invalid_user; + + ASSERT_EQ(user_id::invalid, invalid_user); +} + +TEST(user_id, to_uid) +{ + user_id user(42); + uid_t id = user.to_uid(); + + ASSERT_EQ(42, id); +} + +TEST(user_id, from_uid) +{ + uid_t id = 23; + auto user = user_id::from_uid(id); + + ASSERT_EQ(23, user); +} diff --git a/test/webfuse/test_app.cpp b/test-src/webfuse/test_app.cpp similarity index 100% rename from test/webfuse/test_app.cpp rename to test-src/webfuse/test_app.cpp