Commit 054fbcfe authored by Julius Athenstaedt's avatar Julius Athenstaedt Committed by Ramon Nou
Browse files

cleanup and ci setup for fuse

parent dd092351
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -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
+5 −184
Original line number Diff line number Diff line
@@ -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
@@ -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>
@@ -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
@@ -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
@@ -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) {
+83 −33
Original line number Diff line number Diff line
@@ -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 */
@@ -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
@@ -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;
@@ -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,
@@ -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,
@@ -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;
@@ -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) {
+9 −0
Original line number Diff line number Diff line
@@ -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
+16 −4
Original line number Diff line number Diff line
@@ -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):
@@ -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