Commit 846377f0 authored by Julius Athenstaedt's avatar Julius Athenstaedt
Browse files

pointertrick and some refactoring

parent 7f24311e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -248,7 +248,7 @@ gkfs_define_option(
    DEFAULT_VALUE ON
)

# use old resolve function
# build fuse client for gekkofs
gkfs_define_option(
    GKFS_BUILD_FUSE
    HELP_TEXT "Build FUSE client"
+21 −9
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ extern "C" {
#endif

// GekkoFS Project Headers
#include <config.hpp>
#include <common/path_util.hpp>
#include <client/hooks.hpp>
#include <client/logging.hpp> // Assumed to provide LOG and CTX
@@ -87,10 +88,28 @@ extern "C" {
#include <client/preload_context.hpp>
#include <client/user_functions.hpp>

// TODO do we really need the stat here? no i dont think so
// Pointer trick to save a hash table and the lookup for inodes.
// Only works if the fuse_ino_t has the size of uintptr_t.
// For older compilers, the else case fails during compile time if the size does
// not match. From libfuse passthrough example
#if defined(__GNUC__) &&                                                       \
        (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) &&              \
        !defined __cplusplus
_Static_assert(!gkfs::config::fuse::pointertrick ||
                       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
        : (!gkfs::config::fuse::pointertrick ||
                           (sizeof(fuse_ino_t) >= sizeof(uintptr_t))
                   ? 1
                   : -1);
};
#endif

struct Inode {
    std::string path;
    struct stat st;
    uint64_t lookup_count;
};

@@ -115,11 +134,4 @@ struct u_data {
    int timeout_set;
};

struct GkfsDir { // Hypothetical structure that might be used if DIR is cast
    int fd;
    long int tell_pos; // for telldir/seekdir
    char* path;
    // other members libc DIR might have
};

#endif // GKFS_CLIENT_FUSE_CONTEXT_HPP
+10 −0
Original line number Diff line number Diff line
@@ -183,6 +183,16 @@ constexpr auto max_stats = 1000000; ///< How many stats will be stored
constexpr auto prometheus_gateway = "127.0.0.1:9091";
} // namespace stats

namespace fuse {

// use pointertrick for fuse client
// only works if sizeof(fuse_ino_t) >= sizeof(uintptr_t)
constexpr auto pointertrick = false;
// on read and write, check existence of the inode for better errors
// not needed
constexpr auto check_inode = true;
} // namespace fuse

} // namespace gkfs::config

#endif // GEKKOFS_CONFIG_HPP
+83 −64
Original line number Diff line number Diff line
@@ -61,8 +61,14 @@ static const std::string fifo_path = "/tmp/gekkofs_fifos/";
static fuse_ino_t
alloc_inode(const std::string& path) {
    std::lock_guard<std::mutex> lk(ino_mutex);
    fuse_ino_t ino = next_ino++;
    ino_map[ino] = {path, {}, 1};
    fuse_ino_t ino;
    if(gkfs::config::fuse::pointertrick) {
        Inode* inode = new Inode{path, 1};
        ino = (fuse_ino_t) inode;
    } else {
        ino = next_ino++;
        ino_map[ino] = {path, 1};
    }
    path_map[path] = ino;
    return ino;
}
@@ -70,9 +76,29 @@ alloc_inode(const std::string& path) {
static Inode*
get_inode(fuse_ino_t ino) {
    std::lock_guard<std::mutex> lk(ino_mutex);
    if(gkfs::config::fuse::pointertrick) {
        if(ino == FUSE_ROOT_ID) {
            return &ino_map[FUSE_ROOT_ID];
        }
        return (Inode*) ino;
    } else {
        auto it = ino_map.find(ino);
        return it != ino_map.end() ? &it->second : nullptr;
    }
}

static void
remove_inode_by_path(const std::string path) {
    auto it_src = path_map.find(path);
    if(it_src != path_map.end()) {
        path_map.erase(it_src);
        if(gkfs::config::fuse::pointertrick) {
            delete get_inode(it_src->second);
        } else {
            ino_map.erase(it_src->second);
        }
    }
}

static struct u_data*
udata(fuse_req_t req) {
@@ -196,23 +222,26 @@ lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) {
        }
    }

    struct stat st;
    int rc = gkfs::syscall::gkfs_stat(child, &st);
    if(rc < 0) {
        fuse_reply_err(req, ENOENT);
        return;
    }

    // See if we already have this path
    auto it = path_map.find(child);
    fuse_ino_t ino;
    if(it != path_map.end()) {
        ino = it->second;
        ino_map[ino].lookup_count++;
        auto inode = get_inode(ino);
        if(inode) {
            inode->lookup_count++;
        }
    } else {
        ino = alloc_inode(child);
    }

    struct stat st;
    int rc = gkfs::syscall::gkfs_stat(child, &st);
    if(rc < 0) {
        fuse_reply_err(req, ENOENT);
        return;
    }
    ino_map[ino].st = st;
    fuse_entry_param e = {};
    e.ino = ino;
    e.attr = st;
@@ -238,7 +267,6 @@ getattr_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
        fuse_reply_err(req, errno);
        return;
    }
    inode->st = st;
    fuse_reply_attr(req, &st, ud->timeout);
}

@@ -262,18 +290,19 @@ setattr_handler(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set,
            fuse_reply_err(req, EIO);
            return;
        }
        // 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;
    if(to_set & FUSE_SET_ATTR_MTIME)
        inode->st.st_mtim = attr->st_mtim;
    struct stat st;
    int rc = gkfs::syscall::gkfs_stat(inode->path, &st);
    if(rc) {
        DEBUG_INFO(ud, "getattr error %u", rc);
        fuse_reply_err(req, errno);
        return;
    }

    // TODO because we cannot save the attributes in gekko, we just return the
    // buffered results of stat
    fuse_reply_attr(req, &inode->st, ud->timeout);
    fuse_reply_attr(req, &st, ud->timeout);
    return;
}

@@ -293,7 +322,7 @@ open_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
        fuse_reply_err(req, ENOENT);
        return;
    }
    fi->fh = fd; // TODO file handle == file descriptor?
    fi->fh = fd;
    fi->direct_io = ud->direct_io;
    fuse_reply_open(req, fi);
}
@@ -316,11 +345,13 @@ read_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
             struct fuse_file_info* fi) {
    auto* ud = udata(req);
    DEBUG_INFO(ud, "read handler");
    if(gkfs::config::fuse::check_inode) {
        auto* inode = get_inode(ino);
        if(!inode) {
            fuse_reply_err(req, ENOENT);
            return;
        }
    }
    std::vector<char> buf(size);
    int rc = gkfs::syscall::gkfs_pread(fi->fh, buf.data(), size, off);
    if(rc < 0) {
@@ -336,11 +367,13 @@ write_handler(fuse_req_t req, fuse_ino_t ino, const char* buf, size_t size,
              off_t off, struct fuse_file_info* fi) {
    auto* ud = udata(req);
    DEBUG_INFO(ud, "write handler");
    if(gkfs::config::fuse::check_inode) {
        auto* inode = get_inode(ino);
        if(!inode) {
            fuse_reply_err(req, ENOENT);
            return;
        }
    }
    int rc = gkfs::syscall::gkfs_pwrite(fi->fh, buf, size, off);
    if(rc < 0) {
        DEBUG_INFO(ud, "write fail");
@@ -377,7 +410,6 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode,
    }
    fuse_ino_t ino = alloc_inode(path);
    DEBUG_INFO(ud, "create new inode ino %i", ino);
    ino_map[ino].st = st;
    fuse_entry_param e = {};
    e.ino = ino;
    e.attr = st;
@@ -411,11 +443,7 @@ unlink_handler(fuse_req_t req, fuse_ino_t parent, const char* name) {
    }

    int rc = gkfs::syscall::gkfs_remove(path);
    auto it_src = path_map.find(path);
    if(it_src != path_map.end()) {
        path_map.erase(it_src);
        ino_map.erase(it_src->second);
    }
    remove_inode_by_path(path);
    if(rc == -1) {
        fuse_reply_err(req, 1);
        return;
@@ -563,21 +591,19 @@ forget_handler(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) {
    auto* ud = udata(req);
    DEBUG_INFO(ud, "forget handler");

    auto it = ino_map.find(ino);
    if(it == ino_map.end()) {
    Inode* inode = get_inode(ino);
    if(!inode) {
        fuse_reply_none(req);
        return;
    }

    Inode& inode = it->second;
    if(inode.lookup_count > nlookup)
        inode.lookup_count -= nlookup;
    if(inode->lookup_count > nlookup)
        inode->lookup_count -= nlookup;
    else
        inode.lookup_count = 0;
        inode->lookup_count = 0;

    if(inode.lookup_count == 0) { // && inode.open_count == 0
        path_map.erase(inode.path);
        ino_map.erase(it);
    if(inode->lookup_count == 0) { // && inode.open_count == 0
        remove_inode_by_path(inode->path);
        DEBUG_INFO(ud, "reached lookup_count 0");
    }

@@ -589,11 +615,13 @@ static void
flush_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
    auto* ud = udata(req);
    DEBUG_INFO(ud, "flush handler");
    if(gkfs::config::fuse::check_inode) {
        auto* inode = get_inode(ino);
        if(!inode) {
            fuse_reply_err(req, ENOENT);
            return;
        }
    }
    int lc = gkfs::syscall::gkfs_fsync(fi->fh);
    if(lc < 0) {
        fuse_reply_err(req, 1);
@@ -656,7 +684,6 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name,
    }
    fuse_ino_t ino = alloc_inode(path);
    DEBUG_INFO(ud, "create new inode ino %i", ino);
    ino_map[ino].st = st;
    fuse_entry_param e = {};
    e.ino = ino;
    e.attr = st;
@@ -680,11 +707,7 @@ rmdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name) {
        fuse_reply_err(req, errno);
        return;
    }
    auto it_src = path_map.find(path);
    if(it_src != path_map.end()) {
        path_map.erase(it_src);
        ino_map.erase(it_src->second);
    }
    remove_inode_by_path(path);
    fuse_reply_err(req, 0);
}

@@ -811,12 +834,13 @@ rename_handler(fuse_req_t req, fuse_ino_t old_parent, const char* old_name,
        src_ino = it_src->second;
        path_map.erase(it_src);
        path_map[new_path] = src_ino;
        ino_map[src_ino].path = new_path;
    } else {
        src_ino = alloc_inode(new_path);
        path_map[new_path] = src_ino;
        ino_map[src_ino].path = new_path;
        ino_map[src_ino].st = st_src;
    }
    auto* inode = get_inode(src_ino);
    if(inode) {
        inode->path = new_path;
    }

    // If destination existed and was overwritten, detach its mapping
@@ -834,11 +858,6 @@ rename_handler(fuse_req_t req, fuse_ino_t old_parent, const char* old_name,
        }
    }

    // Refresh src inode attributes under its new name
    struct stat st_new{};
    if(gkfs::syscall::gkfs_stat(new_path, &st_new) == 0) {
        ino_map[src_ino].st = st_new;
    }
    fuse_reply_err(req, 0);
#else
    fuse_reply_err(req, ENOTSUP);
@@ -860,6 +879,7 @@ mknod_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode,
    }

    mkdir(fifo_path.c_str(), 0700);
    // TODO name should somehow include the parent path to avoid name collision
    std::string path = fifo_path + "/" + name;

    if(!S_ISFIFO(mode)) {
@@ -913,8 +933,7 @@ init_gekkofs() {
        fuse_log(FUSE_LOG_ERR, "failed to open root\n");
        exit(1);
    }
    ino_map[FUSE_ROOT_ID] = {root_path, {}, 1};
    ino_map[FUSE_ROOT_ID].st = st;
    ino_map[FUSE_ROOT_ID] = {root_path, 1};
    path_map[root_path] = FUSE_ROOT_ID;
    std::cout << "root node allocated" << std::endl;
}