Loading include/client/fuse/fuse_client.hpp +1 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ struct __dirstream { #include <unordered_map> #include <string> #include <mutex> #include <cstdlib> #ifdef __FreeBSD__ #include <sys/socket.h> Loading src/client/fuse/fuse_client.cpp +109 −16 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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()); Loading @@ -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; } Loading Loading @@ -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 Loading Loading @@ -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"); Loading Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -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 Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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; } Loading src/client/gkfs_functions.cpp +5 −1 Original line number Diff line number Diff line Loading @@ -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)) { Loading Loading @@ -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) { Loading tests/integration/conftest.template +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 @@ -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() tests/integration/fuse/test_basic_operations.py +8 −1 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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 Loading @@ -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" Loading
include/client/fuse/fuse_client.hpp +1 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ struct __dirstream { #include <unordered_map> #include <string> #include <mutex> #include <cstdlib> #ifdef __FreeBSD__ #include <sys/socket.h> Loading
src/client/fuse/fuse_client.cpp +109 −16 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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()); Loading @@ -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; } Loading Loading @@ -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 Loading Loading @@ -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"); Loading Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -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 Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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; } Loading
src/client/gkfs_functions.cpp +5 −1 Original line number Diff line number Diff line Loading @@ -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)) { Loading Loading @@ -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) { Loading
tests/integration/conftest.template +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 @@ -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()
tests/integration/fuse/test_basic_operations.py +8 −1 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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 Loading @@ -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"