Loading .gitlab-ci.yml +4 −1 Original line number Diff line number Diff line Loading @@ -62,6 +62,9 @@ gkfs: - sed -i 's/constexpr bool use_dentry_cache = false;/constexpr bool use_dentry_cache = true;/g' "${CI_PROJECT_DIR}/include/config.hpp" #- sed -i 's/constexpr auto zero_buffer_before_read = false;/constexpr auto zero_buffer_before_read = true;/g' "${CI_PROJECT_DIR}/include/config.hpp" #- sed -i 's/constexpr auto implicit_data_removal = true;/constexpr auto implicit_data_removal = false;/g' "${CI_PROJECT_DIR}/include/config.hpp" # install libfuse - apt-get update - apt-get install -y libfuse3-dev fuse3 # use ccache - ccache --zero-stats -M 750MiB -F 800 --evict-older-than 10d - /usr/sbin/update-ccache-symlinks Loading include/client/fuse/fuse_client.hpp +5 −184 Original line number Diff line number Diff line Loading @@ -46,8 +46,7 @@ extern "C" { #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12) #include <fuse3/fuse_lowlevel.h> // TODO do we really need this? Now its to replace the libc.hpp def for the // sizeof(DIR) statement // TODO do we really need this? sizeof(DIR) statement, copied from gkfs_libc.hpp struct __dirstream { int fd; // File descriptor. //__libc_lock_define (, lock) // Mutex lock for this structure. //TODO Loading Loading @@ -75,29 +74,10 @@ struct __dirstream { #include <pthread.h> #include <sys/file.h> #include <sys/xattr.h> /* We are re-using pointers to our `struct lo_inode` and `struct lo_dirp` elements as inodes. This means that we must be able to store uintptr_t values in a fuse_ino_t variable. The following incantation checks this condition at compile time. */ #if defined(__GNUC__) && \ (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && \ !defined __cplusplus _Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t), "fuse_ino_t too small to hold uintptr_t values!"); #else struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { unsigned _uintptr_to_must_hold_fuse_ino_t : ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); }; #endif #include <errno.h> #include <fcntl.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <filesystem> #include <unordered_map> #include <string> #include <mutex> Loading @@ -117,98 +97,6 @@ struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { #include <client/preload_context.hpp> #include <client/user_functions.hpp> static inline int do_fallocate(int fd, int mode, off_t offset, off_t length) { #ifdef HAVE_FALLOCATE if(fallocate(fd, mode, offset, length) == -1) return -errno; return 0; #else // HAVE_FALLOCATE #ifdef HAVE_POSIX_FALLOCATE if(mode == 0) return -posix_fallocate(fd, offset, length); #endif #ifdef HAVE_FSPACECTL // 0x3 == FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE if(mode == 0x3) { struct spacectl_range sr; sr.r_offset = offset; sr.r_len = length; if(fspacectl(fd, SPACECTL_DEALLOC, &sr, 0, NULL) == -1) return -errno; return 0; } #endif return -EOPNOTSUPP; #endif // HAVE_FALLOCATE } // all from the gkfs_libc enum class PathStatus { External, Internal, Error }; /** * @brief Resolves a path in the context of GekkoFS. * (Details as in original comment) */ static inline PathStatus resolve_gkfs_path(int dirfd, const char* path, std::string& resolved, int flags = 0, bool resolve_last_link = true) { const auto status = CTX->relativize_fd_path(dirfd, path, resolved, flags, resolve_last_link); switch(status) { case gkfs::preload::RelativizeStatus::internal: return PathStatus::Internal; case gkfs::preload::RelativizeStatus::fd_not_a_dir: errno = ENOTDIR; return PathStatus::Error; case gkfs::preload::RelativizeStatus::fd_unknown: errno = EBADF; return PathStatus::Error; default: // Assuming other statuses mean external or not applicable return PathStatus::External; } } #define DEBUG_INFO(...) \ do { \ if(CTX->interception_enabled()) \ LOG(DEBUG, __VA_ARGS__); \ } while(0) #define GKFS_PATH_OPERATION(name, dirfd, path, ...) \ if(CTX->interception_enabled()) { \ std::string resolved; \ switch(resolve_gkfs_path(dirfd, path, resolved)) { \ case PathStatus::Internal: \ fuse_log(FUSE_LOG_DEBUG, "[GKFS] {}(path='{}')\n", #name, \ resolved.c_str()); \ return gkfs::syscall::gkfs_##name(resolved, __VA_ARGS__); \ case PathStatus::Error: \ return -1; \ default: /* External */ \ break; \ } \ } #define GKFS_PATH_OPERATION1(name, dirfd, path) \ if(CTX->interception_enabled()) { \ std::string resolved; \ switch(resolve_gkfs_path(dirfd, path, resolved)) { \ case PathStatus::Internal: \ fuse_log(FUSE_LOG_DEBUG, "[GKFS] {}(path='{}')\n", #name, \ resolved.c_str()); \ gkfs::syscall::gkfs_##name(resolved); \ case PathStatus::Error: \ fuse_reply_err(req, -1); \ default: /* External */ \ fuse_reply_err(req, -1); \ } \ } struct GkfsDir { // Hypothetical structure that might be used if DIR is cast int fd; long int tell_pos; // for telldir/seekdir Loading @@ -216,73 +104,6 @@ struct GkfsDir { // Hypothetical structure that might be used if DIR is cast // other members libc DIR might have }; static inline std::pair<int, std::string> lol_path(int dfd, const char* path, int flags) { std::string resolved = ""; int err = 0; if(CTX->interception_enabled()) { // AT_SYMLINK_NOFOLLOW means lstat behavior, otherwise stat behavior. bool follow_link = !(flags & AT_SYMLINK_NOFOLLOW); // Path resolution needs to know if it's for lstat or stat context for // the final component. resolve_gkfs_path's `resolve_last_link` // parameter handles this. switch(resolve_gkfs_path(dfd, path, resolved, flags, follow_link)) { case PathStatus::Internal: break; case PathStatus::Error: err = -1; break; default: // External break; } } return std::make_pair(err, resolved); } static inline int lol_fstatat(int dfd, const char* path, struct stat* buf, int flags) { if(CTX->interception_enabled()) { std::string resolved; // AT_SYMLINK_NOFOLLOW means lstat behavior, otherwise stat behavior. bool follow_link = !(flags & AT_SYMLINK_NOFOLLOW); // Path resolution needs to know if it's for lstat or stat context for // the final component. resolve_gkfs_path's `resolve_last_link` // parameter handles this. switch(resolve_gkfs_path(dfd, path, resolved, flags, follow_link)) { case PathStatus::Internal: return gkfs::syscall::gkfs_stat(resolved, buf, follow_link); case PathStatus::Error: return -1; // errno set default: // External break; } } return -1; } static inline int lol_openat(int dfd, const char* path, mode_t mode, int flags) { if(CTX->interception_enabled()) { std::string resolved; // AT_SYMLINK_NOFOLLOW means lstat behavior, otherwise stat behavior. bool follow_link = !(flags & AT_SYMLINK_NOFOLLOW); // Path resolution needs to know if it's for lstat or stat context for // the final component. resolve_gkfs_path's `resolve_last_link` // parameter handles this. switch(resolve_gkfs_path(dfd, path, resolved, flags, follow_link)) { case PathStatus::Internal: fuse_log(FUSE_LOG_DEBUG, "lol_openat internal\n"); return gkfs::syscall::gkfs_open(resolved, mode, follow_link); case PathStatus::Error: return -1; // errno set default: // External break; } } return -1; } /* * Creates files on the underlying file system in response to a FUSE_MKNOD * operation Loading @@ -294,12 +115,12 @@ mknod_wrapper(int dirfd, const char* path, const char* link, int mode, int res = -1; if(S_ISREG(mode)) { res = lol_openat(dirfd, path, mode, O_CREAT | O_EXCL | O_WRONLY); // res = lol_openat(dirfd, path, mode, O_CREAT | O_EXCL | O_WRONLY); fuse_log(FUSE_LOG_DEBUG, "lol_openat internal %s\n", res); if(res >= 0) res = gkfs::syscall::gkfs_close(res); } else if(S_ISDIR(mode)) { GKFS_PATH_OPERATION(create, dirfd, path, mode | S_IFDIR) // GKFS_PATH_OPERATION(create, dirfd, path, mode | S_IFDIR) // res = gkfs::syscall::gkfs_create(resolved, mode | S_IFDIR); // res = mkdirat(dirfd, path, mode); } else if(S_ISLNK(mode) && link != NULL) { Loading src/client/fuse/fuse_client.cpp +83 −33 Original line number Diff line number Diff line Loading @@ -37,14 +37,7 @@ SPDX-License-Identifier: LGPL-3.0-or-later */ #include "client/env.hpp" #include "common/env_util.hpp" #include <cerrno> #include <client/fuse/fuse_client.hpp> #include <cstdio> #include <cstdlib> #include <dirent.h> #include <iostream> struct lo_inode { struct lo_inode* next; /* protected by lo->mutex */ Loading Loading @@ -225,33 +218,34 @@ getattr_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { static void setattr_handler(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, struct fuse_file_info* fi) { fuse_log(FUSE_LOG_DEBUG, "setattr handler \n"); fuse_log(FUSE_LOG_DEBUG, "setattr handler ino %u\n", ino); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); return; } // TODO how to change attr? int rc = 0; if(rc) { fuse_reply_err(req, rc); if(to_set & FUSE_SET_ATTR_SIZE) { off_t new_size = attr->st_size; int res = gkfs::syscall::gkfs_truncate(inode->path, new_size); if(res < 0) { fuse_log(FUSE_LOG_DEBUG, "setattr truncate failed on %s\n", inode->path.c_str()); fuse_reply_err(req, EIO); return; } // TODO thats not in gkfs!!! // Update cached stat so users see the new size inode->st.st_size = new_size; } if(to_set & FUSE_SET_ATTR_ATIME) inode->st.st_atim = attr->st_atim; inode->st.st_blksize = attr->st_blksize; inode->st.st_blocks = attr->st_blocks; inode->st.st_ctim = attr->st_ctim; inode->st.st_dev = attr->st_dev; inode->st.st_gid = attr->st_gid; inode->st.st_ino = attr->st_ino; inode->st.st_mode = attr->st_mode; if(to_set & FUSE_SET_ATTR_MTIME) inode->st.st_mtim = attr->st_mtim; inode->st.st_nlink = attr->st_nlink; inode->st.st_rdev = attr->st_rdev; inode->st.st_size = attr->st_size; inode->st.st_uid = attr->st_uid; // TODO because we cannot save the attributes in gekko, we just return the // buffered results of stat fuse_reply_attr(req, &inode->st, 1.0); return; } static void Loading Loading @@ -353,6 +347,7 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, return; } fuse_ino_t ino = alloc_inode(path); fuse_log(FUSE_LOG_DEBUG, "create new inode ino %i\n", ino); ino_map[ino].st = st; fuse_entry_param e = {}; e.ino = ino; Loading Loading @@ -465,15 +460,69 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, static void init_handler(void* userdata, struct fuse_conn_info* conn) { fuse_log(FUSE_LOG_DEBUG, "init handler \n"); // userdata is GekkoFuse* if passed // optional: adjust conn->max_write, enable writeback etc. } static void destroy_handler(void* userdata) { fuse_log(FUSE_LOG_DEBUG, "destroy handler \n"); // userdata is GekkoFuse* if passed // optional: adjust conn->max_write, enable writeback etc. } static void release_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { // TODO decrease inode count fuse_log(FUSE_LOG_DEBUG, "release handler \n"); auto* inode = get_inode(ino); if(!inode) { fuse_log(FUSE_LOG_DEBUG, "release here \n"); fuse_reply_err(req, ENOENT); return; } int lc = gkfs::syscall::gkfs_close(fi->fh); if(lc < 0) { fuse_log(FUSE_LOG_DEBUG, "release there \n"); fuse_reply_err(req, 1); return; } fuse_log(FUSE_LOG_DEBUG, "release success \n"); fuse_reply_err(req, 0); } static void forget_handler(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { // TODO remove inode at some point if count == 0 fuse_log(FUSE_LOG_DEBUG, "forget handler \n"); fuse_reply_err(req, 0); } static void flush_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { fuse_log(FUSE_LOG_DEBUG, "flush handler \n"); auto* inode = get_inode(ino); if(!inode) { fuse_log(FUSE_LOG_DEBUG, "flush here \n"); fuse_reply_err(req, ENOENT); return; } int lc = gkfs::syscall::gkfs_fsync(fi->fh); if(lc < 0) { fuse_log(FUSE_LOG_DEBUG, "flush there \n"); fuse_reply_err(req, 1); return; } fuse_log(FUSE_LOG_DEBUG, "flush success \n"); fuse_reply_err(req, 0); } static const struct fuse_lowlevel_ops lo_oper = { .init = init_handler, // lo_init, //.destroy = lo_destroy, .init = init_handler, .destroy = destroy_handler, .lookup = lookup_handler, //.forget = lo_forget, .forget = forget_handler, .getattr = getattr_handler, .setattr = setattr_handler, //.readlink = lo_readlink, Loading @@ -487,8 +536,8 @@ static const struct fuse_lowlevel_ops lo_oper = { .open = open_handler, .read = read_handler, .write = write_handler, //.flush = lo_flush, //.release = lo_release, .flush = flush_handler, .release = release_handler, //.fsync = lo_fsync, .opendir = opendir_handler, .readdir = readdir_handler, Loading Loading @@ -532,7 +581,7 @@ main(int argc, char* argv[]) { int ret = -1; /* Don't mask creation mode, kernel already did that */ umask(0); umask(0); // TODO do we need this and why? pthread_mutex_init(&lo.mutex, NULL); lo.root.next = lo.root.prev = &lo.root; Loading Loading @@ -634,6 +683,7 @@ main(int argc, char* argv[]) { exit(1); } // TODO do we still want this? what do we want? fuse_log(FUSE_LOG_DEBUG, "hier 1\n"); lo.root.fd = gkfs::syscall::gkfs_open(lo.source, 755, O_PATH); if(lo.root.fd == -1) { Loading tests/integration/CMakeLists.txt +9 −0 Original line number Diff line number Diff line Loading @@ -164,6 +164,15 @@ if (GKFS_RENAME_SUPPORT) ) endif () if (GKFS_BUILD_FUSE) gkfs_add_python_test( NAME test_fuse_client PYTHON_VERSION 3.6 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/integration SOURCE fuse/ ) endif () if (GKFS_INSTALL_TESTS) install(DIRECTORY harness DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/integration Loading tests/integration/conftest.py +16 −4 Original line number Diff line number Diff line Loading @@ -34,7 +34,7 @@ from pathlib import Path from harness.logger import logger, initialize_logging, finalize_logging from harness.cli import add_cli_options, set_default_log_formatter from harness.workspace import Workspace, FileCreator from harness.gkfs import Daemon, Client, ClientLibc, Proxy, ShellClient, ShellClientLibc, FwdDaemon, FwdClient, ShellFwdClient, FwdDaemonCreator, FwdClientCreator from harness.gkfs import Daemon, Client, ClientLibc, Proxy, ShellClient, ShellClientLibc, FwdDaemon, FwdClient, ShellFwdClient, FwdDaemonCreator, FwdClientCreator, FuseClient from harness.reporter import report_test_status, report_test_headline, report_assertion_pass def pytest_configure(config): Loading Loading @@ -226,3 +226,15 @@ def gkfwd_client_factory(test_workspace): """ return FwdClientCreator(test_workspace) @pytest.fixture def fuse_client(test_workspace): """ Sets up a gekkofs fuse client environment so that operations (system calls, library calls, ...) can be requested from a co-running daemon. """ fuse_client = FuseClient(test_workspace) yield fuse_client.run() fuse_client.shutdown() Loading
.gitlab-ci.yml +4 −1 Original line number Diff line number Diff line Loading @@ -62,6 +62,9 @@ gkfs: - sed -i 's/constexpr bool use_dentry_cache = false;/constexpr bool use_dentry_cache = true;/g' "${CI_PROJECT_DIR}/include/config.hpp" #- sed -i 's/constexpr auto zero_buffer_before_read = false;/constexpr auto zero_buffer_before_read = true;/g' "${CI_PROJECT_DIR}/include/config.hpp" #- sed -i 's/constexpr auto implicit_data_removal = true;/constexpr auto implicit_data_removal = false;/g' "${CI_PROJECT_DIR}/include/config.hpp" # install libfuse - apt-get update - apt-get install -y libfuse3-dev fuse3 # use ccache - ccache --zero-stats -M 750MiB -F 800 --evict-older-than 10d - /usr/sbin/update-ccache-symlinks Loading
include/client/fuse/fuse_client.hpp +5 −184 Original line number Diff line number Diff line Loading @@ -46,8 +46,7 @@ extern "C" { #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12) #include <fuse3/fuse_lowlevel.h> // TODO do we really need this? Now its to replace the libc.hpp def for the // sizeof(DIR) statement // TODO do we really need this? sizeof(DIR) statement, copied from gkfs_libc.hpp struct __dirstream { int fd; // File descriptor. //__libc_lock_define (, lock) // Mutex lock for this structure. //TODO Loading Loading @@ -75,29 +74,10 @@ struct __dirstream { #include <pthread.h> #include <sys/file.h> #include <sys/xattr.h> /* We are re-using pointers to our `struct lo_inode` and `struct lo_dirp` elements as inodes. This means that we must be able to store uintptr_t values in a fuse_ino_t variable. The following incantation checks this condition at compile time. */ #if defined(__GNUC__) && \ (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && \ !defined __cplusplus _Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t), "fuse_ino_t too small to hold uintptr_t values!"); #else struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { unsigned _uintptr_to_must_hold_fuse_ino_t : ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); }; #endif #include <errno.h> #include <fcntl.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <filesystem> #include <unordered_map> #include <string> #include <mutex> Loading @@ -117,98 +97,6 @@ struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { #include <client/preload_context.hpp> #include <client/user_functions.hpp> static inline int do_fallocate(int fd, int mode, off_t offset, off_t length) { #ifdef HAVE_FALLOCATE if(fallocate(fd, mode, offset, length) == -1) return -errno; return 0; #else // HAVE_FALLOCATE #ifdef HAVE_POSIX_FALLOCATE if(mode == 0) return -posix_fallocate(fd, offset, length); #endif #ifdef HAVE_FSPACECTL // 0x3 == FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE if(mode == 0x3) { struct spacectl_range sr; sr.r_offset = offset; sr.r_len = length; if(fspacectl(fd, SPACECTL_DEALLOC, &sr, 0, NULL) == -1) return -errno; return 0; } #endif return -EOPNOTSUPP; #endif // HAVE_FALLOCATE } // all from the gkfs_libc enum class PathStatus { External, Internal, Error }; /** * @brief Resolves a path in the context of GekkoFS. * (Details as in original comment) */ static inline PathStatus resolve_gkfs_path(int dirfd, const char* path, std::string& resolved, int flags = 0, bool resolve_last_link = true) { const auto status = CTX->relativize_fd_path(dirfd, path, resolved, flags, resolve_last_link); switch(status) { case gkfs::preload::RelativizeStatus::internal: return PathStatus::Internal; case gkfs::preload::RelativizeStatus::fd_not_a_dir: errno = ENOTDIR; return PathStatus::Error; case gkfs::preload::RelativizeStatus::fd_unknown: errno = EBADF; return PathStatus::Error; default: // Assuming other statuses mean external or not applicable return PathStatus::External; } } #define DEBUG_INFO(...) \ do { \ if(CTX->interception_enabled()) \ LOG(DEBUG, __VA_ARGS__); \ } while(0) #define GKFS_PATH_OPERATION(name, dirfd, path, ...) \ if(CTX->interception_enabled()) { \ std::string resolved; \ switch(resolve_gkfs_path(dirfd, path, resolved)) { \ case PathStatus::Internal: \ fuse_log(FUSE_LOG_DEBUG, "[GKFS] {}(path='{}')\n", #name, \ resolved.c_str()); \ return gkfs::syscall::gkfs_##name(resolved, __VA_ARGS__); \ case PathStatus::Error: \ return -1; \ default: /* External */ \ break; \ } \ } #define GKFS_PATH_OPERATION1(name, dirfd, path) \ if(CTX->interception_enabled()) { \ std::string resolved; \ switch(resolve_gkfs_path(dirfd, path, resolved)) { \ case PathStatus::Internal: \ fuse_log(FUSE_LOG_DEBUG, "[GKFS] {}(path='{}')\n", #name, \ resolved.c_str()); \ gkfs::syscall::gkfs_##name(resolved); \ case PathStatus::Error: \ fuse_reply_err(req, -1); \ default: /* External */ \ fuse_reply_err(req, -1); \ } \ } struct GkfsDir { // Hypothetical structure that might be used if DIR is cast int fd; long int tell_pos; // for telldir/seekdir Loading @@ -216,73 +104,6 @@ struct GkfsDir { // Hypothetical structure that might be used if DIR is cast // other members libc DIR might have }; static inline std::pair<int, std::string> lol_path(int dfd, const char* path, int flags) { std::string resolved = ""; int err = 0; if(CTX->interception_enabled()) { // AT_SYMLINK_NOFOLLOW means lstat behavior, otherwise stat behavior. bool follow_link = !(flags & AT_SYMLINK_NOFOLLOW); // Path resolution needs to know if it's for lstat or stat context for // the final component. resolve_gkfs_path's `resolve_last_link` // parameter handles this. switch(resolve_gkfs_path(dfd, path, resolved, flags, follow_link)) { case PathStatus::Internal: break; case PathStatus::Error: err = -1; break; default: // External break; } } return std::make_pair(err, resolved); } static inline int lol_fstatat(int dfd, const char* path, struct stat* buf, int flags) { if(CTX->interception_enabled()) { std::string resolved; // AT_SYMLINK_NOFOLLOW means lstat behavior, otherwise stat behavior. bool follow_link = !(flags & AT_SYMLINK_NOFOLLOW); // Path resolution needs to know if it's for lstat or stat context for // the final component. resolve_gkfs_path's `resolve_last_link` // parameter handles this. switch(resolve_gkfs_path(dfd, path, resolved, flags, follow_link)) { case PathStatus::Internal: return gkfs::syscall::gkfs_stat(resolved, buf, follow_link); case PathStatus::Error: return -1; // errno set default: // External break; } } return -1; } static inline int lol_openat(int dfd, const char* path, mode_t mode, int flags) { if(CTX->interception_enabled()) { std::string resolved; // AT_SYMLINK_NOFOLLOW means lstat behavior, otherwise stat behavior. bool follow_link = !(flags & AT_SYMLINK_NOFOLLOW); // Path resolution needs to know if it's for lstat or stat context for // the final component. resolve_gkfs_path's `resolve_last_link` // parameter handles this. switch(resolve_gkfs_path(dfd, path, resolved, flags, follow_link)) { case PathStatus::Internal: fuse_log(FUSE_LOG_DEBUG, "lol_openat internal\n"); return gkfs::syscall::gkfs_open(resolved, mode, follow_link); case PathStatus::Error: return -1; // errno set default: // External break; } } return -1; } /* * Creates files on the underlying file system in response to a FUSE_MKNOD * operation Loading @@ -294,12 +115,12 @@ mknod_wrapper(int dirfd, const char* path, const char* link, int mode, int res = -1; if(S_ISREG(mode)) { res = lol_openat(dirfd, path, mode, O_CREAT | O_EXCL | O_WRONLY); // res = lol_openat(dirfd, path, mode, O_CREAT | O_EXCL | O_WRONLY); fuse_log(FUSE_LOG_DEBUG, "lol_openat internal %s\n", res); if(res >= 0) res = gkfs::syscall::gkfs_close(res); } else if(S_ISDIR(mode)) { GKFS_PATH_OPERATION(create, dirfd, path, mode | S_IFDIR) // GKFS_PATH_OPERATION(create, dirfd, path, mode | S_IFDIR) // res = gkfs::syscall::gkfs_create(resolved, mode | S_IFDIR); // res = mkdirat(dirfd, path, mode); } else if(S_ISLNK(mode) && link != NULL) { Loading
src/client/fuse/fuse_client.cpp +83 −33 Original line number Diff line number Diff line Loading @@ -37,14 +37,7 @@ SPDX-License-Identifier: LGPL-3.0-or-later */ #include "client/env.hpp" #include "common/env_util.hpp" #include <cerrno> #include <client/fuse/fuse_client.hpp> #include <cstdio> #include <cstdlib> #include <dirent.h> #include <iostream> struct lo_inode { struct lo_inode* next; /* protected by lo->mutex */ Loading Loading @@ -225,33 +218,34 @@ getattr_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { static void setattr_handler(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, struct fuse_file_info* fi) { fuse_log(FUSE_LOG_DEBUG, "setattr handler \n"); fuse_log(FUSE_LOG_DEBUG, "setattr handler ino %u\n", ino); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); return; } // TODO how to change attr? int rc = 0; if(rc) { fuse_reply_err(req, rc); if(to_set & FUSE_SET_ATTR_SIZE) { off_t new_size = attr->st_size; int res = gkfs::syscall::gkfs_truncate(inode->path, new_size); if(res < 0) { fuse_log(FUSE_LOG_DEBUG, "setattr truncate failed on %s\n", inode->path.c_str()); fuse_reply_err(req, EIO); return; } // TODO thats not in gkfs!!! // Update cached stat so users see the new size inode->st.st_size = new_size; } if(to_set & FUSE_SET_ATTR_ATIME) inode->st.st_atim = attr->st_atim; inode->st.st_blksize = attr->st_blksize; inode->st.st_blocks = attr->st_blocks; inode->st.st_ctim = attr->st_ctim; inode->st.st_dev = attr->st_dev; inode->st.st_gid = attr->st_gid; inode->st.st_ino = attr->st_ino; inode->st.st_mode = attr->st_mode; if(to_set & FUSE_SET_ATTR_MTIME) inode->st.st_mtim = attr->st_mtim; inode->st.st_nlink = attr->st_nlink; inode->st.st_rdev = attr->st_rdev; inode->st.st_size = attr->st_size; inode->st.st_uid = attr->st_uid; // TODO because we cannot save the attributes in gekko, we just return the // buffered results of stat fuse_reply_attr(req, &inode->st, 1.0); return; } static void Loading Loading @@ -353,6 +347,7 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, return; } fuse_ino_t ino = alloc_inode(path); fuse_log(FUSE_LOG_DEBUG, "create new inode ino %i\n", ino); ino_map[ino].st = st; fuse_entry_param e = {}; e.ino = ino; Loading Loading @@ -465,15 +460,69 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, static void init_handler(void* userdata, struct fuse_conn_info* conn) { fuse_log(FUSE_LOG_DEBUG, "init handler \n"); // userdata is GekkoFuse* if passed // optional: adjust conn->max_write, enable writeback etc. } static void destroy_handler(void* userdata) { fuse_log(FUSE_LOG_DEBUG, "destroy handler \n"); // userdata is GekkoFuse* if passed // optional: adjust conn->max_write, enable writeback etc. } static void release_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { // TODO decrease inode count fuse_log(FUSE_LOG_DEBUG, "release handler \n"); auto* inode = get_inode(ino); if(!inode) { fuse_log(FUSE_LOG_DEBUG, "release here \n"); fuse_reply_err(req, ENOENT); return; } int lc = gkfs::syscall::gkfs_close(fi->fh); if(lc < 0) { fuse_log(FUSE_LOG_DEBUG, "release there \n"); fuse_reply_err(req, 1); return; } fuse_log(FUSE_LOG_DEBUG, "release success \n"); fuse_reply_err(req, 0); } static void forget_handler(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { // TODO remove inode at some point if count == 0 fuse_log(FUSE_LOG_DEBUG, "forget handler \n"); fuse_reply_err(req, 0); } static void flush_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { fuse_log(FUSE_LOG_DEBUG, "flush handler \n"); auto* inode = get_inode(ino); if(!inode) { fuse_log(FUSE_LOG_DEBUG, "flush here \n"); fuse_reply_err(req, ENOENT); return; } int lc = gkfs::syscall::gkfs_fsync(fi->fh); if(lc < 0) { fuse_log(FUSE_LOG_DEBUG, "flush there \n"); fuse_reply_err(req, 1); return; } fuse_log(FUSE_LOG_DEBUG, "flush success \n"); fuse_reply_err(req, 0); } static const struct fuse_lowlevel_ops lo_oper = { .init = init_handler, // lo_init, //.destroy = lo_destroy, .init = init_handler, .destroy = destroy_handler, .lookup = lookup_handler, //.forget = lo_forget, .forget = forget_handler, .getattr = getattr_handler, .setattr = setattr_handler, //.readlink = lo_readlink, Loading @@ -487,8 +536,8 @@ static const struct fuse_lowlevel_ops lo_oper = { .open = open_handler, .read = read_handler, .write = write_handler, //.flush = lo_flush, //.release = lo_release, .flush = flush_handler, .release = release_handler, //.fsync = lo_fsync, .opendir = opendir_handler, .readdir = readdir_handler, Loading Loading @@ -532,7 +581,7 @@ main(int argc, char* argv[]) { int ret = -1; /* Don't mask creation mode, kernel already did that */ umask(0); umask(0); // TODO do we need this and why? pthread_mutex_init(&lo.mutex, NULL); lo.root.next = lo.root.prev = &lo.root; Loading Loading @@ -634,6 +683,7 @@ main(int argc, char* argv[]) { exit(1); } // TODO do we still want this? what do we want? fuse_log(FUSE_LOG_DEBUG, "hier 1\n"); lo.root.fd = gkfs::syscall::gkfs_open(lo.source, 755, O_PATH); if(lo.root.fd == -1) { Loading
tests/integration/CMakeLists.txt +9 −0 Original line number Diff line number Diff line Loading @@ -164,6 +164,15 @@ if (GKFS_RENAME_SUPPORT) ) endif () if (GKFS_BUILD_FUSE) gkfs_add_python_test( NAME test_fuse_client PYTHON_VERSION 3.6 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/integration SOURCE fuse/ ) endif () if (GKFS_INSTALL_TESTS) install(DIRECTORY harness DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/integration Loading
tests/integration/conftest.py +16 −4 Original line number Diff line number Diff line Loading @@ -34,7 +34,7 @@ from pathlib import Path from harness.logger import logger, initialize_logging, finalize_logging from harness.cli import add_cli_options, set_default_log_formatter from harness.workspace import Workspace, FileCreator from harness.gkfs import Daemon, Client, ClientLibc, Proxy, ShellClient, ShellClientLibc, FwdDaemon, FwdClient, ShellFwdClient, FwdDaemonCreator, FwdClientCreator from harness.gkfs import Daemon, Client, ClientLibc, Proxy, ShellClient, ShellClientLibc, FwdDaemon, FwdClient, ShellFwdClient, FwdDaemonCreator, FwdClientCreator, FuseClient from harness.reporter import report_test_status, report_test_headline, report_assertion_pass def pytest_configure(config): Loading Loading @@ -226,3 +226,15 @@ def gkfwd_client_factory(test_workspace): """ return FwdClientCreator(test_workspace) @pytest.fixture def fuse_client(test_workspace): """ Sets up a gekkofs fuse client environment so that operations (system calls, library calls, ...) can be requested from a co-running daemon. """ fuse_client = FuseClient(test_workspace) yield fuse_client.run() fuse_client.shutdown()