Commit aec76d22 authored by sevenuz's avatar sevenuz Committed by Julius Athenstaedt
Browse files

symlink support, fsync, tmpfile (not supported though)

parent 83e5b825
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ struct __dirstream {
#include <unordered_map>
#include <string>
#include <mutex>
#include <cstdlib>

#ifdef __FreeBSD__
#include <sys/socket.h>
+109 −16
Original line number Diff line number Diff line
@@ -68,6 +68,9 @@ udata(fuse_req_t req) {

static std::string
get_path(const Inode* inode, const char* name) {
    if (name[0] == '/') {
        return std::string(name);
    }
    if(inode->path == "/") {
        return inode->path + name;
    } else {
@@ -145,8 +148,6 @@ lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) {
        return;
    }
    std::string child = get_path(parent_inode, name);
    fuse_log(FUSE_LOG_DEBUG, "lookup parent %s name %s\n",
             parent_inode->path.c_str(), name);
    fuse_log(FUSE_LOG_DEBUG, "lookup %s\n", child.c_str());


@@ -163,7 +164,6 @@ 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_log(FUSE_LOG_DEBUG, "that error 2 \n", parent);
        fuse_reply_err(req, ENOENT);
        return;
    }
@@ -354,6 +354,15 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode,
    fuse_reply_create(req, &e, fi);
}

// create a normal file with generated name?
// fake tmp file because it will be listed in the directory it is created in
static void
tmpfile_handler(fuse_req_t req, fuse_ino_t parent, mode_t mode,
                struct fuse_file_info* fi) {
    fuse_log(FUSE_LOG_DEBUG, "tmpfile handler \n");
    fuse_reply_err(req, ENOTSUP);
}

/// TODO normally, the file should only be removed if the lookup count is zero,
/// problem?
static void
@@ -555,6 +564,13 @@ flush_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
    fuse_reply_err(req, 0);
}

static void
fsync_handler(fuse_req_t req, fuse_ino_t ino, int datasync,
              struct fuse_file_info* fi) {
    // on datasync > 0, metadata should not be flushed
    flush_handler(req, ino, fi);
}

static void
access_handler(fuse_req_t req, fuse_ino_t ino, int mask) {
    fuse_log(FUSE_LOG_DEBUG, "access handler \n");
@@ -622,6 +638,81 @@ rmdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name) {
    fuse_reply_err(req, 0);
}

static void
readlink_handler(fuse_req_t req, fuse_ino_t ino) {
#ifdef HAS_SYMLINKS
    auto* inode = get_inode(ino);
    if(!inode) {
        fuse_reply_err(req, ENOENT);
        return;
    }
    char link[PATH_MAX];
    int rc = gkfs::syscall::gkfs_readlink(inode->path, link, PATH_MAX);
    if(rc == -1) {
        fuse_reply_err(req, 1);
        return;
    }
    link[rc] = '\0';
    fuse_reply_readlink(req, link);
#else
    fuse_reply_err(req, ENOTSUP);
#endif
}

static void
symlink_handler(fuse_req_t req, const char* linkname, fuse_ino_t parent,
                const char* name) {
#ifdef HAS_SYMLINKS
    fuse_log(FUSE_LOG_DEBUG, "symlink handler linkname %s name %s\n", linkname,
             name);
    auto* ud = udata(req);
    auto* parent_inode = get_inode(parent);
    if(!parent_inode) {
        fuse_log(FUSE_LOG_DEBUG, "symlink parent inode ino %i\n", parent);
        fuse_reply_err(req, ENOENT);
        return;
    }

    std::string path = get_path(parent_inode, name);
    std::string target = get_path(parent_inode, linkname);

    if (target.rfind(ud->source, 0) == 0) {
        // starts with mount path
        target = get_path(parent_inode, target.substr(strlen(ud->source)).c_str());
    }

    fuse_log(FUSE_LOG_DEBUG, "mk symlink path %s target %s\n", path.c_str(),
             target.c_str());
    int rc = gkfs::syscall::gkfs_mk_symlink(path, target);
    if(rc < 0) {
        fuse_reply_err(req, 1);
        return;
    }

    // Stat the new symlink so we can reply with entry info
    struct stat st;
    if(gkfs::syscall::gkfs_stat(path, &st) < 0) {
        fuse_log(FUSE_LOG_DEBUG, "stat failed\n");
        fuse_reply_err(req, errno);
        return;
    }
    fuse_log(FUSE_LOG_DEBUG, "stat mode %i, iflink %i\n", st.st_mode, S_IFLNK);
    // TODO this meta is not saved and therefore on restart gone
    // this shows the link on ls -l
    st.st_mode = S_IFLNK | 0777; // mark as symlink + full perms
    fuse_entry_param e = {};
    e.ino = alloc_inode(path);
    e.attr = st;
    st.st_size = strlen(target.c_str());
    e.attr_timeout = ud->timeout;
    e.entry_timeout = ud->timeout;
    fuse_reply_entry(req, &e);
#else
    fuse_reply_err(req, ENOTSUP);
#endif
}


static void
init_gekkofs() {
    // TODO how to handle mount point
@@ -660,14 +751,14 @@ init_ll_ops(fuse_lowlevel_ops* ops) {
    ops->unlink = unlink_handler;
    ops->forget = forget_handler;
    // ops->forget_multi
    // ops->readlink
    ops->readlink = readlink_handler;
    // ops->mknod
    // ops->symlink
    ops->symlink = symlink_handler;
    // ops->rename
    // ops->link
    ops->flush = flush_handler;
    ops->release = release_handler;
    // ops->fsync
    ops->fsync = fsync_handler;
    // ops->write_buf
    ops->lseek = lseek_handler;

@@ -697,8 +788,9 @@ init_ll_ops(fuse_lowlevel_ops* ops) {
    // misc
    ops->init = init_handler;
    ops->destroy = destroy_handler;
    ops->statfs = nullptr;
    ops->tmpfile = tmpfile_handler;

    // ops->statfs = nullptr;
    // ops->flock
    // ops->getlk
    // ops->setlk
@@ -708,13 +800,13 @@ init_ll_ops(fuse_lowlevel_ops* ops) {
    // ops->retrive_reply
    // ops->fallocate
    // ops->copy_file_range
    // ops->tmpfile
    // ops->statx
}

void
err_cleanup1(fuse_cmdline_opts opts, fuse_args& args) {
err_cleanup1(fuse_cmdline_opts opts, fuse_args& args, struct u_data& ud) {
    free(opts.mountpoint);
    free(ud.source);
    fuse_opt_free_args(&args);
    std::cout << "# Resources released" << std::endl;
}
@@ -757,13 +849,13 @@ main(int argc, char* argv[]) {
        fuse_cmdline_help();
        fuse_lowlevel_help();
        passthrough_ll_help();
        err_cleanup1(opts, args);
        err_cleanup1(opts, args, ud);
        return 0;
    } else if(opts.show_version) {
        printf("FUSE library version %s\n", fuse_pkgversion());
        fuse_lowlevel_version();
        ret = 0;
        err_cleanup1(opts, args);
        err_cleanup1(opts, args, ud);
        return 0;
    }

@@ -771,7 +863,7 @@ main(int argc, char* argv[]) {
        printf("usage: %s [options] <mountpoint>\n", argv[0]);
        printf("       %s --help\n", argv[0]);
        ret = 1;
        err_cleanup1(opts, args);
        err_cleanup1(opts, args, ud);
        return 0;
    }

@@ -799,25 +891,26 @@ main(int argc, char* argv[]) {
    }


    ud.source = strdup(opts.mountpoint);
    init_gekkofs();


    se = fuse_session_new(&args, &ll_ops, sizeof(ll_ops), &ud);
    if(se == nullptr) {
        err_cleanup1(opts, args);
        err_cleanup1(opts, args, ud);
        return 0;
    }

    if(fuse_set_signal_handlers(se) != 0) {
        err_cleanup2(*se);
        err_cleanup1(opts, args);
        err_cleanup1(opts, args, ud);
        return 0;
    }

    if(fuse_session_mount(se, opts.mountpoint) != 0) {
        err_cleanup3(*se);
        err_cleanup2(*se);
        err_cleanup1(opts, args);
        err_cleanup1(opts, args, ud);
        return 0;
    }

+5 −1
Original line number Diff line number Diff line
@@ -1835,6 +1835,10 @@ gkfs_mk_symlink(const std::string& path, const std::string& target_path) {
            errno = ENOTSUP;
            return -1;
        }
    } else {
        // target is not in gekkofs
        errno = ENOENT;
        return -1;
    }

    if(check_parent_dir(path)) {
@@ -1871,7 +1875,7 @@ gkfs_mk_symlink(const std::string& path, const std::string& target_path) {
 * @param path
 * @param buf
 * @param bufsize
 * @return 0 on success or -1 on error
 * @return path size on success or -1 on error
 */
int
gkfs_readlink(const std::string& path, char* buf, int bufsize) {
+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):
@@ -225,3 +225,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()
+8 −1
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ def test_read(gkfs_daemon, fuse_client):

    dir = gkfs_daemon.mountdir / "dir"

    # creation and removal
    sh.bash("-c", "echo baum > " + str(file))
    assert sh.ls(fuse_client.mountdir) == "file\n"
    assert sh.cat(file) == "baum\n"
@@ -68,7 +69,7 @@ def test_read(gkfs_daemon, fuse_client):
    sh.rm(str(file2))
    assert sh.ls(fuse_client.mountdir) == "dir  file\n"

    # lseek test
    # lseek test (TODO doesn't use the lseek handler)
    path = gkfs_daemon.mountdir / "lseek_file"
    with open(path, "wb") as f:
        f.write(b"HelloWorld")  # 10 bytes
@@ -79,3 +80,9 @@ def test_read(gkfs_daemon, fuse_client):
    assert data == b"World"
    os.close(fd)
    os.remove(path)

    # symlink
    assert sh.pwd() == str(dir) + "\n"
    sh.ln("-s", str(file), "link")
    assert sh.ls(str(dir)) == "link\n"
    assert sh.cat("link") == "baum\n"