From d42c4ec0e0702848f3b0b1cdf35d33b4ad6fb382 Mon Sep 17 00:00:00 2001 From: Julius Athenstaedt Date: Tue, 22 Jul 2025 11:33:05 +0200 Subject: [PATCH 01/32] init fuse setup --- CMake/gkfs-options.cmake | 7 +++++++ CMakeLists.txt | 5 +++++ include/client/fuse/fuse_client.hpp | 17 +++++++++++++++++ src/client/CMakeLists.txt | 29 ++++++++++++++++++++++++++--- src/client/fuse/fuse_client.cpp | 6 ++++++ 5 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 include/client/fuse/fuse_client.hpp create mode 100644 src/client/fuse/fuse_client.cpp diff --git a/CMake/gkfs-options.cmake b/CMake/gkfs-options.cmake index 8a03d7c48..b70b99e07 100644 --- a/CMake/gkfs-options.cmake +++ b/CMake/gkfs-options.cmake @@ -248,6 +248,13 @@ gkfs_define_option( DEFAULT_VALUE ON ) +# use old resolve function +gkfs_define_option( + GKFS_BUILD_FUSE + HELP_TEXT "Build FUSE client" + DEFAULT_VALUE ON +) + # use old resolve function gkfs_define_option( GKFS_USE_LEGACY_PATH_RESOLVE diff --git a/CMakeLists.txt b/CMakeLists.txt index 98fc0776b..d930172e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -216,6 +216,11 @@ find_package(Threads REQUIRED) # details transparently find_package(Filesystem REQUIRED) +find_package(PkgConfig REQUIRED) +pkg_check_modules(FUSE3 REQUIRED fuse3) +include_directories(${FUSE3_INCLUDE_DIRS}) +link_directories(${FUSE3_LIBRARY_DIRS}) +add_definitions(${FUSE3_CFLAGS_OTHER}) # Search for 'source-only' dependencies ############################################################################### diff --git a/include/client/fuse/fuse_client.hpp b/include/client/fuse/fuse_client.hpp new file mode 100644 index 000000000..e393574de --- /dev/null +++ b/include/client/fuse/fuse_client.hpp @@ -0,0 +1,17 @@ +#ifndef GKFS_CLIENT_FUSE_CONTEXT_HPP +#define GKFS_CLIENT_FUSE_CONTEXT_HPP + +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#define FUSE_USE_VERSION 31 +#include +} + +#endif //GKFS_CLIENT_FUSE_CONTEXT_HPP diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index a7d0247f5..2ac9c6e03 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -37,7 +37,7 @@ add_library(gkfs_common SHARED) target_sources(gkfs_common PUBLIC path.cpp - PRIVATE + PRIVATE logging.cpp open_file_map.cpp open_dir.cpp @@ -104,10 +104,29 @@ target_sources( hooks.cpp preload.cpp preload_context.cpp - preload_util.cpp + preload_util.cpp ) endif () +if (GKFS_BUILD_FUSE) +add_executable(fuse_client "") +target_sources(fuse_client + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/fuse/fuse_client.cpp + PRIVATE + ${INCLUDE_DIR}/client/fuse/fuse_client.hpp + ) + +target_link_libraries(fuse_client + ${FUSE3_LIBRARIES} + gkfs_user_lib + ) + +target_include_directories(fuse_client + PRIVATE + ${FUSE3_INCLUDE_DIRS} + ) +endif() if (GKFS_BUILD_USER_LIB) target_compile_definitions(gkfs_user_lib PUBLIC BYPASS_SYSCALL ENABLE_USER) @@ -217,9 +236,13 @@ install( ) endif () +if (GKFS_BUILD_FUSE) +install(TARGETS fuse_client RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif () + install( TARGETS gkfs_common LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gkfs -) \ No newline at end of file +) diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp new file mode 100644 index 000000000..affd9d5fa --- /dev/null +++ b/src/client/fuse/fuse_client.cpp @@ -0,0 +1,6 @@ +#include + +int main(int argc, const char* argv[]) { + return 0; +} + -- GitLab From 1b0769a8750430760e867499a58d0043bae07157 Mon Sep 17 00:00:00 2001 From: sevenuz Date: Tue, 22 Jul 2025 13:51:24 +0200 Subject: [PATCH 02/32] passthrough example --- include/client/fuse/fuse_client.hpp | 160 +++- src/client/fuse/fuse_client.cpp | 1324 ++++++++++++++++++++++++++- 2 files changed, 1473 insertions(+), 11 deletions(-) diff --git a/include/client/fuse/fuse_client.hpp b/include/client/fuse/fuse_client.hpp index e393574de..a1f3d2d5b 100644 --- a/include/client/fuse/fuse_client.hpp +++ b/include/client/fuse/fuse_client.hpp @@ -1,17 +1,157 @@ #ifndef GKFS_CLIENT_FUSE_CONTEXT_HPP #define GKFS_CLIENT_FUSE_CONTEXT_HPP -#include -#include -#include -#include -#include -#include -#include - extern "C" { -#define FUSE_USE_VERSION 31 +#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12) #include } -#endif //GKFS_CLIENT_FUSE_CONTEXT_HPP +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 + +/* + * FUSE: Filesystem in Userspace + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE + */ + +#include +#include +#include +#include +#include + +#ifdef __FreeBSD__ +#include +#include +#endif + +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 +} + +/* + * Creates files on the underlying file system in response to a FUSE_MKNOD + * operation + */ +static inline int +mknod_wrapper(int dirfd, const char* path, const char* link, int mode, + dev_t rdev) { + int res; + + if(S_ISREG(mode)) { + res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode); + if(res >= 0) + res = close(res); + } else if(S_ISDIR(mode)) { + res = mkdirat(dirfd, path, mode); + } else if(S_ISLNK(mode) && link != NULL) { + res = symlinkat(link, dirfd, path); + } else if(S_ISFIFO(mode)) { + res = mkfifoat(dirfd, path, mode); +#ifdef __FreeBSD__ + } else if(S_ISSOCK(mode)) { + struct sockaddr_un su; + int fd; + + if(strlen(path) >= sizeof(su.sun_path)) { + errno = ENAMETOOLONG; + return -1; + } + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if(fd >= 0) { + /* + * We must bind the socket to the underlying file + * system to create the socket file, even though + * we'll never listen on this socket. + */ + su.sun_family = AF_UNIX; + strncpy(su.sun_path, path, sizeof(su.sun_path)); + res = bindat(dirfd, fd, (struct sockaddr*) &su, sizeof(su)); + if(res == 0) + close(fd); + } else { + res = -1; + } +#endif + } else { + res = mknodat(dirfd, path, mode, rdev); + } + + return res; +} + +#endif // GKFS_CLIENT_FUSE_CONTEXT_HPP diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index affd9d5fa..86da62d5f 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -1,6 +1,1328 @@ #include -int main(int argc, const char* argv[]) { +struct lo_inode { + struct lo_inode* next; /* protected by lo->mutex */ + struct lo_inode* prev; /* protected by lo->mutex */ + int fd; + ino_t ino; + dev_t dev; + uint64_t refcount; /* protected by lo->mutex */ +}; + +enum { + CACHE_NEVER, + CACHE_NORMAL, + CACHE_ALWAYS, +}; + +struct lo_data { + pthread_mutex_t mutex; + int debug; + int writeback; + int flock; + int xattr; + char* source; + double timeout; + int cache; + int timeout_set; + struct lo_inode root; /* protected by lo->mutex */ +}; + +static const struct fuse_opt lo_opts[] = { + {"writeback", offsetof(struct lo_data, writeback), 1}, + {"no_writeback", offsetof(struct lo_data, writeback), 0}, + {"source=%s", offsetof(struct lo_data, source), 0}, + {"flock", offsetof(struct lo_data, flock), 1}, + {"no_flock", offsetof(struct lo_data, flock), 0}, + {"xattr", offsetof(struct lo_data, xattr), 1}, + {"no_xattr", offsetof(struct lo_data, xattr), 0}, + {"timeout=%lf", offsetof(struct lo_data, timeout), 0}, + {"timeout=", offsetof(struct lo_data, timeout_set), 1}, + {"cache=never", offsetof(struct lo_data, cache), CACHE_NEVER}, + {"cache=auto", offsetof(struct lo_data, cache), CACHE_NORMAL}, + {"cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS}, + + FUSE_OPT_END}; + +static void +passthrough_ll_help(void) { + printf(" -o writeback Enable writeback\n" + " -o no_writeback Disable write back\n" + " -o source=/home/dir Source directory to be mounted\n" + " -o flock Enable flock\n" + " -o no_flock Disable flock\n" + " -o xattr Enable xattr\n" + " -o no_xattr Disable xattr\n" + " -o timeout=1.0 Caching timeout\n" + " -o timeout=0/1 Timeout is set\n" + " -o cache=never Disable cache\n" + " -o cache=auto Auto enable cache\n" + " -o cache=always Cache always\n"); +} + +static struct lo_data* +lo_data(fuse_req_t req) { + return (struct lo_data*) fuse_req_userdata(req); +} + +static struct lo_inode* +lo_inode(fuse_req_t req, fuse_ino_t ino) { + if(ino == FUSE_ROOT_ID) + return &lo_data(req)->root; + else + return (struct lo_inode*) (uintptr_t) ino; +} + +static int +lo_fd(fuse_req_t req, fuse_ino_t ino) { + return lo_inode(req, ino)->fd; +} + +static bool +lo_debug(fuse_req_t req) { + return lo_data(req)->debug != 0; +} + +static void +lo_init(void* userdata, struct fuse_conn_info* conn) { + struct lo_data* lo = (struct lo_data*) userdata; + bool has_flag; + + if(lo->writeback) { + has_flag = fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE); + if(lo->debug && has_flag) + fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); + } + if(lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { + has_flag = fuse_set_feature_flag(conn, FUSE_CAP_FLOCK_LOCKS); + if(lo->debug && has_flag) + fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); + } + + /* Disable the receiving and processing of FUSE_INTERRUPT requests */ + conn->no_interrupt = 1; +} + +static void +lo_destroy(void* userdata) { + struct lo_data* lo = (struct lo_data*) userdata; + + while(lo->root.next != &lo->root) { + struct lo_inode* next = lo->root.next; + lo->root.next = next->next; + close(next->fd); + free(next); + } +} + +static void +lo_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { + int res; + struct stat buf; + struct lo_data* lo = lo_data(req); + int fd = fi ? fi->fh : lo_fd(req, ino); + + (void) fi; + + res = fstatat(fd, "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); + if(res == -1) + return (void) fuse_reply_err(req, errno); + + fuse_reply_attr(req, &buf, lo->timeout); +} + +static void +lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int valid, + struct fuse_file_info* fi) { + int saverr; + char procname[64]; + struct lo_inode* inode = lo_inode(req, ino); + int ifd = inode->fd; + int res; + + if(valid & FUSE_SET_ATTR_MODE) { + if(fi) { + res = fchmod(fi->fh, attr->st_mode); + } else { + sprintf(procname, "/proc/self/fd/%i", ifd); + res = chmod(procname, attr->st_mode); + } + if(res == -1) + goto out_err; + } + if(valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) { + uid_t uid = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t) -1; + gid_t gid = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t) -1; + + res = fchownat(ifd, "", uid, gid, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); + if(res == -1) + goto out_err; + } + if(valid & FUSE_SET_ATTR_SIZE) { + if(fi) { + res = ftruncate(fi->fh, attr->st_size); + } else { + sprintf(procname, "/proc/self/fd/%i", ifd); + res = truncate(procname, attr->st_size); + } + if(res == -1) + goto out_err; + } + if(valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { + struct timespec tv[2]; + + tv[0].tv_sec = 0; + tv[1].tv_sec = 0; + tv[0].tv_nsec = UTIME_OMIT; + tv[1].tv_nsec = UTIME_OMIT; + + if(valid & FUSE_SET_ATTR_ATIME_NOW) + tv[0].tv_nsec = UTIME_NOW; + else if(valid & FUSE_SET_ATTR_ATIME) + tv[0] = attr->st_atim; + + if(valid & FUSE_SET_ATTR_MTIME_NOW) + tv[1].tv_nsec = UTIME_NOW; + else if(valid & FUSE_SET_ATTR_MTIME) + tv[1] = attr->st_mtim; + + if(fi) + res = futimens(fi->fh, tv); + else { + sprintf(procname, "/proc/self/fd/%i", ifd); + res = utimensat(AT_FDCWD, procname, tv, 0); + } + if(res == -1) + goto out_err; + } + + return lo_getattr(req, ino, fi); + +out_err: + saverr = errno; + fuse_reply_err(req, saverr); +} + +static struct lo_inode* +lo_find(struct lo_data* lo, struct stat* st) { + struct lo_inode* p; + struct lo_inode* ret = NULL; + + pthread_mutex_lock(&lo->mutex); + for(p = lo->root.next; p != &lo->root; p = p->next) { + if(p->ino == st->st_ino && p->dev == st->st_dev) { + assert(p->refcount > 0); + ret = p; + ret->refcount++; + break; + } + } + pthread_mutex_unlock(&lo->mutex); + return ret; +} + + +static struct lo_inode* +create_new_inode(int fd, struct fuse_entry_param* e, struct lo_data* lo) { + struct lo_inode* inode = NULL; + struct lo_inode *prev, *next; + + inode = (struct lo_inode*) calloc(1, sizeof(struct lo_inode)); + if(!inode) + return NULL; + + inode->refcount = 1; + inode->fd = fd; + inode->ino = e->attr.st_ino; + inode->dev = e->attr.st_dev; + + pthread_mutex_lock(&lo->mutex); + prev = &lo->root; + next = prev->next; + next->prev = inode; + inode->next = next; + inode->prev = prev; + prev->next = inode; + pthread_mutex_unlock(&lo->mutex); + return inode; +} + +static int +fill_entry_param_new_inode(fuse_req_t req, fuse_ino_t parent, int fd, + struct fuse_entry_param* e) { + int res; + struct lo_data* lo = lo_data(req); + + memset(e, 0, sizeof(*e)); + e->attr_timeout = lo->timeout; + e->entry_timeout = lo->timeout; + + res = fstatat(fd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); + if(res == -1) + return errno; + + e->ino = (uintptr_t) create_new_inode(dup(fd), e, lo); + + if(lo_debug(req)) + fuse_log(FUSE_LOG_DEBUG, " %lli/%lli -> %lli\n", + (unsigned long long) parent, fd, (unsigned long long) e->ino); + return 0; } +static int +lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char* name, + struct fuse_entry_param* e) { + int newfd; + int res; + int saverr; + struct lo_data* lo = lo_data(req); + struct lo_inode* inode; + + memset(e, 0, sizeof(*e)); + e->attr_timeout = lo->timeout; + e->entry_timeout = lo->timeout; + + newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW); + if(newfd == -1) + goto out_err; + + res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); + if(res == -1) + goto out_err; + + inode = lo_find(lo_data(req), &e->attr); + if(inode) { + close(newfd); + newfd = -1; + } else { + inode = create_new_inode(newfd, e, lo); + if(!inode) + goto out_err; + } + e->ino = (uintptr_t) inode; + + if(lo_debug(req)) + fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", + (unsigned long long) parent, name, + (unsigned long long) e->ino); + + return 0; + +out_err: + saverr = errno; + if(newfd != -1) + close(newfd); + return saverr; +} + +static void +lo_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) { + struct fuse_entry_param e; + int err; + + if(lo_debug(req)) + fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", + parent, name); + + err = lo_do_lookup(req, parent, name, &e); + if(err) + fuse_reply_err(req, err); + else + fuse_reply_entry(req, &e); +} + +static void +lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, const char* name, + mode_t mode, dev_t rdev, const char* link) { + int res; + int saverr; + struct lo_inode* dir = lo_inode(req, parent); + struct fuse_entry_param e; + + res = mknod_wrapper(dir->fd, name, link, mode, rdev); + + saverr = errno; + if(res == -1) + goto out; + + saverr = lo_do_lookup(req, parent, name, &e); + if(saverr) + goto out; + + if(lo_debug(req)) + fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", + (unsigned long long) parent, name, (unsigned long long) e.ino); + + fuse_reply_entry(req, &e); + return; + +out: + fuse_reply_err(req, saverr); +} + +static void +lo_mknod(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, + dev_t rdev) { + lo_mknod_symlink(req, parent, name, mode, rdev, NULL); +} + +static void +lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode) { + lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL); +} + +static void +lo_symlink(fuse_req_t req, const char* link, fuse_ino_t parent, + const char* name) { + lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link); +} + +static void +lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, const char* name) { + int res; + struct lo_data* lo = lo_data(req); + struct lo_inode* inode = lo_inode(req, ino); + struct fuse_entry_param e; + char procname[64]; + int saverr; + + memset(&e, 0, sizeof(struct fuse_entry_param)); + e.attr_timeout = lo->timeout; + e.entry_timeout = lo->timeout; + + sprintf(procname, "/proc/self/fd/%i", inode->fd); + res = linkat(AT_FDCWD, procname, lo_fd(req, parent), name, + AT_SYMLINK_FOLLOW); + if(res == -1) + goto out_err; + + res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); + if(res == -1) + goto out_err; + + pthread_mutex_lock(&lo->mutex); + inode->refcount++; + pthread_mutex_unlock(&lo->mutex); + e.ino = (uintptr_t) inode; + + if(lo_debug(req)) + fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", + (unsigned long long) parent, name, (unsigned long long) e.ino); + + fuse_reply_entry(req, &e); + return; + +out_err: + saverr = errno; + fuse_reply_err(req, saverr); +} + +static void +lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char* name) { + int res; + + res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); + + fuse_reply_err(req, res == -1 ? errno : 0); +} + +static void +lo_rename(fuse_req_t req, fuse_ino_t parent, const char* name, + fuse_ino_t newparent, const char* newname, unsigned int flags) { + int res; + + if(flags) { + fuse_reply_err(req, EINVAL); + return; + } + + res = renameat(lo_fd(req, parent), name, lo_fd(req, newparent), newname); + + fuse_reply_err(req, res == -1 ? errno : 0); +} + +static void +lo_unlink(fuse_req_t req, fuse_ino_t parent, const char* name) { + int res; + + res = unlinkat(lo_fd(req, parent), name, 0); + + fuse_reply_err(req, res == -1 ? errno : 0); +} + +static void +unref_inode(struct lo_data* lo, struct lo_inode* inode, uint64_t n) { + if(!inode) + return; + + pthread_mutex_lock(&lo->mutex); + assert(inode->refcount >= n); + inode->refcount -= n; + if(!inode->refcount) { + struct lo_inode *prev, *next; + + prev = inode->prev; + next = inode->next; + next->prev = prev; + prev->next = next; + + pthread_mutex_unlock(&lo->mutex); + close(inode->fd); + free(inode); + + } else { + pthread_mutex_unlock(&lo->mutex); + } +} + +static void +lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { + struct lo_data* lo = lo_data(req); + struct lo_inode* inode = lo_inode(req, ino); + + if(lo_debug(req)) { + fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", + (unsigned long long) ino, (unsigned long long) inode->refcount, + (unsigned long long) nlookup); + } + + unref_inode(lo, inode, nlookup); +} + +static void +lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { + lo_forget_one(req, ino, nlookup); + fuse_reply_none(req); +} + +static void +lo_forget_multi(fuse_req_t req, size_t count, + struct fuse_forget_data* forgets) { + int i; + + for(i = 0; i < count; i++) + lo_forget_one(req, forgets[i].ino, forgets[i].nlookup); + fuse_reply_none(req); +} + +static void +lo_readlink(fuse_req_t req, fuse_ino_t ino) { + char buf[PATH_MAX + 1]; + int res; + + res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf)); + if(res == -1) + return (void) fuse_reply_err(req, errno); + + if(res == sizeof(buf)) + return (void) fuse_reply_err(req, ENAMETOOLONG); + + buf[res] = '\0'; + + fuse_reply_readlink(req, buf); +} + +struct lo_dirp { + DIR* dp; + struct dirent* entry; + off_t offset; +}; + +static struct lo_dirp* +lo_dirp(struct fuse_file_info* fi) { + return (struct lo_dirp*) (uintptr_t) fi->fh; +} + +static void +lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { + int error = ENOMEM; + struct lo_data* lo = lo_data(req); + struct lo_dirp* d; + int fd = -1; + + d = (struct lo_dirp*) calloc(1, sizeof(struct lo_dirp)); + if(d == NULL) + goto out_err; + + fd = openat(lo_fd(req, ino), ".", O_RDONLY); + if(fd == -1) + goto out_errno; + + d->dp = fdopendir(fd); + if(d->dp == NULL) + goto out_errno; + + d->offset = 0; + d->entry = NULL; + + fi->fh = (uintptr_t) d; + if(lo->cache != CACHE_NEVER) + fi->cache_readdir = 1; + if(lo->cache == CACHE_ALWAYS) + fi->keep_cache = 1; + fuse_reply_open(req, fi); + return; + +out_errno: + error = errno; +out_err: + if(d) { + if(fd != -1) + close(fd); + free(d); + } + fuse_reply_err(req, error); +} + +static int +is_dot_or_dotdot(const char* name) { + return name[0] == '.' && + (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); +} + +static void +lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, + struct fuse_file_info* fi, int plus) { + struct lo_dirp* d = lo_dirp(fi); + char* buf; + char* p; + size_t rem = size; + int err; + + (void) ino; + + buf = (char*) calloc(1, size); + if(!buf) { + err = ENOMEM; + goto error; + } + p = buf; + + if(offset != d->offset) { + seekdir(d->dp, offset); + d->entry = NULL; + d->offset = offset; + } + while(1) { + size_t entsize; + off_t nextoff; + const char* name; + + if(!d->entry) { + errno = 0; + d->entry = readdir(d->dp); + if(!d->entry) { + if(errno) { // Error + err = errno; + goto error; + } else { // End of stream + break; + } + } + } + nextoff = d->entry->d_off; + name = d->entry->d_name; + fuse_ino_t entry_ino = 0; + if(plus) { + struct fuse_entry_param e; + if(is_dot_or_dotdot(name)) { + struct fuse_entry_param e = {}; + e.attr.st_ino = d->entry->d_ino; + e.attr.st_mode = static_cast<__mode_t>(d->entry->d_type << 12); + } else { + err = lo_do_lookup(req, ino, name, &e); + if(err) + goto error; + entry_ino = e.ino; + } + + entsize = fuse_add_direntry_plus(req, p, rem, name, &e, nextoff); + } else { + struct stat st = { + .st_ino = d->entry->d_ino, + .st_mode = static_cast<__mode_t>(d->entry->d_type << 12), + }; + entsize = fuse_add_direntry(req, p, rem, name, &st, nextoff); + } + if(entsize > rem) { + if(entry_ino != 0) + lo_forget_one(req, entry_ino, 1); + break; + } + + p += entsize; + rem -= entsize; + + d->entry = NULL; + d->offset = nextoff; + } + + err = 0; +error: + // If there's an error, we can only signal it if we haven't stored + // any entries yet - otherwise we'd end up with wrong lookup + // counts for the entries that are already in the buffer. So we + // return what we've collected until that point. + if(err && rem == size) + fuse_reply_err(req, err); + else + fuse_reply_buf(req, buf, size - rem); + free(buf); +} + +static void +lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, + struct fuse_file_info* fi) { + lo_do_readdir(req, ino, size, offset, fi, 0); +} + +static void +lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, + struct fuse_file_info* fi) { + lo_do_readdir(req, ino, size, offset, fi, 1); +} + +static void +lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { + struct lo_dirp* d = lo_dirp(fi); + (void) ino; + closedir(d->dp); + free(d); + fuse_reply_err(req, 0); +} + +static void +lo_tmpfile(fuse_req_t req, fuse_ino_t parent, mode_t mode, + struct fuse_file_info* fi) { + int fd; + struct lo_data* lo = lo_data(req); + struct fuse_entry_param e; + int err; + + if(lo_debug(req)) + fuse_log(FUSE_LOG_DEBUG, "lo_tmpfile(parent=%" PRIu64 ")\n", parent); + + fd = openat(lo_fd(req, parent), ".", (fi->flags | O_TMPFILE) & ~O_NOFOLLOW, + mode); + if(fd == -1) + return (void) fuse_reply_err(req, errno); + + fi->fh = fd; + if(lo->cache == CACHE_NEVER) + fi->direct_io = 1; + else if(lo->cache == CACHE_ALWAYS) + fi->keep_cache = 1; + + /* parallel_direct_writes feature depends on direct_io features. + To make parallel_direct_writes valid, need set fi->direct_io + in current function. */ + fi->parallel_direct_writes = 1; + + err = fill_entry_param_new_inode(req, parent, fd, &e); + if(err) + fuse_reply_err(req, err); + else + fuse_reply_create(req, &e, fi); +} + +static void +lo_create(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, + struct fuse_file_info* fi) { + int fd; + struct lo_data* lo = lo_data(req); + struct fuse_entry_param e; + int err; + + if(lo_debug(req)) + fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", + parent, name); + + fd = openat(lo_fd(req, parent), name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, + mode); + if(fd == -1) + return (void) fuse_reply_err(req, errno); + + fi->fh = fd; + if(lo->cache == CACHE_NEVER) + fi->direct_io = 1; + else if(lo->cache == CACHE_ALWAYS) + fi->keep_cache = 1; + + /* parallel_direct_writes feature depends on direct_io features. + To make parallel_direct_writes valid, need set fi->direct_io + in current function. */ + fi->parallel_direct_writes = 1; + + err = lo_do_lookup(req, parent, name, &e); + if(err) + fuse_reply_err(req, err); + else + fuse_reply_create(req, &e, fi); +} + +static void +lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info* fi) { + int res; + int fd = dirfd(lo_dirp(fi)->dp); + (void) ino; + if(datasync) + res = fdatasync(fd); + else + res = fsync(fd); + fuse_reply_err(req, res == -1 ? errno : 0); +} + +static void +lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { + int fd; + char buf[64]; + struct lo_data* lo = lo_data(req); + + if(lo_debug(req)) + fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ino, + fi->flags); + + /* With writeback cache, kernel may send read requests even + when userspace opened write-only */ + if(lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) { + fi->flags &= ~O_ACCMODE; + fi->flags |= O_RDWR; + } + + /* With writeback cache, O_APPEND is handled by the kernel. + This breaks atomicity (since the file may change in the + underlying filesystem, so that the kernel's idea of the + end of the file isn't accurate anymore). In this example, + we just accept that. A more rigorous filesystem may want + to return an error here */ + if(lo->writeback && (fi->flags & O_APPEND)) + fi->flags &= ~O_APPEND; + + sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino)); + fd = open(buf, fi->flags & ~O_NOFOLLOW); + if(fd == -1) + return (void) fuse_reply_err(req, errno); + + fi->fh = fd; + if(lo->cache == CACHE_NEVER) + fi->direct_io = 1; + else if(lo->cache == CACHE_ALWAYS) + fi->keep_cache = 1; + + /* Enable direct_io when open has flags O_DIRECT to enjoy the feature + parallel_direct_writes (i.e., to get a shared lock, not exclusive lock, +for writes to the same file in the kernel). */ + if(fi->flags & O_DIRECT) + fi->direct_io = 1; + + /* parallel_direct_writes feature depends on direct_io features. + To make parallel_direct_writes valid, need set fi->direct_io + in current function. */ + fi->parallel_direct_writes = 1; + + fuse_reply_open(req, fi); +} + +static void +lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { + (void) ino; + + close(fi->fh); + fuse_reply_err(req, 0); +} + +static void +lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { + int res; + (void) ino; + res = close(dup(fi->fh)); + fuse_reply_err(req, res == -1 ? errno : 0); +} + +static void +lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info* fi) { + int res; + (void) ino; + if(datasync) + res = fdatasync(fi->fh); + else + res = fsync(fi->fh); + fuse_reply_err(req, res == -1 ? errno : 0); +} + +static void +lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, + struct fuse_file_info* fi) { + struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size); + + if(lo_debug(req)) + fuse_log(FUSE_LOG_DEBUG, + "lo_read(ino=%" PRIu64 ", size=%zd, " + "off=%lu)\n", + ino, size, (unsigned long) offset); + + buf.buf[0].flags = (enum fuse_buf_flags)(FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK); + buf.buf[0].fd = fi->fh; + buf.buf[0].pos = offset; + + fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE); +} + +static void +lo_write_buf(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec* in_buf, + off_t off, struct fuse_file_info* fi) { + (void) ino; + ssize_t res; + struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); + + out_buf.buf[0].flags = + (enum fuse_buf_flags)(FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK); + out_buf.buf[0].fd = fi->fh; + out_buf.buf[0].pos = off; + + if(lo_debug(req)) + fuse_log(FUSE_LOG_DEBUG, + "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n", ino, + out_buf.buf[0].size, (unsigned long) off); + + res = (ssize_t) fuse_buf_copy(&out_buf, in_buf, + (enum fuse_buf_copy_flags) 0); + if(res < 0) + fuse_reply_err(req, -res); + else + fuse_reply_write(req, (size_t) res); +} + +static void +lo_statfs(fuse_req_t req, fuse_ino_t ino) { + int res; + struct statvfs stbuf; + + res = fstatvfs(lo_fd(req, ino), &stbuf); + if(res == -1) + fuse_reply_err(req, errno); + else + fuse_reply_statfs(req, &stbuf); +} + +static void +lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, + off_t length, struct fuse_file_info* fi) { + int err; + (void) ino; + + err = -do_fallocate(fi->fh, mode, offset, length); + + fuse_reply_err(req, err); +} + +static void +lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi, int op) { + int res; + (void) ino; + + res = flock(fi->fh, op); + + fuse_reply_err(req, res == -1 ? errno : 0); +} + +static void +lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char* name, size_t size) { + char* value = NULL; + char procname[64]; + struct lo_inode* inode = lo_inode(req, ino); + ssize_t ret; + int saverr; + + saverr = ENOSYS; + if(!lo_data(req)->xattr) + goto out; + + if(lo_debug(req)) { + fuse_log(FUSE_LOG_DEBUG, + "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", ino, name, + size); + } + + sprintf(procname, "/proc/self/fd/%i", inode->fd); + + if(size) { + value = (char*) malloc(size); + if(!value) + goto out_err; + + ret = getxattr(procname, name, value, size); + if(ret == -1) + goto out_err; + saverr = 0; + if(ret == 0) + goto out; + + fuse_reply_buf(req, value, ret); + } else { + ret = getxattr(procname, name, NULL, 0); + if(ret == -1) + goto out_err; + + fuse_reply_xattr(req, ret); + } +out_free: + free(value); + return; + +out_err: + saverr = errno; +out: + fuse_reply_err(req, saverr); + goto out_free; +} + +static void +lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) { + char* value = NULL; + char procname[64]; + struct lo_inode* inode = lo_inode(req, ino); + ssize_t ret; + int saverr; + + saverr = ENOSYS; + if(!lo_data(req)->xattr) + goto out; + + if(lo_debug(req)) { + fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n", + ino, size); + } + + sprintf(procname, "/proc/self/fd/%i", inode->fd); + + if(size) { + value = (char*) malloc(size); + if(!value) + goto out_err; + + ret = listxattr(procname, value, size); + if(ret == -1) + goto out_err; + saverr = 0; + if(ret == 0) + goto out; + + fuse_reply_buf(req, value, ret); + } else { + ret = listxattr(procname, NULL, 0); + if(ret == -1) + goto out_err; + + fuse_reply_xattr(req, ret); + } +out_free: + free(value); + return; + +out_err: + saverr = errno; +out: + fuse_reply_err(req, saverr); + goto out_free; +} + +static void +lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char* name, const char* value, + size_t size, int flags) { + char procname[64]; + struct lo_inode* inode = lo_inode(req, ino); + ssize_t ret; + int saverr; + + saverr = ENOSYS; + if(!lo_data(req)->xattr) + goto out; + + if(lo_debug(req)) { + fuse_log(FUSE_LOG_DEBUG, + "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n", + ino, name, value, size); + } + + sprintf(procname, "/proc/self/fd/%i", inode->fd); + + ret = setxattr(procname, name, value, size, flags); + saverr = ret == -1 ? errno : 0; + +out: + fuse_reply_err(req, saverr); +} + +static void +lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char* name) { + char procname[64]; + struct lo_inode* inode = lo_inode(req, ino); + ssize_t ret; + int saverr; + + saverr = ENOSYS; + if(!lo_data(req)->xattr) + goto out; + + if(lo_debug(req)) { + fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", + ino, name); + } + + sprintf(procname, "/proc/self/fd/%i", inode->fd); + + ret = removexattr(procname, name); + saverr = ret == -1 ? errno : 0; + +out: + fuse_reply_err(req, saverr); +} + +#ifdef HAVE_COPY_FILE_RANGE +static void +lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, + struct fuse_file_info* fi_in, fuse_ino_t ino_out, + off_t off_out, struct fuse_file_info* fi_out, size_t len, + int flags) { + ssize_t res; + + if(lo_debug(req)) + fuse_log(FUSE_LOG_DEBUG, + "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, " + "off=%lu, ino=%" PRIu64 "/fd=%lu, " + "off=%lu, size=%zd, flags=0x%x)\n", + ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out, len, + flags); + + res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len, flags); + if(res < 0) + fuse_reply_err(req, errno); + else + fuse_reply_write(req, res); +} +#endif + +static void +lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, + struct fuse_file_info* fi) { + off_t res; + + (void) ino; + res = lseek(fi->fh, off, whence); + if(res != -1) + fuse_reply_lseek(req, res); + else + fuse_reply_err(req, errno); +} + +#ifdef HAVE_STATX +static void +lo_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask, + struct fuse_file_info* fi) { + struct lo_data* lo = lo_data(req); + struct statx buf; + int res; + int fd; + + if(fi) + fd = fi->fh; + else + fd = lo_fd(req, ino); + + res = statx(fd, "", flags | AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, mask, + &buf); + if(res == -1) + fuse_reply_err(req, errno); + else + fuse_reply_statx(req, 0, &buf, lo->timeout); +} +#endif + +static const struct fuse_lowlevel_ops lo_oper = { + .init = lo_init, + .destroy = lo_destroy, + .lookup = lo_lookup, + .forget = lo_forget, + .getattr = lo_getattr, + .setattr = lo_setattr, + .readlink = lo_readlink, + .mknod = lo_mknod, + .mkdir = lo_mkdir, + .unlink = lo_unlink, + .rmdir = lo_rmdir, + .symlink = lo_symlink, + .rename = lo_rename, + .link = lo_link, + .open = lo_open, + .read = lo_read, + // write + .flush = lo_flush, + .release = lo_release, + .fsync = lo_fsync, + .opendir = lo_opendir, + .readdir = lo_readdir, + .releasedir = lo_releasedir, + .fsyncdir = lo_fsyncdir, + .statfs = lo_statfs, + .setxattr = lo_setxattr, + .getxattr = lo_getxattr, + .listxattr = lo_listxattr, + .removexattr = lo_removexattr, + // access + .create = lo_create, + // getlk + // setlk + // bmap + // ioctl + .write_buf = lo_write_buf, + // poll + // retrive_reply + .forget_multi = lo_forget_multi, + .flock = lo_flock, + .fallocate = lo_fallocate, + .readdirplus = lo_readdirplus, +#ifdef HAVE_COPY_FILE_RANGE + .copy_file_range = lo_copy_file_range, +#endif + .lseek = lo_lseek, + .tmpfile = lo_tmpfile, +#ifdef HAVE_STATX + .statx = lo_statx, +#endif +}; + +int +main(int argc, char* argv[]) { + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + struct fuse_session* se; + struct fuse_cmdline_opts opts; + struct fuse_loop_config* config; + struct lo_data lo = {.debug = 0, .writeback = 0}; + int ret = -1; + + /* Don't mask creation mode, kernel already did that */ + umask(0); + + pthread_mutex_init(&lo.mutex, NULL); + lo.root.next = lo.root.prev = &lo.root; + lo.root.fd = -1; + lo.cache = CACHE_NORMAL; + + if(fuse_parse_cmdline(&args, &opts) != 0) + return 1; + if(opts.show_help) { + printf("usage: %s [options] \n\n", argv[0]); + fuse_cmdline_help(); + fuse_lowlevel_help(); + passthrough_ll_help(); + ret = 0; + goto err_out1; + } else if(opts.show_version) { + printf("FUSE library version %s\n", fuse_pkgversion()); + fuse_lowlevel_version(); + ret = 0; + goto err_out1; + } + + if(opts.mountpoint == NULL) { + printf("usage: %s [options] \n", argv[0]); + printf(" %s --help\n", argv[0]); + ret = 1; + goto err_out1; + } + + if(fuse_opt_parse(&args, &lo, lo_opts, NULL) == -1) + return 1; + + lo.debug = opts.debug; + lo.root.refcount = 2; + if(lo.source) { + struct stat stat; + int res; + + res = lstat(lo.source, &stat); + if(res == -1) { + fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n", + lo.source); + exit(1); + } + if(!S_ISDIR(stat.st_mode)) { + fuse_log(FUSE_LOG_ERR, "source is not a directory\n"); + exit(1); + } + + } else { + lo.source = strdup("/"); + if(!lo.source) { + fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); + exit(1); + } + } + if(!lo.timeout_set) { + switch(lo.cache) { + case CACHE_NEVER: + lo.timeout = 0.0; + break; + + case CACHE_NORMAL: + lo.timeout = 1.0; + break; + + case CACHE_ALWAYS: + lo.timeout = 86400.0; + break; + } + } else if(lo.timeout < 0) { + fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n", lo.timeout); + exit(1); + } + + lo.root.fd = open(lo.source, O_PATH); + if(lo.root.fd == -1) { + fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", lo.source); + exit(1); + } + + se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); + if(se == NULL) + goto err_out1; + + if(fuse_set_signal_handlers(se) != 0) + goto err_out2; + + if(fuse_session_mount(se, opts.mountpoint) != 0) + goto err_out3; + + fuse_daemonize(opts.foreground); + + /* Block until ctrl+c or fusermount -u */ + if(opts.singlethread) + ret = fuse_session_loop(se); + else { + config = fuse_loop_cfg_create(); + fuse_loop_cfg_set_clone_fd(config, opts.clone_fd); + fuse_loop_cfg_set_max_threads(config, opts.max_threads); + ret = fuse_session_loop_mt(se, config); + fuse_loop_cfg_destroy(config); + config = NULL; + } + + fuse_session_unmount(se); +err_out3: + fuse_remove_signal_handlers(se); +err_out2: + fuse_session_destroy(se); +err_out1: + free(opts.mountpoint); + fuse_opt_free_args(&args); + + if(lo.root.fd >= 0) + close(lo.root.fd); + + free(lo.source); + return ret ? 1 : 0; +} -- GitLab From bc835b437846cb38cdbf33024e1beeb1f7706b57 Mon Sep 17 00:00:00 2001 From: sevenuz Date: Fri, 25 Jul 2025 11:23:13 +0200 Subject: [PATCH 03/32] some comments and gkfs init --- include/client/fuse/fuse_client.hpp | 66 +++++++++------- src/client/fuse/fuse_client.cpp | 112 +++++++++++++++++++++++++++- src/client/gkfs_functions.cpp | 4 +- 3 files changed, 154 insertions(+), 28 deletions(-) diff --git a/include/client/fuse/fuse_client.hpp b/include/client/fuse/fuse_client.hpp index a1f3d2d5b..d67e09976 100644 --- a/include/client/fuse/fuse_client.hpp +++ b/include/client/fuse/fuse_client.hpp @@ -1,3 +1,42 @@ +/* + Copyright 2018-2025, Barcelona Supercomputing Center (BSC), Spain + Copyright 2015-2025, Johannes Gutenberg Universitaet Mainz, Germany + + This software was partially supported by the + EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). + + This software was partially supported by the + ADA-FS project under the SPPEXA project funded by the DFG. + + This software was partially supported by the + the European Union’s Horizon 2020 JTI-EuroHPC research and + innovation programme, by the project ADMIRE (Project ID: 956748, + admire-eurohpc.eu) + + This project was partially promoted by the Ministry for Digital Transformation + and the Civil Service, within the framework of the Recovery, + Transformation and Resilience Plan - Funded by the European Union + -NextGenerationEU. + + This file is part of GekkoFS' POSIX interface. + + GekkoFS' POSIX interface is free software: you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GekkoFS' POSIX interface is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with GekkoFS' POSIX interface. If not, see + . + + SPDX-License-Identifier: LGPL-3.0-or-later +*/ + #ifndef GKFS_CLIENT_FUSE_CONTEXT_HPP #define GKFS_CLIENT_FUSE_CONTEXT_HPP @@ -37,31 +76,6 @@ struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { }; #endif -/* - * FUSE: Filesystem in Userspace - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE - */ - #include #include #include @@ -73,6 +87,8 @@ struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { #include #endif +#include + static inline int do_fallocate(int fd, int mode, off_t offset, off_t length) { #ifdef HAVE_FALLOCATE diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 86da62d5f..83d5434d2 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -1,4 +1,47 @@ +/* + Copyright 2018-2025, Barcelona Supercomputing Center (BSC), Spain + Copyright 2015-2025, Johannes Gutenberg Universitaet Mainz, Germany + + This software was partially supported by the + EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). + + This software was partially supported by the + ADA-FS project under the SPPEXA project funded by the DFG. + + This software was partially supported by the + the European Union’s Horizon 2020 JTI-EuroHPC research and + innovation programme, by the project ADMIRE (Project ID: 956748, + admire-eurohpc.eu) + + This project was partially promoted by the Ministry for Digital Transformation + and the Civil Service, within the framework of the Recovery, + Transformation and Resilience Plan - Funded by the European Union + -NextGenerationEU. + + This file is part of GekkoFS' POSIX interface. + + GekkoFS' POSIX interface is free software: you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GekkoFS' POSIX interface is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with GekkoFS' POSIX interface. If not, see + . + + SPDX-License-Identifier: LGPL-3.0-or-later +*/ + +#include "client/env.hpp" +#include "common/env_util.hpp" #include +#include +#include struct lo_inode { struct lo_inode* next; /* protected by lo->mutex */ @@ -85,6 +128,7 @@ lo_debug(fuse_req_t req) { static void lo_init(void* userdata, struct fuse_conn_info* conn) { + // TODO init gkfs struct lo_data* lo = (struct lo_data*) userdata; bool has_flag; @@ -103,8 +147,11 @@ lo_init(void* userdata, struct fuse_conn_info* conn) { conn->no_interrupt = 1; } +// cleanup of the fs +// delete all inodes static void lo_destroy(void* userdata) { + fuse_log(FUSE_LOG_DEBUG, "lo_destroy\n"); struct lo_data* lo = (struct lo_data*) userdata; while(lo->root.next != &lo->root) { @@ -115,8 +162,11 @@ lo_destroy(void* userdata) { } } +// This is similar to stat(). The st_dev and st_blksize fields are ignored. The +// st_ino field is ignored unless the use_ino mount option is given. static void lo_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_getattr\n"); int res; struct stat buf; struct lo_data* lo = lo_data(req); @@ -134,6 +184,7 @@ lo_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int valid, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_setattr\n"); int saverr; char procname[64]; struct lo_inode* inode = lo_inode(req, ino); @@ -203,8 +254,10 @@ out_err: fuse_reply_err(req, saverr); } +// traverse double linked list to find inode on device id and original ino static struct lo_inode* lo_find(struct lo_data* lo, struct stat* st) { + fuse_log(FUSE_LOG_DEBUG, "lo_find\n"); struct lo_inode* p; struct lo_inode* ret = NULL; @@ -224,6 +277,7 @@ lo_find(struct lo_data* lo, struct stat* st) { static struct lo_inode* create_new_inode(int fd, struct fuse_entry_param* e, struct lo_data* lo) { + fuse_log(FUSE_LOG_DEBUG, "create new inode\n"); struct lo_inode* inode = NULL; struct lo_inode *prev, *next; @@ -236,6 +290,8 @@ create_new_inode(int fd, struct fuse_entry_param* e, struct lo_data* lo) { inode->ino = e->attr.st_ino; inode->dev = e->attr.st_dev; + // this places the new inode directly after root and before the otheres + // doubled linked inode list... pthread_mutex_lock(&lo->mutex); prev = &lo->root; next = prev->next; @@ -250,6 +306,7 @@ create_new_inode(int fd, struct fuse_entry_param* e, struct lo_data* lo) { static int fill_entry_param_new_inode(fuse_req_t req, fuse_ino_t parent, int fd, struct fuse_entry_param* e) { + fuse_log(FUSE_LOG_DEBUG, "fill entry param new inode\n"); int res; struct lo_data* lo = lo_data(req); @@ -261,6 +318,7 @@ fill_entry_param_new_inode(fuse_req_t req, fuse_ino_t parent, int fd, if(res == -1) return errno; + // pointer trick, set ino to lo_inode address e->ino = (uintptr_t) create_new_inode(dup(fd), e, lo); if(lo_debug(req)) @@ -273,13 +331,14 @@ fill_entry_param_new_inode(fuse_req_t req, fuse_ino_t parent, int fd, static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char* name, struct fuse_entry_param* e) { + fuse_log(FUSE_LOG_DEBUG, "lo_do_lookup\n"); int newfd; int res; int saverr; struct lo_data* lo = lo_data(req); struct lo_inode* inode; - memset(e, 0, sizeof(*e)); + memset(e, 0, sizeof(*e)); // zero init bc e is our result thing from lo_lookup e->attr_timeout = lo->timeout; e->entry_timeout = lo->timeout; @@ -300,6 +359,7 @@ lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char* name, if(!inode) goto out_err; } + // pointer trick, set ino to lo_inode address e->ino = (uintptr_t) inode; if(lo_debug(req)) @@ -318,6 +378,7 @@ out_err: static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) { + fuse_log(FUSE_LOG_DEBUG, "lo_lookup\n"); struct fuse_entry_param e; int err; @@ -335,6 +396,7 @@ lo_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) { static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, dev_t rdev, const char* link) { + fuse_log(FUSE_LOG_DEBUG, "lo_mknod_symlink\n"); int res; int saverr; struct lo_inode* dir = lo_inode(req, parent); @@ -364,22 +426,26 @@ out: static void lo_mknod(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, dev_t rdev) { + fuse_log(FUSE_LOG_DEBUG, "lo_mknod\n"); lo_mknod_symlink(req, parent, name, mode, rdev, NULL); } static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode) { + fuse_log(FUSE_LOG_DEBUG, "lo_mkdir\n"); lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL); } static void lo_symlink(fuse_req_t req, const char* link, fuse_ino_t parent, const char* name) { + fuse_log(FUSE_LOG_DEBUG, "lo_symlink\n"); lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link); } static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, const char* name) { + fuse_log(FUSE_LOG_DEBUG, "lo_link\n"); int res; struct lo_data* lo = lo_data(req); struct lo_inode* inode = lo_inode(req, ino); @@ -420,6 +486,7 @@ out_err: static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char* name) { + fuse_log(FUSE_LOG_DEBUG, "lo_rmdir\n"); int res; res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); @@ -430,6 +497,7 @@ lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char* name) { static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char* name, fuse_ino_t newparent, const char* newname, unsigned int flags) { + fuse_log(FUSE_LOG_DEBUG, "lo_rename\n"); int res; if(flags) { @@ -444,6 +512,7 @@ lo_rename(fuse_req_t req, fuse_ino_t parent, const char* name, static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char* name) { + fuse_log(FUSE_LOG_DEBUG, "lo_unlink\n"); int res; res = unlinkat(lo_fd(req, parent), name, 0); @@ -453,6 +522,7 @@ lo_unlink(fuse_req_t req, fuse_ino_t parent, const char* name) { static void unref_inode(struct lo_data* lo, struct lo_inode* inode, uint64_t n) { + fuse_log(FUSE_LOG_DEBUG, "unref inode\n"); if(!inode) return; @@ -478,6 +548,7 @@ unref_inode(struct lo_data* lo, struct lo_inode* inode, uint64_t n) { static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { + fuse_log(FUSE_LOG_DEBUG, "lo_forget_one\n"); struct lo_data* lo = lo_data(req); struct lo_inode* inode = lo_inode(req, ino); @@ -492,6 +563,7 @@ lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { + fuse_log(FUSE_LOG_DEBUG, "lo_forget\n"); lo_forget_one(req, ino, nlookup); fuse_reply_none(req); } @@ -499,6 +571,7 @@ lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { static void lo_forget_multi(fuse_req_t req, size_t count, struct fuse_forget_data* forgets) { + fuse_log(FUSE_LOG_DEBUG, "lo_forget_multi\n"); int i; for(i = 0; i < count; i++) @@ -508,6 +581,7 @@ lo_forget_multi(fuse_req_t req, size_t count, static void lo_readlink(fuse_req_t req, fuse_ino_t ino) { + fuse_log(FUSE_LOG_DEBUG, "lo_readlink\n"); char buf[PATH_MAX + 1]; int res; @@ -531,11 +605,13 @@ struct lo_dirp { static struct lo_dirp* lo_dirp(struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_dirp\n"); return (struct lo_dirp*) (uintptr_t) fi->fh; } static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_opendir\n"); int error = ENOMEM; struct lo_data* lo = lo_data(req); struct lo_dirp* d; @@ -577,6 +653,7 @@ out_err: static int is_dot_or_dotdot(const char* name) { + fuse_log(FUSE_LOG_DEBUG, "lo_dot_or_dotdot\n"); return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); } @@ -584,6 +661,7 @@ is_dot_or_dotdot(const char* name) { static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info* fi, int plus) { + fuse_log(FUSE_LOG_DEBUG, "lo_do_readdir\n"); struct lo_dirp* d = lo_dirp(fi); char* buf; char* p; @@ -674,17 +752,20 @@ error: static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_readdir\n"); lo_do_readdir(req, ino, size, offset, fi, 0); } static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_readdirplus\n"); lo_do_readdir(req, ino, size, offset, fi, 1); } static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_releasedir\n"); struct lo_dirp* d = lo_dirp(fi); (void) ino; closedir(d->dp); @@ -695,6 +776,7 @@ lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { static void lo_tmpfile(fuse_req_t req, fuse_ino_t parent, mode_t mode, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_tmpfile\n"); int fd; struct lo_data* lo = lo_data(req); struct fuse_entry_param e; @@ -729,6 +811,7 @@ lo_tmpfile(fuse_req_t req, fuse_ino_t parent, mode_t mode, static void lo_create(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_create\n"); int fd; struct lo_data* lo = lo_data(req); struct fuse_entry_param e; @@ -764,6 +847,7 @@ lo_create(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_fsyncdir\n"); int res; int fd = dirfd(lo_dirp(fi)->dp); (void) ino; @@ -776,6 +860,7 @@ lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_open\n"); int fd; char buf[64]; struct lo_data* lo = lo_data(req); @@ -827,6 +912,7 @@ for writes to the same file in the kernel). */ static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_release\n"); (void) ino; close(fi->fh); @@ -835,6 +921,7 @@ lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_flush\n"); int res; (void) ino; res = close(dup(fi->fh)); @@ -844,6 +931,7 @@ lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_fsync\n"); int res; (void) ino; if(datasync) @@ -856,6 +944,7 @@ lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_read\n"); struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size); if(lo_debug(req)) @@ -874,6 +963,7 @@ lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec* in_buf, off_t off, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_write_buf\n"); (void) ino; ssize_t res; struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); @@ -898,6 +988,7 @@ lo_write_buf(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec* in_buf, static void lo_statfs(fuse_req_t req, fuse_ino_t ino) { + fuse_log(FUSE_LOG_DEBUG, "lo_statfs\n"); int res; struct statvfs stbuf; @@ -911,6 +1002,7 @@ lo_statfs(fuse_req_t req, fuse_ino_t ino) { static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, off_t length, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_fallocate\n"); int err; (void) ino; @@ -921,6 +1013,7 @@ lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi, int op) { + fuse_log(FUSE_LOG_DEBUG, "lo_flock\n"); int res; (void) ino; @@ -931,6 +1024,7 @@ lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi, int op) { static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char* name, size_t size) { + fuse_log(FUSE_LOG_DEBUG, "lo_getxattr\n"); char* value = NULL; char procname[64]; struct lo_inode* inode = lo_inode(req, ino); @@ -982,6 +1076,7 @@ out: static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) { + fuse_log(FUSE_LOG_DEBUG, "lo_listxattr\n"); char* value = NULL; char procname[64]; struct lo_inode* inode = lo_inode(req, ino); @@ -1033,6 +1128,7 @@ out: static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char* name, const char* value, size_t size, int flags) { + fuse_log(FUSE_LOG_DEBUG, "lo_setxattr\n"); char procname[64]; struct lo_inode* inode = lo_inode(req, ino); ssize_t ret; @@ -1059,6 +1155,7 @@ out: static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char* name) { + fuse_log(FUSE_LOG_DEBUG, "lo_removexattr\n"); char procname[64]; struct lo_inode* inode = lo_inode(req, ino); ssize_t ret; @@ -1109,6 +1206,7 @@ lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lo_lseek\n"); off_t res; (void) ino; @@ -1212,6 +1310,18 @@ main(int argc, char* argv[]) { lo.root.fd = -1; lo.cache = CACHE_NORMAL; + // init gekkofs + // TODO how to handle mount point + int res = gkfs_init(); + if(res != 0) { + printf("FUSE client failed to connect to gkfs daemon. Exit."); + exit(1); + } + auto fl = gkfs::syscall::gkfs_get_file_list("/"); + for(std::string s : fl) { + std::cout << s << std::endl; + } + if(fuse_parse_cmdline(&args, &opts) != 0) return 1; if(opts.show_help) { diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index 95405e0cc..6f9920df2 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -185,7 +185,7 @@ test_lock_file(const std::string& path) { * @param path * @param mode * @param flags - * @return 0 on success, -1 on failure + * @return fd on success, -1 on failure */ int gkfs_open(const std::string& path, mode_t mode, int flags) { @@ -1451,7 +1451,7 @@ gkfs_readv(int fd, const struct iovec* iov, int iovcnt) { * wrapper function for opening directories * errno may be set * @param path - * @return 0 on success or -1 on error + * @return fd on success or -1 on error */ int gkfs_opendir(const std::string& path) { -- GitLab From 9936cd50026d2e172837d141379df422b6ca69a9 Mon Sep 17 00:00:00 2001 From: sevenuz Date: Tue, 29 Jul 2025 13:09:33 +0200 Subject: [PATCH 04/32] first draft for gkfs --- include/client/fuse/fuse_client.hpp | 178 +++- src/client/CMakeLists.txt | 2 + src/client/fuse/fuse_client.cpp | 1399 ++++++--------------------- src/client/rpc/forward_metadata.cpp | 2 +- 4 files changed, 494 insertions(+), 1087 deletions(-) diff --git a/include/client/fuse/fuse_client.hpp b/include/client/fuse/fuse_client.hpp index d67e09976..8951676ef 100644 --- a/include/client/fuse/fuse_client.hpp +++ b/include/client/fuse/fuse_client.hpp @@ -40,6 +40,8 @@ #ifndef GKFS_CLIENT_FUSE_CONTEXT_HPP #define GKFS_CLIENT_FUSE_CONTEXT_HPP +#include "fuse_log.h" +#include extern "C" { #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12) #include @@ -81,13 +83,26 @@ struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { #include #include #include +#include +#include +#include +#include #ifdef __FreeBSD__ #include #include #endif +// GekkoFS Project Headers +#include +#include +#include // Assumed to provide LOG and CTX +#include +#include +#include +#include #include +#include static inline int do_fallocate(int fd, int mode, off_t offset, off_t length) { @@ -119,6 +134,142 @@ do_fallocate(int fd, int mode, off_t offset, off_t length) { #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 + char* path; + // other members libc DIR might have +}; + +static inline std::pair +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 @@ -126,18 +277,28 @@ do_fallocate(int fd, int mode, off_t offset, off_t length) { static inline int mknod_wrapper(int dirfd, const char* path, const char* link, int mode, dev_t rdev) { - int res; + fuse_log(FUSE_LOG_DEBUG, "mknod_wrapper \n"); + int res = -1; if(S_ISREG(mode)) { - res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode); + 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 = close(res); + res = gkfs::syscall::gkfs_close(res); } else if(S_ISDIR(mode)) { - res = mkdirat(dirfd, path, mode); + 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) { - res = symlinkat(link, dirfd, path); + fuse_log(FUSE_LOG_ERR, "fifo in mknod_wrapper not supported\n"); + errno = ENOTSUP; + return -1; + // res = symlinkat(link, dirfd, path); } else if(S_ISFIFO(mode)) { - res = mkfifoat(dirfd, path, mode); + fuse_log(FUSE_LOG_ERR, "fifo in mknod_wrapper not supported\n"); + errno = ENOTSUP; + return -1; + // res = mkfifoat(dirfd, path, mode); #ifdef __FreeBSD__ } else if(S_ISSOCK(mode)) { struct sockaddr_un su; @@ -164,7 +325,10 @@ mknod_wrapper(int dirfd, const char* path, const char* link, int mode, } #endif } else { - res = mknodat(dirfd, path, mode, rdev); + fuse_log(FUSE_LOG_ERR, "mknodat in mknod_wrapper not supported\n"); + errno = ENOTSUP; + return -1; + // res = mknodat(dirfd, path, mode, rdev); } return res; diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index 2ac9c6e03..b685d4a56 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -118,8 +118,10 @@ target_sources(fuse_client ) target_link_libraries(fuse_client + PRIVATE gkfs_common metadata distributor env_util arithmetic path_util rpc_utils ${FUSE3_LIBRARIES} gkfs_user_lib + gkfs_libc_intercept ) target_include_directories(fuse_client diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 83d5434d2..cfbf438d2 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -39,8 +39,11 @@ #include "client/env.hpp" #include "common/env_util.hpp" +#include #include +#include #include +#include #include struct lo_inode { @@ -147,1149 +150,368 @@ lo_init(void* userdata, struct fuse_conn_info* conn) { conn->no_interrupt = 1; } -// cleanup of the fs -// delete all inodes -static void -lo_destroy(void* userdata) { - fuse_log(FUSE_LOG_DEBUG, "lo_destroy\n"); - struct lo_data* lo = (struct lo_data*) userdata; - - while(lo->root.next != &lo->root) { - struct lo_inode* next = lo->root.next; - lo->root.next = next->next; - close(next->fd); - free(next); +// Simplified inode structure +struct Inode { + std::string path; + struct stat st; + uint64_t lookup_count; +}; +static std::mutex ino_mutex; +static std::unordered_map ino_map; +static fuse_ino_t next_ino = 2; // reserve 1 for root + +static fuse_ino_t +alloc_inode(const std::string& path) { + std::lock_guard lk(ino_mutex); + fuse_ino_t ino = next_ino++; + ino_map[ino] = {path, {}, 1}; + return ino; +} +static Inode* +get_inode(fuse_ino_t ino) { + std::lock_guard lk(ino_mutex); + auto it = ino_map.find(ino); + return it != ino_map.end() ? &it->second : nullptr; +} + +static void +lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { + fuse_log(FUSE_LOG_DEBUG, "lookup handler ino %u\n", parent); + auto* parent_inode = get_inode(parent); + if(!parent_inode) { + fuse_log(FUSE_LOG_DEBUG, "this error 1 \n", parent); + fuse_reply_err(req, ENOENT); + return; } + std::string child = parent_inode->path + name; + fuse_log(FUSE_LOG_DEBUG, "lookup %s\n", child.c_str()); + 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; + } + fuse_ino_t ino = alloc_inode(child); + ino_map[ino].st = st; + fuse_entry_param e = {}; + e.ino = ino; + e.attr = st; + e.attr_timeout = 1.0; + e.entry_timeout = 1.0; + fuse_reply_entry(req, &e); } -// This is similar to stat(). The st_dev and st_blksize fields are ignored. The -// st_ino field is ignored unless the use_ino mount option is given. static void -lo_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_getattr\n"); - int res; - struct stat buf; - struct lo_data* lo = lo_data(req); - int fd = fi ? fi->fh : lo_fd(req, ino); - - (void) fi; - - res = fstatat(fd, "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); - if(res == -1) - return (void) fuse_reply_err(req, errno); - - fuse_reply_attr(req, &buf, lo->timeout); +getattr_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "getattr handler \n"); + auto* inode = get_inode(ino); + if(!inode) { + fuse_reply_err(req, ENOENT); + return; + } + // query GekkoFS for latest attr + struct stat st; + int rc = gkfs::syscall::gkfs_stat(inode->path, &st); + if(rc) { + fuse_log(FUSE_LOG_DEBUG, "getattr error %u\n", rc); + fuse_reply_err(req, ENOENT); + return; + } + inode->st = st; + fuse_reply_attr(req, &st, 1.0); } static void -lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int valid, - struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_setattr\n"); - int saverr; - char procname[64]; - struct lo_inode* inode = lo_inode(req, ino); - int ifd = inode->fd; - int res; - - if(valid & FUSE_SET_ATTR_MODE) { - if(fi) { - res = fchmod(fi->fh, attr->st_mode); - } else { - sprintf(procname, "/proc/self/fd/%i", ifd); - res = chmod(procname, attr->st_mode); - } - if(res == -1) - goto out_err; - } - if(valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) { - uid_t uid = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t) -1; - gid_t gid = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t) -1; - - res = fchownat(ifd, "", uid, gid, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); - if(res == -1) - goto out_err; - } - if(valid & FUSE_SET_ATTR_SIZE) { - if(fi) { - res = ftruncate(fi->fh, attr->st_size); - } else { - sprintf(procname, "/proc/self/fd/%i", ifd); - res = truncate(procname, attr->st_size); - } - if(res == -1) - goto out_err; +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"); + auto* inode = get_inode(ino); + if(!inode) { + fuse_reply_err(req, ENOENT); + return; } - if(valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { - struct timespec tv[2]; - - tv[0].tv_sec = 0; - tv[1].tv_sec = 0; - tv[0].tv_nsec = UTIME_OMIT; - tv[1].tv_nsec = UTIME_OMIT; - - if(valid & FUSE_SET_ATTR_ATIME_NOW) - tv[0].tv_nsec = UTIME_NOW; - else if(valid & FUSE_SET_ATTR_ATIME) - tv[0] = attr->st_atim; - - if(valid & FUSE_SET_ATTR_MTIME_NOW) - tv[1].tv_nsec = UTIME_NOW; - else if(valid & FUSE_SET_ATTR_MTIME) - tv[1] = attr->st_mtim; - - if(fi) - res = futimens(fi->fh, tv); - else { - sprintf(procname, "/proc/self/fd/%i", ifd); - res = utimensat(AT_FDCWD, procname, tv, 0); - } - if(res == -1) - goto out_err; + // TODO how to change attr? + int rc = 0; + if(rc) { + fuse_reply_err(req, rc); + return; } - - return lo_getattr(req, ino, fi); - -out_err: - saverr = errno; - fuse_reply_err(req, saverr); -} - -// traverse double linked list to find inode on device id and original ino -static struct lo_inode* -lo_find(struct lo_data* lo, struct stat* st) { - fuse_log(FUSE_LOG_DEBUG, "lo_find\n"); - struct lo_inode* p; - struct lo_inode* ret = NULL; - - pthread_mutex_lock(&lo->mutex); - for(p = lo->root.next; p != &lo->root; p = p->next) { - if(p->ino == st->st_ino && p->dev == st->st_dev) { - assert(p->refcount > 0); - ret = p; - ret->refcount++; - break; - } + // TODO thats not in gkfs!!! + 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; + 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; + fuse_reply_attr(req, &inode->st, 1.0); +} + +static void +open_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "open handler \n"); + auto* inode = get_inode(ino); + if(!inode) { + fuse_reply_err(req, ENOENT); + return; } - pthread_mutex_unlock(&lo->mutex); - return ret; -} - - -static struct lo_inode* -create_new_inode(int fd, struct fuse_entry_param* e, struct lo_data* lo) { - fuse_log(FUSE_LOG_DEBUG, "create new inode\n"); - struct lo_inode* inode = NULL; - struct lo_inode *prev, *next; - - inode = (struct lo_inode*) calloc(1, sizeof(struct lo_inode)); - if(!inode) - return NULL; - - inode->refcount = 1; - inode->fd = fd; - inode->ino = e->attr.st_ino; - inode->dev = e->attr.st_dev; - - // this places the new inode directly after root and before the otheres - // doubled linked inode list... - pthread_mutex_lock(&lo->mutex); - prev = &lo->root; - next = prev->next; - next->prev = inode; - inode->next = next; - inode->prev = prev; - prev->next = inode; - pthread_mutex_unlock(&lo->mutex); - return inode; -} - -static int -fill_entry_param_new_inode(fuse_req_t req, fuse_ino_t parent, int fd, - struct fuse_entry_param* e) { - fuse_log(FUSE_LOG_DEBUG, "fill entry param new inode\n"); - int res; - struct lo_data* lo = lo_data(req); - - memset(e, 0, sizeof(*e)); - e->attr_timeout = lo->timeout; - e->entry_timeout = lo->timeout; - - res = fstatat(fd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); - if(res == -1) - return errno; - - // pointer trick, set ino to lo_inode address - e->ino = (uintptr_t) create_new_inode(dup(fd), e, lo); - - if(lo_debug(req)) - fuse_log(FUSE_LOG_DEBUG, " %lli/%lli -> %lli\n", - (unsigned long long) parent, fd, (unsigned long long) e->ino); - - return 0; -} - -static int -lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char* name, - struct fuse_entry_param* e) { - fuse_log(FUSE_LOG_DEBUG, "lo_do_lookup\n"); - int newfd; - int res; - int saverr; - struct lo_data* lo = lo_data(req); - struct lo_inode* inode; - - memset(e, 0, sizeof(*e)); // zero init bc e is our result thing from lo_lookup - e->attr_timeout = lo->timeout; - e->entry_timeout = lo->timeout; - - newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW); - if(newfd == -1) - goto out_err; - - res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); - if(res == -1) - goto out_err; - - inode = lo_find(lo_data(req), &e->attr); - if(inode) { - close(newfd); - newfd = -1; - } else { - inode = create_new_inode(newfd, e, lo); - if(!inode) - goto out_err; + const int mode = 0644; // -rw-r--r-- I think that doesnt matter anyway + int fd = gkfs::syscall::gkfs_open(inode->path, mode, + fi->flags); // TODO mode! + if(fd < 0) { + fuse_reply_err(req, ENOENT); + return; } - // pointer trick, set ino to lo_inode address - e->ino = (uintptr_t) inode; - - if(lo_debug(req)) - fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", - (unsigned long long) parent, name, - (unsigned long long) e->ino); - - return 0; - -out_err: - saverr = errno; - if(newfd != -1) - close(newfd); - return saverr; -} - -static void -lo_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) { - fuse_log(FUSE_LOG_DEBUG, "lo_lookup\n"); - struct fuse_entry_param e; - int err; - - if(lo_debug(req)) - fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", - parent, name); - - err = lo_do_lookup(req, parent, name, &e); - if(err) - fuse_reply_err(req, err); - else - fuse_reply_entry(req, &e); -} - -static void -lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, const char* name, - mode_t mode, dev_t rdev, const char* link) { - fuse_log(FUSE_LOG_DEBUG, "lo_mknod_symlink\n"); - int res; - int saverr; - struct lo_inode* dir = lo_inode(req, parent); - struct fuse_entry_param e; - - res = mknod_wrapper(dir->fd, name, link, mode, rdev); - - saverr = errno; - if(res == -1) - goto out; - - saverr = lo_do_lookup(req, parent, name, &e); - if(saverr) - goto out; - - if(lo_debug(req)) - fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", - (unsigned long long) parent, name, (unsigned long long) e.ino); - - fuse_reply_entry(req, &e); - return; - -out: - fuse_reply_err(req, saverr); -} - -static void -lo_mknod(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, - dev_t rdev) { - fuse_log(FUSE_LOG_DEBUG, "lo_mknod\n"); - lo_mknod_symlink(req, parent, name, mode, rdev, NULL); -} - -static void -lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode) { - fuse_log(FUSE_LOG_DEBUG, "lo_mkdir\n"); - lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL); -} - -static void -lo_symlink(fuse_req_t req, const char* link, fuse_ino_t parent, - const char* name) { - fuse_log(FUSE_LOG_DEBUG, "lo_symlink\n"); - lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link); -} - -static void -lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, const char* name) { - fuse_log(FUSE_LOG_DEBUG, "lo_link\n"); - int res; - struct lo_data* lo = lo_data(req); - struct lo_inode* inode = lo_inode(req, ino); - struct fuse_entry_param e; - char procname[64]; - int saverr; - - memset(&e, 0, sizeof(struct fuse_entry_param)); - e.attr_timeout = lo->timeout; - e.entry_timeout = lo->timeout; - - sprintf(procname, "/proc/self/fd/%i", inode->fd); - res = linkat(AT_FDCWD, procname, lo_fd(req, parent), name, - AT_SYMLINK_FOLLOW); - if(res == -1) - goto out_err; - - res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); - if(res == -1) - goto out_err; - - pthread_mutex_lock(&lo->mutex); - inode->refcount++; - pthread_mutex_unlock(&lo->mutex); - e.ino = (uintptr_t) inode; - - if(lo_debug(req)) - fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", - (unsigned long long) parent, name, (unsigned long long) e.ino); - - fuse_reply_entry(req, &e); - return; - -out_err: - saverr = errno; - fuse_reply_err(req, saverr); -} - -static void -lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char* name) { - fuse_log(FUSE_LOG_DEBUG, "lo_rmdir\n"); - int res; - - res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); - - fuse_reply_err(req, res == -1 ? errno : 0); + fi->fh = fd; // TODO file handle == file descriptor? + fuse_reply_open(req, fi); } static void -lo_rename(fuse_req_t req, fuse_ino_t parent, const char* name, - fuse_ino_t newparent, const char* newname, unsigned int flags) { - fuse_log(FUSE_LOG_DEBUG, "lo_rename\n"); - int res; - - if(flags) { - fuse_reply_err(req, EINVAL); +read_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, + struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "read handler \n"); + auto* inode = get_inode(ino); + if(!inode) { + fuse_reply_err(req, ENOENT); return; } - - res = renameat(lo_fd(req, parent), name, lo_fd(req, newparent), newname); - - fuse_reply_err(req, res == -1 ? errno : 0); -} - -static void -lo_unlink(fuse_req_t req, fuse_ino_t parent, const char* name) { - fuse_log(FUSE_LOG_DEBUG, "lo_unlink\n"); - int res; - - res = unlinkat(lo_fd(req, parent), name, 0); - - fuse_reply_err(req, res == -1 ? errno : 0); -} - -static void -unref_inode(struct lo_data* lo, struct lo_inode* inode, uint64_t n) { - fuse_log(FUSE_LOG_DEBUG, "unref inode\n"); - if(!inode) + std::vector buf(size); + int lc = gkfs::syscall::gkfs_lseek(fi->fh, off, SEEK_SET); + if(lc < 0) { + fuse_reply_err(req, 1); return; - - pthread_mutex_lock(&lo->mutex); - assert(inode->refcount >= n); - inode->refcount -= n; - if(!inode->refcount) { - struct lo_inode *prev, *next; - - prev = inode->prev; - next = inode->next; - next->prev = prev; - prev->next = next; - - pthread_mutex_unlock(&lo->mutex); - close(inode->fd); - free(inode); - - } else { - pthread_mutex_unlock(&lo->mutex); } -} - -static void -lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { - fuse_log(FUSE_LOG_DEBUG, "lo_forget_one\n"); - struct lo_data* lo = lo_data(req); - struct lo_inode* inode = lo_inode(req, ino); - - if(lo_debug(req)) { - fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", - (unsigned long long) ino, (unsigned long long) inode->refcount, - (unsigned long long) nlookup); + int rc = gkfs::syscall::gkfs_read(fi->fh, buf.data(), size); + if(rc < 0) { + fuse_reply_err(req, 1); + return; } - - unref_inode(lo, inode, nlookup); -} - -static void -lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { - fuse_log(FUSE_LOG_DEBUG, "lo_forget\n"); - lo_forget_one(req, ino, nlookup); - fuse_reply_none(req); -} - -static void -lo_forget_multi(fuse_req_t req, size_t count, - struct fuse_forget_data* forgets) { - fuse_log(FUSE_LOG_DEBUG, "lo_forget_multi\n"); - int i; - - for(i = 0; i < count; i++) - lo_forget_one(req, forgets[i].ino, forgets[i].nlookup); - fuse_reply_none(req); -} - -static void -lo_readlink(fuse_req_t req, fuse_ino_t ino) { - fuse_log(FUSE_LOG_DEBUG, "lo_readlink\n"); - char buf[PATH_MAX + 1]; - int res; - - res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf)); - if(res == -1) - return (void) fuse_reply_err(req, errno); - - if(res == sizeof(buf)) - return (void) fuse_reply_err(req, ENAMETOOLONG); - - buf[res] = '\0'; - - fuse_reply_readlink(req, buf); -} - -struct lo_dirp { - DIR* dp; - struct dirent* entry; - off_t offset; -}; - -static struct lo_dirp* -lo_dirp(struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_dirp\n"); - return (struct lo_dirp*) (uintptr_t) fi->fh; + fuse_reply_buf(req, buf.data(), rc); } static void -lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_opendir\n"); - int error = ENOMEM; - struct lo_data* lo = lo_data(req); - struct lo_dirp* d; - int fd = -1; - - d = (struct lo_dirp*) calloc(1, sizeof(struct lo_dirp)); - if(d == NULL) - goto out_err; - - fd = openat(lo_fd(req, ino), ".", O_RDONLY); - if(fd == -1) - goto out_errno; - - d->dp = fdopendir(fd); - if(d->dp == NULL) - goto out_errno; - - d->offset = 0; - d->entry = NULL; - - fi->fh = (uintptr_t) d; - if(lo->cache != CACHE_NEVER) - fi->cache_readdir = 1; - if(lo->cache == CACHE_ALWAYS) - fi->keep_cache = 1; - fuse_reply_open(req, fi); - return; - -out_errno: - error = errno; -out_err: - if(d) { - if(fd != -1) - close(fd); - free(d); +write_handler(fuse_req_t req, fuse_ino_t ino, const char* buf, size_t size, + off_t off, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "write handler \n"); + auto* inode = get_inode(ino); + if(!inode) { + fuse_reply_err(req, ENOENT); + return; } - fuse_reply_err(req, error); -} - -static int -is_dot_or_dotdot(const char* name) { - fuse_log(FUSE_LOG_DEBUG, "lo_dot_or_dotdot\n"); - return name[0] == '.' && - (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); -} - -static void -lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, - struct fuse_file_info* fi, int plus) { - fuse_log(FUSE_LOG_DEBUG, "lo_do_readdir\n"); - struct lo_dirp* d = lo_dirp(fi); - char* buf; - char* p; - size_t rem = size; - int err; - - (void) ino; - - buf = (char*) calloc(1, size); - if(!buf) { - err = ENOMEM; - goto error; + int lc = gkfs::syscall::gkfs_lseek(fi->fh, off, SEEK_SET); + if(lc < 0) { + fuse_reply_err(req, 1); + return; } - p = buf; - - if(offset != d->offset) { - seekdir(d->dp, offset); - d->entry = NULL; - d->offset = offset; + int rc = gkfs::syscall::gkfs_write(fi->fh, buf, size); + if(rc < 0) { + fuse_reply_err(req, 1); + return; } - while(1) { - size_t entsize; - off_t nextoff; - const char* name; - - if(!d->entry) { - errno = 0; - d->entry = readdir(d->dp); - if(!d->entry) { - if(errno) { // Error - err = errno; - goto error; - } else { // End of stream - break; - } - } - } - nextoff = d->entry->d_off; - name = d->entry->d_name; - fuse_ino_t entry_ino = 0; - if(plus) { - struct fuse_entry_param e; - if(is_dot_or_dotdot(name)) { - struct fuse_entry_param e = {}; - e.attr.st_ino = d->entry->d_ino; - e.attr.st_mode = static_cast<__mode_t>(d->entry->d_type << 12); - } else { - err = lo_do_lookup(req, ino, name, &e); - if(err) - goto error; - entry_ino = e.ino; - } - - entsize = fuse_add_direntry_plus(req, p, rem, name, &e, nextoff); - } else { - struct stat st = { - .st_ino = d->entry->d_ino, - .st_mode = static_cast<__mode_t>(d->entry->d_type << 12), - }; - entsize = fuse_add_direntry(req, p, rem, name, &st, nextoff); - } - if(entsize > rem) { - if(entry_ino != 0) - lo_forget_one(req, entry_ino, 1); - break; - } - - p += entsize; - rem -= entsize; - - d->entry = NULL; - d->offset = nextoff; + if(rc < 0) { + fuse_reply_err(req, 1); + return; } - - err = 0; -error: - // If there's an error, we can only signal it if we haven't stored - // any entries yet - otherwise we'd end up with wrong lookup - // counts for the entries that are already in the buffer. So we - // return what we've collected until that point. - if(err && rem == size) - fuse_reply_err(req, err); - else - fuse_reply_buf(req, buf, size - rem); - free(buf); + fuse_reply_write(req, rc); } static void -lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, - struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_readdir\n"); - lo_do_readdir(req, ino, size, offset, fi, 0); -} - -static void -lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, +create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_readdirplus\n"); - lo_do_readdir(req, ino, size, offset, fi, 1); -} - -static void -lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_releasedir\n"); - struct lo_dirp* d = lo_dirp(fi); - (void) ino; - closedir(d->dp); - free(d); - fuse_reply_err(req, 0); -} - -static void -lo_tmpfile(fuse_req_t req, fuse_ino_t parent, mode_t mode, - struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_tmpfile\n"); - int fd; - struct lo_data* lo = lo_data(req); - struct fuse_entry_param e; - int err; - - if(lo_debug(req)) - fuse_log(FUSE_LOG_DEBUG, "lo_tmpfile(parent=%" PRIu64 ")\n", parent); - - fd = openat(lo_fd(req, parent), ".", (fi->flags | O_TMPFILE) & ~O_NOFOLLOW, - mode); - if(fd == -1) - return (void) fuse_reply_err(req, errno); - - fi->fh = fd; - if(lo->cache == CACHE_NEVER) - fi->direct_io = 1; - else if(lo->cache == CACHE_ALWAYS) - fi->keep_cache = 1; - - /* parallel_direct_writes feature depends on direct_io features. - To make parallel_direct_writes valid, need set fi->direct_io - in current function. */ - fi->parallel_direct_writes = 1; - - err = fill_entry_param_new_inode(req, parent, fd, &e); - if(err) - fuse_reply_err(req, err); - else - fuse_reply_create(req, &e, fi); -} - -static void -lo_create(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, - struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_create\n"); - int fd; - struct lo_data* lo = lo_data(req); - struct fuse_entry_param e; - int err; - - if(lo_debug(req)) - fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", - parent, name); - - fd = openat(lo_fd(req, parent), name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, - mode); - if(fd == -1) - return (void) fuse_reply_err(req, errno); - - fi->fh = fd; - if(lo->cache == CACHE_NEVER) - fi->direct_io = 1; - else if(lo->cache == CACHE_ALWAYS) - fi->keep_cache = 1; - - /* parallel_direct_writes feature depends on direct_io features. - To make parallel_direct_writes valid, need set fi->direct_io - in current function. */ - fi->parallel_direct_writes = 1; - - err = lo_do_lookup(req, parent, name, &e); - if(err) - fuse_reply_err(req, err); - else - fuse_reply_create(req, &e, fi); -} + fuse_log(FUSE_LOG_DEBUG, "create handler \n"); + auto* parent_inode = get_inode(parent); + if(!parent_inode) { + fuse_reply_err(req, ENOENT); + return; + } + std::string path = parent_inode->path + name; + int rc = gkfs::syscall::gkfs_create(path, mode); + if(rc == -1) { + fuse_log(FUSE_LOG_DEBUG, "here here mode %i flags %i \n", mode, fi->flags); + fuse_reply_err(req, 1); + return; + } + struct stat st; + int sc = gkfs::syscall::gkfs_stat(path, &st); + if(sc == -1) { + fuse_log(FUSE_LOG_DEBUG, "thats why its not allowed \n"); + fuse_reply_err(req, 1); + return; + } + fuse_ino_t ino = alloc_inode(path); + ino_map[ino].st = st; + fuse_entry_param e = {}; + e.ino = ino; + e.attr = st; + e.attr_timeout = 1.0; + e.entry_timeout = 1.0; + fuse_reply_create(req, &e, fi); +} + +static void +opendir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "opendir handler \n"); + auto* inode = get_inode(ino); + if(!inode) { + fuse_reply_err(req, ENOTDIR); + return; + } + struct stat st; + fuse_log(FUSE_LOG_DEBUG, "open dir %s \n", inode->path.c_str()); + if(gkfs::syscall::gkfs_stat(inode->path, &st) != 0 || S_ISREG(st.st_mode)) { + fuse_reply_err(req, ENOTDIR); + return; + } -static void -lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, - struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_fsyncdir\n"); - int res; - int fd = dirfd(lo_dirp(fi)->dp); - (void) ino; - if(datasync) - res = fdatasync(fd); - else - res = fsync(fd); - fuse_reply_err(req, res == -1 ? errno : 0); -} + const int fd = gkfs::syscall::gkfs_opendir(inode->path); + if(fd < 0) { + fuse_reply_err(req, ENOTDIR); + return; + } -static void -lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_open\n"); - int fd; - char buf[64]; - struct lo_data* lo = lo_data(req); - - if(lo_debug(req)) - fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ino, - fi->flags); - - /* With writeback cache, kernel may send read requests even - when userspace opened write-only */ - if(lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) { - fi->flags &= ~O_ACCMODE; - fi->flags |= O_RDWR; + // Simulate DIR structure for GekkoFS + DIR* dirp = + static_cast(malloc(sizeof(DIR) + inode->path.length() + + 1)); // Approximate size for DIR and path + if(dirp == nullptr) { + gkfs::syscall::gkfs_close(fd); // Clean up opened GekkoFS fd + fuse_reply_err(req, ENOMEM); + return; } - /* With writeback cache, O_APPEND is handled by the kernel. - This breaks atomicity (since the file may change in the - underlying filesystem, so that the kernel's idea of the - end of the file isn't accurate anymore). In this example, - we just accept that. A more rigorous filesystem may want - to return an error here */ - if(lo->writeback && (fi->flags & O_APPEND)) - fi->flags &= ~O_APPEND; - - sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino)); - fd = open(buf, fi->flags & ~O_NOFOLLOW); - if(fd == -1) - return (void) fuse_reply_err(req, errno); - - fi->fh = fd; - if(lo->cache == CACHE_NEVER) - fi->direct_io = 1; - else if(lo->cache == CACHE_ALWAYS) - fi->keep_cache = 1; - - /* Enable direct_io when open has flags O_DIRECT to enjoy the feature - parallel_direct_writes (i.e., to get a shared lock, not exclusive lock, -for writes to the same file in the kernel). */ - if(fi->flags & O_DIRECT) - fi->direct_io = 1; - - /* parallel_direct_writes feature depends on direct_io features. - To make parallel_direct_writes valid, need set fi->direct_io - in current function. */ - fi->parallel_direct_writes = 1; + GkfsDir* gkfs_dir = reinterpret_cast(dirp); + gkfs_dir->fd = fd; + gkfs_dir->path = strdup(inode->path.c_str()); // strdup allocates memory + if(!gkfs_dir->path) { + free(dirp); + gkfs::syscall::gkfs_close(fd); + fuse_reply_err(req, ENOMEM); + return; + } + fi->fh = (uint64_t) dirp; // pointer trick fuse_reply_open(req, fi); } static void -lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_release\n"); - (void) ino; - - close(fi->fh); - fuse_reply_err(req, 0); -} - -static void -lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_flush\n"); - int res; - (void) ino; - res = close(dup(fi->fh)); - fuse_reply_err(req, res == -1 ? errno : 0); -} - -static void -lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, - struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_fsync\n"); - int res; - (void) ino; - if(datasync) - res = fdatasync(fi->fh); - else - res = fsync(fi->fh); - fuse_reply_err(req, res == -1 ? errno : 0); -} - -static void -lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, - struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_read\n"); - struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size); - - if(lo_debug(req)) - fuse_log(FUSE_LOG_DEBUG, - "lo_read(ino=%" PRIu64 ", size=%zd, " - "off=%lu)\n", - ino, size, (unsigned long) offset); - - buf.buf[0].flags = (enum fuse_buf_flags)(FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK); - buf.buf[0].fd = fi->fh; - buf.buf[0].pos = offset; - - fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE); -} - -static void -lo_write_buf(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec* in_buf, - off_t off, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_write_buf\n"); - (void) ino; - ssize_t res; - struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); - - out_buf.buf[0].flags = - (enum fuse_buf_flags)(FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK); - out_buf.buf[0].fd = fi->fh; - out_buf.buf[0].pos = off; - - if(lo_debug(req)) - fuse_log(FUSE_LOG_DEBUG, - "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n", ino, - out_buf.buf[0].size, (unsigned long) off); - - res = (ssize_t) fuse_buf_copy(&out_buf, in_buf, - (enum fuse_buf_copy_flags) 0); - if(res < 0) - fuse_reply_err(req, -res); - else - fuse_reply_write(req, (size_t) res); -} - -static void -lo_statfs(fuse_req_t req, fuse_ino_t ino) { - fuse_log(FUSE_LOG_DEBUG, "lo_statfs\n"); - int res; - struct statvfs stbuf; - - res = fstatvfs(lo_fd(req, ino), &stbuf); - if(res == -1) - fuse_reply_err(req, errno); - else - fuse_reply_statfs(req, &stbuf); -} - -static void -lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, - off_t length, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_fallocate\n"); - int err; - (void) ino; - - err = -do_fallocate(fi->fh, mode, offset, length); - - fuse_reply_err(req, err); -} - -static void -lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi, int op) { - fuse_log(FUSE_LOG_DEBUG, "lo_flock\n"); - int res; - (void) ino; - - res = flock(fi->fh, op); - - fuse_reply_err(req, res == -1 ? errno : 0); -} - -static void -lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char* name, size_t size) { - fuse_log(FUSE_LOG_DEBUG, "lo_getxattr\n"); - char* value = NULL; - char procname[64]; - struct lo_inode* inode = lo_inode(req, ino); - ssize_t ret; - int saverr; - - saverr = ENOSYS; - if(!lo_data(req)->xattr) - goto out; - - if(lo_debug(req)) { - fuse_log(FUSE_LOG_DEBUG, - "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", ino, name, - size); +readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, + struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "readdir handler \n"); + auto* dir_ptr = reinterpret_cast(fi->fh); + if(!dir_ptr) { + fuse_reply_err(req, EBADF); + return; } - sprintf(procname, "/proc/self/fd/%i", inode->fd); + auto open_dir = CTX->file_map()->get_dir(dir_ptr->fd); - if(size) { - value = (char*) malloc(size); - if(!value) - goto out_err; + fuse_log(FUSE_LOG_DEBUG, "read dir %s \n", open_dir->path().c_str()); - ret = getxattr(procname, name, value, size); - if(ret == -1) - goto out_err; - saverr = 0; - if(ret == 0) - goto out; - - fuse_reply_buf(req, value, ret); - } else { - ret = getxattr(procname, name, NULL, 0); - if(ret == -1) - goto out_err; - - fuse_reply_xattr(req, ret); + if(open_dir == nullptr) { + fuse_reply_err(req, EBADF); + return; } -out_free: - free(value); - return; - -out_err: - saverr = errno; -out: - fuse_reply_err(req, saverr); - goto out_free; -} -static void -lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) { - fuse_log(FUSE_LOG_DEBUG, "lo_listxattr\n"); - char* value = NULL; - char procname[64]; - struct lo_inode* inode = lo_inode(req, ino); - ssize_t ret; - int saverr; - - saverr = ENOSYS; - if(!lo_data(req)->xattr) - goto out; - - if(lo_debug(req)) { - fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n", - ino, size); + // Allocate a buffer to accumulate entries + char* buf = static_cast(malloc(size)); + if(!buf) { + fuse_reply_err(req, ENOMEM); + return; } - sprintf(procname, "/proc/self/fd/%i", inode->fd); - - if(size) { - value = (char*) malloc(size); - if(!value) - goto out_err; - - ret = listxattr(procname, value, size); - if(ret == -1) - goto out_err; - saverr = 0; - if(ret == 0) - goto out; - - fuse_reply_buf(req, value, ret); - } else { - ret = listxattr(procname, NULL, 0); - if(ret == -1) - goto out_err; - - fuse_reply_xattr(req, ret); - } -out_free: - free(value); - return; - -out_err: - saverr = errno; -out: - fuse_reply_err(req, saverr); - goto out_free; -} + size_t bytes_filled = 0; + off_t pos = off; -static void -lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char* name, const char* value, - size_t size, int flags) { - fuse_log(FUSE_LOG_DEBUG, "lo_setxattr\n"); - char procname[64]; - struct lo_inode* inode = lo_inode(req, ino); - ssize_t ret; - int saverr; - - saverr = ENOSYS; - if(!lo_data(req)->xattr) - goto out; - - if(lo_debug(req)) { - fuse_log(FUSE_LOG_DEBUG, - "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n", - ino, name, value, size); - } + while(pos < open_dir->size()) { + auto de = open_dir->getdent(pos); - sprintf(procname, "/proc/self/fd/%i", inode->fd); + struct stat st{}; + // TODO cannot be right, right? + st.st_ino = + std::hash()(open_dir->path() + "/" + de.name()); + st.st_mode = (de.type() == gkfs::filemap::FileType::regular) ? S_IFREG + : S_IFDIR; - ret = setxattr(procname, name, value, size, flags); - saverr = ret == -1 ? errno : 0; + size_t entry_size = + fuse_add_direntry(req, buf + bytes_filled, size - bytes_filled, + de.name().c_str(), &st, pos + 1); -out: - fuse_reply_err(req, saverr); -} + if(entry_size > size - bytes_filled) + break; // not enough space left -static void -lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char* name) { - fuse_log(FUSE_LOG_DEBUG, "lo_removexattr\n"); - char procname[64]; - struct lo_inode* inode = lo_inode(req, ino); - ssize_t ret; - int saverr; - - saverr = ENOSYS; - if(!lo_data(req)->xattr) - goto out; - - if(lo_debug(req)) { - fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", - ino, name); + bytes_filled += entry_size; + pos += 1; } - sprintf(procname, "/proc/self/fd/%i", inode->fd); - - ret = removexattr(procname, name); - saverr = ret == -1 ? errno : 0; - -out: - fuse_reply_err(req, saverr); -} - -#ifdef HAVE_COPY_FILE_RANGE -static void -lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, - struct fuse_file_info* fi_in, fuse_ino_t ino_out, - off_t off_out, struct fuse_file_info* fi_out, size_t len, - int flags) { - ssize_t res; - - if(lo_debug(req)) - fuse_log(FUSE_LOG_DEBUG, - "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, " - "off=%lu, ino=%" PRIu64 "/fd=%lu, " - "off=%lu, size=%zd, flags=0x%x)\n", - ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out, len, - flags); - - res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len, flags); - if(res < 0) - fuse_reply_err(req, errno); - else - fuse_reply_write(req, res); -} -#endif + open_dir->pos(pos); // update internal position if needed -static void -lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, - struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lo_lseek\n"); - off_t res; - - (void) ino; - res = lseek(fi->fh, off, whence); - if(res != -1) - fuse_reply_lseek(req, res); - else - fuse_reply_err(req, errno); + fuse_reply_buf(req, buf, bytes_filled); + free(buf); } -#ifdef HAVE_STATX static void -lo_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask, - struct fuse_file_info* fi) { - struct lo_data* lo = lo_data(req); - struct statx buf; - int res; - int fd; - - if(fi) - fd = fi->fh; - else - fd = lo_fd(req, ino); - - res = statx(fd, "", flags | AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, mask, - &buf); - if(res == -1) - fuse_reply_err(req, errno); - else - fuse_reply_statx(req, 0, &buf, lo->timeout); +init_handler(void* userdata, struct fuse_conn_info* conn) { + // userdata is GekkoFuse* if passed + // optional: adjust conn->max_write, enable writeback etc. } -#endif static const struct fuse_lowlevel_ops lo_oper = { - .init = lo_init, - .destroy = lo_destroy, - .lookup = lo_lookup, - .forget = lo_forget, - .getattr = lo_getattr, - .setattr = lo_setattr, - .readlink = lo_readlink, - .mknod = lo_mknod, - .mkdir = lo_mkdir, - .unlink = lo_unlink, - .rmdir = lo_rmdir, - .symlink = lo_symlink, - .rename = lo_rename, - .link = lo_link, - .open = lo_open, - .read = lo_read, - // write - .flush = lo_flush, - .release = lo_release, - .fsync = lo_fsync, - .opendir = lo_opendir, - .readdir = lo_readdir, - .releasedir = lo_releasedir, - .fsyncdir = lo_fsyncdir, - .statfs = lo_statfs, - .setxattr = lo_setxattr, - .getxattr = lo_getxattr, - .listxattr = lo_listxattr, - .removexattr = lo_removexattr, + .init = init_handler, // lo_init, + //.destroy = lo_destroy, + .lookup = lookup_handler, + //.forget = lo_forget, + .getattr = getattr_handler, + .setattr = setattr_handler, + //.readlink = lo_readlink, + //.mknod = lo_mknod, + //.mkdir = lo_mkdir, + //.unlink = lo_unlink, + //.rmdir = lo_rmdir, + //.symlink = lo_symlink, + //.rename = lo_rename, + //.link = lo_link, + .open = open_handler, + .read = read_handler, + .write = write_handler, + //.flush = lo_flush, + //.release = lo_release, + //.fsync = lo_fsync, + .opendir = opendir_handler, + .readdir = readdir_handler, + //.releasedir = lo_releasedir, + //.fsyncdir = lo_fsyncdir, + //.statfs = lo_statfs, + //.setxattr = lo_setxattr, + //.getxattr = lo_getxattr, + //.listxattr = lo_listxattr, + //.removexattr = lo_removexattr, // access - .create = lo_create, - // getlk - // setlk - // bmap - // ioctl - .write_buf = lo_write_buf, - // poll - // retrive_reply - .forget_multi = lo_forget_multi, - .flock = lo_flock, - .fallocate = lo_fallocate, - .readdirplus = lo_readdirplus, + .create = create_handler, +// getlk +// setlk +// bmap +// ioctl +//.write_buf = lo_write_buf, +// poll +// retrive_reply +//.forget_multi = lo_forget_multi, +//.flock = lo_flock, +//.fallocate = lo_fallocate, +//.readdirplus = lo_readdirplus, #ifdef HAVE_COPY_FILE_RANGE - .copy_file_range = lo_copy_file_range, +//.copy_file_range = lo_copy_file_range, #endif - .lseek = lo_lseek, - .tmpfile = lo_tmpfile, +//.lseek = lo_lseek, +//.tmpfile = lo_tmpfile, #ifdef HAVE_STATX - .statx = lo_statx, +//.statx = lo_statx, #endif }; @@ -1317,11 +539,25 @@ main(int argc, char* argv[]) { printf("FUSE client failed to connect to gkfs daemon. Exit."); exit(1); } + auto fl = gkfs::syscall::gkfs_get_file_list("/"); + for(std::string s : fl) { std::cout << s << std::endl; } + std::string root_path = "/"; + struct stat st; + int rc = gkfs::syscall::gkfs_stat(root_path, &st); + if(rc < 0) { + 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; + std::cout << "root node allocated" << std::endl; + + if(fuse_parse_cmdline(&args, &opts) != 0) return 1; if(opts.show_help) { @@ -1391,12 +627,14 @@ main(int argc, char* argv[]) { exit(1); } - lo.root.fd = open(lo.source, O_PATH); + 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) { fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", lo.source); exit(1); } + fuse_log(FUSE_LOG_DEBUG, "hier 2\n"); se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); if(se == NULL) goto err_out1; @@ -1423,10 +661,13 @@ main(int argc, char* argv[]) { fuse_session_unmount(se); err_out3: + fuse_log(FUSE_LOG_DEBUG, "hier 3\n"); fuse_remove_signal_handlers(se); err_out2: + fuse_log(FUSE_LOG_DEBUG, "hier 4\n"); fuse_session_destroy(se); err_out1: + fuse_log(FUSE_LOG_DEBUG, "hier 5\n"); free(opts.mountpoint); fuse_opt_free_args(&args); diff --git a/src/client/rpc/forward_metadata.cpp b/src/client/rpc/forward_metadata.cpp index ad0882cd2..7a7527bee 100644 --- a/src/client/rpc/forward_metadata.cpp +++ b/src/client/rpc/forward_metadata.cpp @@ -664,7 +664,7 @@ forward_get_metadentry_size(const std::string& path, const int copy) { /** * Send an RPC request to receive all entries of a directory. * @param open_dir - * @return error code + * @return error code, OpenDir */ pair> forward_get_dirents(const string& path) { -- GitLab From 805471ea3f8c2df090adc31c85199a4bc4762fbc Mon Sep 17 00:00:00 2001 From: sevenuz Date: Mon, 4 Aug 2025 10:54:44 +0200 Subject: [PATCH 05/32] compilation fix for older fuse versions, remove gkfs_libc header by including __dirstream struct --- include/client/fuse/fuse_client.hpp | 15 ++++++++++++++- src/client/fuse/fuse_client.cpp | 16 +++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/include/client/fuse/fuse_client.hpp b/include/client/fuse/fuse_client.hpp index 8951676ef..32f7b22be 100644 --- a/include/client/fuse/fuse_client.hpp +++ b/include/client/fuse/fuse_client.hpp @@ -45,6 +45,20 @@ extern "C" { #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12) #include + +// TODO do we really need this? Now its to replace the libc.hpp def for the +// sizeof(DIR) statement +struct __dirstream { + int fd; // File descriptor. + //__libc_lock_define (, lock) // Mutex lock for this structure. //TODO + size_t allocation; // Space allocated for the block. + size_t size; // Total valid data in the block. + size_t offset; // Current offset into the block. + off_t filepos; // Position of next entry to read. + // Directory block. + char* path; + char data[1] __attribute__((aligned(__alignof__(void*)))); +}; // Originally its 0, but C++ does not permit it } #include @@ -102,7 +116,6 @@ struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { #include #include #include -#include static inline int do_fallocate(int fd, int mode, off_t offset, off_t length) { diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index cfbf438d2..139dae487 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -334,10 +334,17 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, std::string path = parent_inode->path + name; int rc = gkfs::syscall::gkfs_create(path, mode); if(rc == -1) { - fuse_log(FUSE_LOG_DEBUG, "here here mode %i flags %i \n", mode, fi->flags); + fuse_log(FUSE_LOG_DEBUG, "here here mode %i flags %i \n", mode, + fi->flags); fuse_reply_err(req, 1); return; } + int fd = gkfs::syscall::gkfs_open(path, mode, fi->flags); + if(fd < 0) { + fuse_reply_err(req, ENOENT); + return; + } + fi->fh = fd; struct stat st; int sc = gkfs::syscall::gkfs_stat(path, &st); if(sc == -1) { @@ -651,12 +658,19 @@ main(int argc, char* argv[]) { if(opts.singlethread) ret = fuse_session_loop(se); else { +#if FUSE_MAJOR_VERSION > 3 || \ + (FUSE_MAJOR_VERSION == 3 && FUSE_MINOR_VERSION >= 12) config = fuse_loop_cfg_create(); fuse_loop_cfg_set_clone_fd(config, opts.clone_fd); fuse_loop_cfg_set_max_threads(config, opts.max_threads); ret = fuse_session_loop_mt(se, config); fuse_loop_cfg_destroy(config); config = NULL; +#else + // for older fuse versions like on the ubuntu22 + ret = fuse_session_loop_mt( + se, NULL); // second argument should be checked!!! +#endif } fuse_session_unmount(se); -- GitLab From 1f388c435f97a660f01717afadf31810522d2ae4 Mon Sep 17 00:00:00 2001 From: sevenuz Date: Mon, 4 Aug 2025 12:22:30 +0200 Subject: [PATCH 06/32] uncomplete cleanup and ci requirements --- .gitlab-ci.yml | 7 +- include/client/fuse/fuse_client.hpp | 185 +--------------------------- src/client/fuse/fuse_client.cpp | 10 +- 3 files changed, 10 insertions(+), 192 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cd389c722..2d1d21638 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -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 update + - apt install libfuse3-dev fuse3 # use ccache - ccache --zero-stats -M 750MiB -F 800 --evict-older-than 10d - /usr/sbin/update-ccache-symlinks @@ -533,7 +536,7 @@ cppcheck: script: - cd ${CI_PROJECT_DIR} - rm -rf external gkfs - - cppcheck -I/usr/local/include --xml --enable=warning,style,performance --force ${CI_PROJECT_DIR} 2> cppcheck_out.xml + - cppcheck -I/usr/local/include --xml --enable=warning,style,performance --force ${CI_PROJECT_DIR} 2> cppcheck_out.xml - cppcheck-codequality --input-file cppcheck_out.xml --output-file cppcheck.json #change paths after_script: @@ -544,7 +547,7 @@ cppcheck: reports: codequality: cppcheck2.json expire_in: 5 days - + ################################################################################ ## Deployment of documentation and reports diff --git a/include/client/fuse/fuse_client.hpp b/include/client/fuse/fuse_client.hpp index 32f7b22be..07bbd68cf 100644 --- a/include/client/fuse/fuse_client.hpp +++ b/include/client/fuse/fuse_client.hpp @@ -46,8 +46,7 @@ extern "C" { #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12) #include -// 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 #include #include - -/* 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 #include #include #include #include -#include #include #include #include @@ -117,98 +97,6 @@ struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { #include #include -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 -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) { diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 139dae487..7418d8178 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -37,14 +37,7 @@ SPDX-License-Identifier: LGPL-3.0-or-later */ -#include "client/env.hpp" -#include "common/env_util.hpp" -#include #include -#include -#include -#include -#include struct lo_inode { struct lo_inode* next; /* protected by lo->mutex */ @@ -532,7 +525,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 +627,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) { -- GitLab From ae0eaceed4f757f2f7a992116a68d6d59ca8ae91 Mon Sep 17 00:00:00 2001 From: sevenuz Date: Mon, 4 Aug 2025 12:26:59 +0200 Subject: [PATCH 07/32] ci dependencies --- .gitlab-ci.yml | 4 ++-- include/client/fuse/fuse_client.hpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2d1d21638..58a446ca7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -63,8 +63,8 @@ gkfs: #- 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 update - - apt install libfuse3-dev fuse3 + - 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 diff --git a/include/client/fuse/fuse_client.hpp b/include/client/fuse/fuse_client.hpp index 07bbd68cf..0bcc213a8 100644 --- a/include/client/fuse/fuse_client.hpp +++ b/include/client/fuse/fuse_client.hpp @@ -115,14 +115,14 @@ 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) - // res = gkfs::syscall::gkfs_create(resolved, mode | S_IFDIR); - // res = mkdirat(dirfd, path, mode); + // 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) { fuse_log(FUSE_LOG_ERR, "fifo in mknod_wrapper not supported\n"); errno = ENOTSUP; -- GitLab From a5a880187e28fef49efefc7befe058ab155e6b32 Mon Sep 17 00:00:00 2001 From: sevenuz Date: Wed, 6 Aug 2025 21:48:54 +0200 Subject: [PATCH 08/32] fuse integration test setup --- tests/integration/CMakeLists.txt | 9 ++ tests/integration/conftest.py | 20 +++- .../integration/fuse/test_basic_operations.py | 49 +++++++++ tests/integration/harness/gkfs.py | 100 ++++++++++++++++-- 4 files changed, 163 insertions(+), 15 deletions(-) create mode 100644 tests/integration/fuse/test_basic_operations.py diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index e7d7cccc1..6941dfc5b 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -143,6 +143,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 diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 9b3f318e6..f0971f690 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -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): @@ -78,7 +78,7 @@ def caplog(test_workspace, request, _caplog): def pytest_runtest_logreport(report): """ - Pytest hook called after a test phase (setup, call, teardownd) + Pytest hook called after a test phase (setup, call, teardownd) has completed. """ @@ -123,7 +123,7 @@ def gkfs_daemon(request): return request.getfixturevalue(request.param) @pytest.fixture -def gkfs_daemon_proxy(test_workspace, request): +def gkfs_daemon_proxy(test_workspace, request): interface = request.config.getoption('--interface') daemon = Daemon(interface, "rocksdb", test_workspace, True) @@ -177,7 +177,7 @@ def gkfs_shell(test_workspace): """ return ShellClient(test_workspace) - + @pytest.fixture def gkfs_shell_proxy(test_workspace): """ @@ -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() diff --git a/tests/integration/fuse/test_basic_operations.py b/tests/integration/fuse/test_basic_operations.py new file mode 100644 index 000000000..9c211a520 --- /dev/null +++ b/tests/integration/fuse/test_basic_operations.py @@ -0,0 +1,49 @@ +################################################################################ +# Copyright 2018-2025, Barcelona Supercomputing Center (BSC), Spain # +# Copyright 2015-2025, Johannes Gutenberg Universitaet Mainz, Germany # +# # +# This software was partially supported by the # +# EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). # +# # +# This software was partially supported by the # +# ADA-FS project under the SPPEXA project funded by the DFG. # +# # +# This file is part of GekkoFS. # +# # +# GekkoFS is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# GekkoFS is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with GekkoFS. If not, see . # +# # +# SPDX-License-Identifier: GPL-3.0-or-later # +################################################################################ + +import harness +from pathlib import Path +import errno +import stat +import os +import ctypes +import sh +import sys +import pytest +import time +from harness.logger import logger + +nonexisting = "nonexisting" + +def test_read(gkfs_daemon, fuse_client): + + file = gkfs_daemon.mountdir / "file" + + sh.bash("-c", "echo baum > " + str(file)) + assert sh.ls(fuse_client.mountdir) == "file\n" + assert sh.cat(file) == "baum\n" diff --git a/tests/integration/harness/gkfs.py b/tests/integration/harness/gkfs.py index e9722b5ed..3ed6da721 100644 --- a/tests/integration/harness/gkfs.py +++ b/tests/integration/harness/gkfs.py @@ -74,6 +74,7 @@ gkfwd_client_log_level = 'all' gkfwd_client_log_syscall_filter = 'epoll_wait,epoll_create' gkfwd_daemon_active_log_pattern = r'Startup successful. Daemon is ready.' +gkfs_fuse_client = 'fuse_client' def get_ip_addr(iface): return netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr'] @@ -345,7 +346,7 @@ class Daemon: logger.debug(f"daemon log file missing, checking if daemon is alive...") pid=self._proc.pid - + if not _process_exists(pid): raise RuntimeError(f"process {pid} is not running") @@ -392,7 +393,7 @@ class Proxy: self._workspace = workspace self._cmd = sh.Command(gkfs_proxy_cmd, self._workspace.bindirs) self._env = os.environ.copy() - + libdirs = ':'.join( filter(None, [os.environ.get('LD_LIBRARY_PATH', '')] + [str(p) for p in self._workspace.libdirs])) @@ -403,14 +404,14 @@ class Proxy: 'GKFS_PROXY_LOG_LEVEL': gkfs_proxy_log_level } self._env.update(self._patched_env) - + def run(self): args = ['-H', str(self.cwd / gkfs_hosts_file), '--pid-path', str(self.cwd / gkfs_proxy_pid), '-p', 'ofi+sockets'] - + logger.debug(f"spawning proxy") logger.debug(f"cmdline: {self._cmd} " + " ".join(map(str, args))) @@ -463,7 +464,7 @@ class Proxy: The maximum number of log lines to check for a match. """ - + init_time = perf_counter() @@ -479,7 +480,7 @@ class Proxy: logger.debug(f"proxy log file missing, checking if daemon is alive...") pid=self._proc.pid - + if not _process_exists(pid): raise RuntimeError(f"process {pid} is not running") @@ -514,7 +515,7 @@ class Proxy: @property def interface(self): return self._interface - + class _proxy_exec(): def __init__(self, client, name): @@ -609,7 +610,7 @@ class Client: @property def cwd(self): return self._workspace.twd - + class ClientLibc: """ A class to represent a GekkoFS client process with a patched LD_PRELOAD. @@ -901,7 +902,7 @@ class ShellClient: extra properties to it. """ - + found_cmd = shutil.which(cmd, path=':'.join(str(p) for p in self._search_paths) @@ -945,7 +946,7 @@ class ShellClient: @property def cwd(self): return self._workspace.twd - + class ShellClientLibc: """ @@ -1128,7 +1129,7 @@ class ShellClientLibc: extra properties to it. """ - + found_cmd = shutil.which(cmd, path=':'.join(str(p) for p in self._search_paths) @@ -1637,3 +1638,80 @@ class ShellFwdClient: @property def cwd(self): return self._workspace.twd + +class FuseClient: + def __init__(self, workspace): + self._workspace = workspace + #self._cmd = sh.Command("printenv", ["/usr/bin/"])#self._workspace.bindirs) + self._cmd = sh.Command(gkfs_fuse_client, self._workspace.bindirs) + self._env = os.environ.copy() + self._metadir = self.rootdir + + libdirs = ':'.join( + filter(None, [os.environ.get('LD_LIBRARY_PATH', '')] + + [str(p) for p in self._workspace.libdirs])) + + self._patched_env = { + 'LD_LIBRARY_PATH' : libdirs, + 'GKFS_HOSTS_FILE' : str(self.cwd / gkfs_hosts_file), + 'LIBGKFS_HOSTS_FILE' : str(self.cwd / gkfs_hosts_file), # TODO wtf why? see gkfs::env::HOSTS_FILE + } + self._env.update(self._patched_env) + + def run(self): + + args = [ "-f", "-s", self._workspace.mountdir, "-o", "auto_unmount" ] + + print(f"spawning fuse client") + print(f"cmdline: {self._cmd} " + " ".join(map(str, args))) + print(f"patched env:\n{pformat(self._patched_env)}") + + self._proc = self._cmd( + args, + _env=self._env, +# _out=sys.stdout, +# _err=sys.stderr, + _bg=True, + ) + + print(f"fuse client process spawned (PID={self._proc.pid})") + time.sleep(2) # give fuse time to register mount + + return self + + def shutdown(self): + print(f"terminating fuse client and unmounting") + sh.fusermount("-u", self._workspace.mountdir) + + try: + self._proc.terminate() + err = self._proc.wait() + except sh.SignalException_SIGTERM: + pass + except Exception: + raise + + + @property + def cwd(self): + return self._workspace.twd + + @property + def rootdir(self): + return self._workspace.rootdir + + @property + def mountdir(self): + return self._workspace.mountdir + + @property + def bindirs(self): + return self._workspace.bindirs + + @property + def logdir(self): + return self._workspace.logdir + + @property + def env(self): + return self._env -- GitLab From 685ff9ad9056be151376ba08847354c79e553980 Mon Sep 17 00:00:00 2001 From: sevenuz Date: Fri, 8 Aug 2025 16:38:30 +0200 Subject: [PATCH 09/32] improve test and fix touch in fuse --- src/client/fuse/fuse_client.cpp | 106 +++++++++++++----- .../integration/fuse/test_basic_operations.py | 2 + tests/integration/harness/gkfs.py | 8 +- 3 files changed, 86 insertions(+), 30 deletions(-) diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 7418d8178..6627a481e 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -218,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); - return; + + 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; + } + // Update cached stat so users see the new size + inode->st.st_size = new_size; } - // TODO thats not in gkfs!!! - 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; - 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; + + 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; + + // 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 @@ -346,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; @@ -458,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, @@ -480,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, diff --git a/tests/integration/fuse/test_basic_operations.py b/tests/integration/fuse/test_basic_operations.py index 9c211a520..5fe221138 100644 --- a/tests/integration/fuse/test_basic_operations.py +++ b/tests/integration/fuse/test_basic_operations.py @@ -43,7 +43,9 @@ nonexisting = "nonexisting" def test_read(gkfs_daemon, fuse_client): file = gkfs_daemon.mountdir / "file" + file2 = gkfs_daemon.mountdir / "file2" sh.bash("-c", "echo baum > " + str(file)) assert sh.ls(fuse_client.mountdir) == "file\n" assert sh.cat(file) == "baum\n" + sh.touch(str(file2)) diff --git a/tests/integration/harness/gkfs.py b/tests/integration/harness/gkfs.py index 3ed6da721..ccbeff673 100644 --- a/tests/integration/harness/gkfs.py +++ b/tests/integration/harness/gkfs.py @@ -1669,9 +1669,10 @@ class FuseClient: self._proc = self._cmd( args, _env=self._env, -# _out=sys.stdout, -# _err=sys.stderr, + _out='/dev/null', + _err='/dev/null', _bg=True, + _ok_code=list(range(0, 256)) ) print(f"fuse client process spawned (PID={self._proc.pid})") @@ -1680,9 +1681,6 @@ class FuseClient: return self def shutdown(self): - print(f"terminating fuse client and unmounting") - sh.fusermount("-u", self._workspace.mountdir) - try: self._proc.terminate() err = self._proc.wait() -- GitLab From 23aeae88ca78c0bca7d174d3d9153dfd4d0ae8ca Mon Sep 17 00:00:00 2001 From: sevenuz Date: Fri, 8 Aug 2025 19:25:15 +0200 Subject: [PATCH 10/32] cleanup and refactoring, fix lookup_count --- include/client/fuse/fuse_client.hpp | 89 +-- src/client/fuse/fuse_client.cpp | 507 +++++++++--------- .../integration/fuse/test_basic_operations.py | 3 + 3 files changed, 271 insertions(+), 328 deletions(-) diff --git a/include/client/fuse/fuse_client.hpp b/include/client/fuse/fuse_client.hpp index 0bcc213a8..9536f58e6 100644 --- a/include/client/fuse/fuse_client.hpp +++ b/include/client/fuse/fuse_client.hpp @@ -97,6 +97,31 @@ struct __dirstream { #include #include +// TODO do we really need the stat here? no i dont think so +struct Inode { + std::string path; + struct stat st; + uint64_t lookup_count; +}; + +enum { + CACHE_NEVER, + CACHE_NORMAL, + CACHE_ALWAYS, +}; + +struct u_data { + pthread_mutex_t mutex; + int debug; + int writeback; + int flock; + int xattr; + char* source; + double timeout; + int cache; + int timeout_set; +}; + struct GkfsDir { // Hypothetical structure that might be used if DIR is cast int fd; long int tell_pos; // for telldir/seekdir @@ -104,68 +129,4 @@ struct GkfsDir { // Hypothetical structure that might be used if DIR is cast // other members libc DIR might have }; -/* - * Creates files on the underlying file system in response to a FUSE_MKNOD - * operation - */ -static inline int -mknod_wrapper(int dirfd, const char* path, const char* link, int mode, - dev_t rdev) { - fuse_log(FUSE_LOG_DEBUG, "mknod_wrapper \n"); - int res = -1; - - if(S_ISREG(mode)) { - // 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) - // res = gkfs::syscall::gkfs_create(resolved, mode | S_IFDIR); - // res = mkdirat(dirfd, path, mode); - } else if(S_ISLNK(mode) && link != NULL) { - fuse_log(FUSE_LOG_ERR, "fifo in mknod_wrapper not supported\n"); - errno = ENOTSUP; - return -1; - // res = symlinkat(link, dirfd, path); - } else if(S_ISFIFO(mode)) { - fuse_log(FUSE_LOG_ERR, "fifo in mknod_wrapper not supported\n"); - errno = ENOTSUP; - return -1; - // res = mkfifoat(dirfd, path, mode); -#ifdef __FreeBSD__ - } else if(S_ISSOCK(mode)) { - struct sockaddr_un su; - int fd; - - if(strlen(path) >= sizeof(su.sun_path)) { - errno = ENAMETOOLONG; - return -1; - } - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if(fd >= 0) { - /* - * We must bind the socket to the underlying file - * system to create the socket file, even though - * we'll never listen on this socket. - */ - su.sun_family = AF_UNIX; - strncpy(su.sun_path, path, sizeof(su.sun_path)); - res = bindat(dirfd, fd, (struct sockaddr*) &su, sizeof(su)); - if(res == 0) - close(fd); - } else { - res = -1; - } -#endif - } else { - fuse_log(FUSE_LOG_ERR, "mknodat in mknod_wrapper not supported\n"); - errno = ENOTSUP; - return -1; - // res = mknodat(dirfd, path, mode, rdev); - } - - return res; -} - #endif // GKFS_CLIENT_FUSE_CONTEXT_HPP diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 6627a481e..b500c1527 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -39,47 +39,44 @@ #include -struct lo_inode { - struct lo_inode* next; /* protected by lo->mutex */ - struct lo_inode* prev; /* protected by lo->mutex */ - int fd; - ino_t ino; - dev_t dev; - uint64_t refcount; /* protected by lo->mutex */ -}; - -enum { - CACHE_NEVER, - CACHE_NORMAL, - CACHE_ALWAYS, -}; - -struct lo_data { - pthread_mutex_t mutex; - int debug; - int writeback; - int flock; - int xattr; - char* source; - double timeout; - int cache; - int timeout_set; - struct lo_inode root; /* protected by lo->mutex */ -}; +static struct fuse_lowlevel_ops ll_ops; +static std::mutex ino_mutex; +static std::unordered_map ino_map; +static std::unordered_map path_map; +static fuse_ino_t next_ino = 2; // reserve 1 for root + +static fuse_ino_t +alloc_inode(const std::string& path) { + std::lock_guard lk(ino_mutex); + fuse_ino_t ino = next_ino++; + ino_map[ino] = {path, {}, 1}; + return ino; +} + +static Inode* +get_inode(fuse_ino_t ino) { + std::lock_guard lk(ino_mutex); + auto it = ino_map.find(ino); + return it != ino_map.end() ? &it->second : nullptr; +} + +static struct u_data* +udata(fuse_req_t req) { + return (struct u_data*) fuse_req_userdata(req); +} static const struct fuse_opt lo_opts[] = { - {"writeback", offsetof(struct lo_data, writeback), 1}, - {"no_writeback", offsetof(struct lo_data, writeback), 0}, - {"source=%s", offsetof(struct lo_data, source), 0}, - {"flock", offsetof(struct lo_data, flock), 1}, - {"no_flock", offsetof(struct lo_data, flock), 0}, - {"xattr", offsetof(struct lo_data, xattr), 1}, - {"no_xattr", offsetof(struct lo_data, xattr), 0}, - {"timeout=%lf", offsetof(struct lo_data, timeout), 0}, - {"timeout=", offsetof(struct lo_data, timeout_set), 1}, - {"cache=never", offsetof(struct lo_data, cache), CACHE_NEVER}, - {"cache=auto", offsetof(struct lo_data, cache), CACHE_NORMAL}, - {"cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS}, + {"writeback", offsetof(struct u_data, writeback), 1}, + {"no_writeback", offsetof(struct u_data, writeback), 0}, + {"flock", offsetof(struct u_data, flock), 1}, + {"no_flock", offsetof(struct u_data, flock), 0}, + {"xattr", offsetof(struct u_data, xattr), 1}, + {"no_xattr", offsetof(struct u_data, xattr), 0}, + {"timeout=%lf", offsetof(struct u_data, timeout), 0}, + {"timeout=", offsetof(struct u_data, timeout_set), 1}, + {"cache=never", offsetof(struct u_data, cache), CACHE_NEVER}, + {"cache=auto", offsetof(struct u_data, cache), CACHE_NORMAL}, + {"cache=always", offsetof(struct u_data, cache), CACHE_ALWAYS}, FUSE_OPT_END}; @@ -99,85 +96,59 @@ passthrough_ll_help(void) { " -o cache=always Cache always\n"); } -static struct lo_data* -lo_data(fuse_req_t req) { - return (struct lo_data*) fuse_req_userdata(req); -} - -static struct lo_inode* -lo_inode(fuse_req_t req, fuse_ino_t ino) { - if(ino == FUSE_ROOT_ID) - return &lo_data(req)->root; - else - return (struct lo_inode*) (uintptr_t) ino; -} - -static int -lo_fd(fuse_req_t req, fuse_ino_t ino) { - return lo_inode(req, ino)->fd; -} - -static bool -lo_debug(fuse_req_t req) { - return lo_data(req)->debug != 0; -} - static void -lo_init(void* userdata, struct fuse_conn_info* conn) { - // TODO init gkfs - struct lo_data* lo = (struct lo_data*) userdata; - bool has_flag; - - if(lo->writeback) { - has_flag = fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE); - if(lo->debug && has_flag) - fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); - } - if(lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { - has_flag = fuse_set_feature_flag(conn, FUSE_CAP_FLOCK_LOCKS); - if(lo->debug && has_flag) - fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); - } +init_handler(void* userdata, struct fuse_conn_info* conn) { + fuse_log(FUSE_LOG_DEBUG, "init handler \n"); + // struct u_data* lo = (struct u_data*) userdata; + // bool has_flag; + + // TODO check other capabilities e.g. FUSE_CAP_READDIRPLUS + // if(lo->writeback) { + // has_flag = fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE); + // if(lo->debug && has_flag) + // fuse_log(FUSE_LOG_DEBUG, "init_handler: activating writeback\n"); + // } + // if(lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { + // has_flag = fuse_set_feature_flag(conn, FUSE_CAP_FLOCK_LOCKS); + // if(lo->debug && has_flag) + // fuse_log(FUSE_LOG_DEBUG, "init_handler: activating flock + // locks\n"); + // } /* Disable the receiving and processing of FUSE_INTERRUPT requests */ - conn->no_interrupt = 1; + // conn->no_interrupt = 1; } -// Simplified inode structure -struct Inode { - std::string path; - struct stat st; - uint64_t lookup_count; -}; -static std::mutex ino_mutex; -static std::unordered_map ino_map; -static fuse_ino_t next_ino = 2; // reserve 1 for root - -static fuse_ino_t -alloc_inode(const std::string& path) { - std::lock_guard lk(ino_mutex); - fuse_ino_t ino = next_ino++; - ino_map[ino] = {path, {}, 1}; - return ino; -} -static Inode* -get_inode(fuse_ino_t ino) { - std::lock_guard lk(ino_mutex); - auto it = ino_map.find(ino); - return it != ino_map.end() ? &it->second : nullptr; +static void +destroy_handler(void* userdata) { + fuse_log(FUSE_LOG_DEBUG, "destroy handler \n"); + // userdata is GekkoFuse* if passed } static void lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { fuse_log(FUSE_LOG_DEBUG, "lookup handler ino %u\n", parent); + auto* ud = udata(req); auto* parent_inode = get_inode(parent); if(!parent_inode) { - fuse_log(FUSE_LOG_DEBUG, "this error 1 \n", parent); fuse_reply_err(req, ENOENT); return; } std::string child = parent_inode->path + name; fuse_log(FUSE_LOG_DEBUG, "lookup %s\n", child.c_str()); + + + // 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++; + } else { + ino = alloc_inode(child); + path_map[child] = ino; + } + struct stat st; int rc = gkfs::syscall::gkfs_stat(child, &st); if(rc < 0) { @@ -185,19 +156,19 @@ lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { fuse_reply_err(req, ENOENT); return; } - fuse_ino_t ino = alloc_inode(child); ino_map[ino].st = st; fuse_entry_param e = {}; e.ino = ino; e.attr = st; - e.attr_timeout = 1.0; - e.entry_timeout = 1.0; + e.attr_timeout = ud->timeout; + e.entry_timeout = ud->timeout; fuse_reply_entry(req, &e); } static void getattr_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { fuse_log(FUSE_LOG_DEBUG, "getattr handler \n"); + auto* ud = udata(req); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); @@ -212,13 +183,14 @@ getattr_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { return; } inode->st = st; - fuse_reply_attr(req, &st, 1.0); + fuse_reply_attr(req, &st, ud->timeout); } 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 ino %u\n", ino); + auto* ud = udata(req); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); @@ -229,7 +201,8 @@ setattr_handler(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, 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_log(FUSE_LOG_DEBUG, "setattr truncate failed on %s\n", + inode->path.c_str()); fuse_reply_err(req, EIO); return; } @@ -244,7 +217,7 @@ setattr_handler(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, // 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); + fuse_reply_attr(req, &inode->st, ud->timeout); return; } @@ -320,6 +293,7 @@ static void create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, struct fuse_file_info* fi) { fuse_log(FUSE_LOG_DEBUG, "create handler \n"); + auto* ud = udata(req); auto* parent_inode = get_inode(parent); if(!parent_inode) { fuse_reply_err(req, ENOENT); @@ -352,8 +326,8 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, fuse_entry_param e = {}; e.ino = ino; e.attr = st; - e.attr_timeout = 1.0; - e.entry_timeout = 1.0; + e.attr_timeout = ud->timeout; + e.entry_timeout = ud->timeout; fuse_reply_create(req, &e, fi); } @@ -429,7 +403,7 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, } size_t bytes_filled = 0; - off_t pos = off; + size_t pos = off; while(pos < open_dir->size()) { auto de = open_dir->getdent(pos); @@ -458,23 +432,9 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, free(buf); } -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. -} - +/// releases file descriptor, not connected to lookup_count 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) { @@ -492,11 +452,31 @@ release_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { fuse_reply_err(req, 0); } +/// decrement lookup count 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); + + auto it = ino_map.find(ino); + if(it == ino_map.end()) { + fuse_reply_none(req); + return; + } + + Inode& inode = it->second; + if(inode.lookup_count > nlookup) + inode.lookup_count -= nlookup; + else + inode.lookup_count = 0; + + if(inode.lookup_count == 0) { // && inode.open_count == 0 + path_map.erase(inode.path); + ino_map.erase(it); + fuse_log(FUSE_LOG_DEBUG, "reached lookup_count 0 \n"); + } + + fuse_reply_none(req); + // fuse_reply_err(req, 0); } static void @@ -518,77 +498,8 @@ flush_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { fuse_reply_err(req, 0); } -static const struct fuse_lowlevel_ops lo_oper = { - .init = init_handler, - .destroy = destroy_handler, - .lookup = lookup_handler, - .forget = forget_handler, - .getattr = getattr_handler, - .setattr = setattr_handler, - //.readlink = lo_readlink, - //.mknod = lo_mknod, - //.mkdir = lo_mkdir, - //.unlink = lo_unlink, - //.rmdir = lo_rmdir, - //.symlink = lo_symlink, - //.rename = lo_rename, - //.link = lo_link, - .open = open_handler, - .read = read_handler, - .write = write_handler, - .flush = flush_handler, - .release = release_handler, - //.fsync = lo_fsync, - .opendir = opendir_handler, - .readdir = readdir_handler, - //.releasedir = lo_releasedir, - //.fsyncdir = lo_fsyncdir, - //.statfs = lo_statfs, - //.setxattr = lo_setxattr, - //.getxattr = lo_getxattr, - //.listxattr = lo_listxattr, - //.removexattr = lo_removexattr, - // access - .create = create_handler, -// getlk -// setlk -// bmap -// ioctl -//.write_buf = lo_write_buf, -// poll -// retrive_reply -//.forget_multi = lo_forget_multi, -//.flock = lo_flock, -//.fallocate = lo_fallocate, -//.readdirplus = lo_readdirplus, -#ifdef HAVE_COPY_FILE_RANGE -//.copy_file_range = lo_copy_file_range, -#endif -//.lseek = lo_lseek, -//.tmpfile = lo_tmpfile, -#ifdef HAVE_STATX -//.statx = lo_statx, -#endif -}; - -int -main(int argc, char* argv[]) { - struct fuse_args args = FUSE_ARGS_INIT(argc, argv); - struct fuse_session* se; - struct fuse_cmdline_opts opts; - struct fuse_loop_config* config; - struct lo_data lo = {.debug = 0, .writeback = 0}; - int ret = -1; - - /* Don't mask creation mode, kernel already did that */ - umask(0); // TODO do we need this and why? - - pthread_mutex_init(&lo.mutex, NULL); - lo.root.next = lo.root.prev = &lo.root; - lo.root.fd = -1; - lo.cache = CACHE_NORMAL; - - // init gekkofs +static void +init_gekkofs() { // TODO how to handle mount point int res = gkfs_init(); if(res != 0) { @@ -612,7 +523,107 @@ main(int argc, char* argv[]) { ino_map[FUSE_ROOT_ID] = {root_path, {}, 1}; ino_map[FUSE_ROOT_ID].st = st; std::cout << "root node allocated" << std::endl; +} + +static void +init_ll_ops(fuse_lowlevel_ops* ops) { + // file + ops->getattr = getattr_handler; + ops->setattr = setattr_handler; + ops->open = open_handler; + ops->create = create_handler; + // ops->unlink + ops->forget = forget_handler; + // ops->forget_multi + // ops->readlink + // ops->mknod + // ops->symlink + // ops->rename + // ops->link + ops->flush = flush_handler; + ops->release = release_handler; + // ops->fsync + // ops->write_buf + + // xattr + // ops->setxattr + // ops->getxattr + // ops->listxattr + // ops->removexattr + + // directory + ops->lookup = lookup_handler; + // ops->mkdir + // ops->rmdir + ops->readdir = readdir_handler; + ops->opendir = opendir_handler; + // ops->releasedir + // ops->fsyncdir = nullptr; + // ops->readdirplus + + // I/O + ops->write = write_handler; + ops->read = read_handler; + + // permission + // ops->access + + // misc + ops->init = init_handler; + ops->destroy = destroy_handler; + ops->statfs = nullptr; + + // ops->flock + // ops->getlk + // ops->setlk + // ops->bmap + // ops->ioctl + // ops->poll + // ops->retrive_reply + // ops->fallocate + // ops->copy_file_range + // ops->lseek + // ops->tmpfile + // ops->statx +} + +void +err_cleanup1(fuse_cmdline_opts opts, fuse_args& args) { + free(opts.mountpoint); + fuse_opt_free_args(&args); + std::cout << "# Resources released" << std::endl; +} + +void +err_cleanup2(fuse_session& se) { + fuse_session_destroy(&se); + std::cout << "# Fuse session destroyed" << std::endl; +} + +void +err_cleanup3(fuse_session& se) { + fuse_remove_signal_handlers(&se); + std::cout << "# Signal handlers removed" << std::endl; +} + +int +main(int argc, char* argv[]) { + init_ll_ops(&ll_ops); + + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + struct fuse_session* se; + struct fuse_cmdline_opts opts; + struct fuse_loop_config* config; + struct u_data ud{}; + ud.debug = 0; + ud.writeback = 0; + int ret = -1; + /* Don't mask creation mode, kernel already did that */ + umask(0); // TODO do we need this and why? + + pthread_mutex_init(&ud.mutex, NULL); + ud.cache = CACHE_NORMAL; if(fuse_parse_cmdline(&args, &opts) != 0) return 1; @@ -621,86 +632,69 @@ main(int argc, char* argv[]) { fuse_cmdline_help(); fuse_lowlevel_help(); passthrough_ll_help(); - ret = 0; - goto err_out1; + err_cleanup1(opts, args); + return 0; } else if(opts.show_version) { printf("FUSE library version %s\n", fuse_pkgversion()); fuse_lowlevel_version(); ret = 0; - goto err_out1; + err_cleanup1(opts, args); + return 0; } if(opts.mountpoint == NULL) { printf("usage: %s [options] \n", argv[0]); printf(" %s --help\n", argv[0]); ret = 1; - goto err_out1; + err_cleanup1(opts, args); + return 0; } - if(fuse_opt_parse(&args, &lo, lo_opts, NULL) == -1) + if(fuse_opt_parse(&args, &ud, lo_opts, NULL) == -1) return 1; - lo.debug = opts.debug; - lo.root.refcount = 2; - if(lo.source) { - struct stat stat; - int res; - - res = lstat(lo.source, &stat); - if(res == -1) { - fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n", - lo.source); - exit(1); - } - if(!S_ISDIR(stat.st_mode)) { - fuse_log(FUSE_LOG_ERR, "source is not a directory\n"); - exit(1); - } - - } else { - lo.source = strdup("/"); - if(!lo.source) { - fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); - exit(1); - } - } - if(!lo.timeout_set) { - switch(lo.cache) { + ud.debug = opts.debug; + if(!ud.timeout_set) { + switch(ud.cache) { case CACHE_NEVER: - lo.timeout = 0.0; + ud.timeout = 0.0; break; case CACHE_NORMAL: - lo.timeout = 1.0; + ud.timeout = 1.0; break; case CACHE_ALWAYS: - lo.timeout = 86400.0; + ud.timeout = 86400.0; break; } - } else if(lo.timeout < 0) { - fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n", lo.timeout); + } else if(ud.timeout < 0) { + fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n", ud.timeout); 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) { - fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", lo.source); - exit(1); - } - fuse_log(FUSE_LOG_DEBUG, "hier 2\n"); - se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); - if(se == NULL) - goto err_out1; + init_gekkofs(); - if(fuse_set_signal_handlers(se) != 0) - goto err_out2; - if(fuse_session_mount(se, opts.mountpoint) != 0) - goto err_out3; + se = fuse_session_new(&args, &ll_ops, sizeof(ll_ops), &ud); + if(se == nullptr) { + err_cleanup1(opts, args); + return 0; + } + + if(fuse_set_signal_handlers(se) != 0) { + err_cleanup2(*se); + err_cleanup1(opts, args); + return 0; + } + + if(fuse_session_mount(se, opts.mountpoint) != 0) { + err_cleanup3(*se); + err_cleanup2(*se); + err_cleanup1(opts, args); + return 0; + } fuse_daemonize(opts.foreground); @@ -724,20 +718,5 @@ main(int argc, char* argv[]) { } fuse_session_unmount(se); -err_out3: - fuse_log(FUSE_LOG_DEBUG, "hier 3\n"); - fuse_remove_signal_handlers(se); -err_out2: - fuse_log(FUSE_LOG_DEBUG, "hier 4\n"); - fuse_session_destroy(se); -err_out1: - fuse_log(FUSE_LOG_DEBUG, "hier 5\n"); - free(opts.mountpoint); - fuse_opt_free_args(&args); - - if(lo.root.fd >= 0) - close(lo.root.fd); - - free(lo.source); return ret ? 1 : 0; } diff --git a/tests/integration/fuse/test_basic_operations.py b/tests/integration/fuse/test_basic_operations.py index 5fe221138..ee252e3c8 100644 --- a/tests/integration/fuse/test_basic_operations.py +++ b/tests/integration/fuse/test_basic_operations.py @@ -49,3 +49,6 @@ def test_read(gkfs_daemon, fuse_client): assert sh.ls(fuse_client.mountdir) == "file\n" assert sh.cat(file) == "baum\n" sh.touch(str(file2)) + assert sh.wc("-c", str(file2)) == "0 " + str(file2) + "\n" + sh.truncate("-s", "20", str(file2)) + assert sh.wc("-c", str(file2)) == "20 " + str(file2) + "\n" -- GitLab From 37d6d8040d38bd222c25eafec218459df5f349cb Mon Sep 17 00:00:00 2001 From: sevenuz Date: Fri, 8 Aug 2025 23:50:20 +0200 Subject: [PATCH 11/32] mkdir causes segvault --- src/client/fuse/fuse_client.cpp | 39 ++++++++++++++++++- .../integration/fuse/test_basic_operations.py | 3 ++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index b500c1527..092bec3f0 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -50,6 +50,7 @@ alloc_inode(const std::string& path) { std::lock_guard lk(ino_mutex); fuse_ino_t ino = next_ino++; ino_map[ino] = {path, {}, 1}; + path_map[path] = ino; return ino; } @@ -146,7 +147,6 @@ lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { ino_map[ino].lookup_count++; } else { ino = alloc_inode(child); - path_map[child] = ino; } struct stat st; @@ -498,6 +498,41 @@ flush_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { fuse_reply_err(req, 0); } +static void +mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name, + mode_t mode) { + auto* ud = udata(req); + auto* parent_inode = get_inode(parent); + if(!parent_inode) { + fuse_reply_err(req, ENOENT); + return; + } + std::string path = parent_inode->path + name; + int rc = gkfs::syscall::gkfs_create(path, mode | S_IFDIR); + if(rc == -1) { + fuse_reply_err(req, 1); + return; + } + struct stat st; + int sc = gkfs::syscall::gkfs_stat(path, &st); + if(sc == -1) { + fuse_log(FUSE_LOG_DEBUG, "thats why its not allowed \n"); + fuse_reply_err(req, 1); + 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; + e.attr = st; + e.attr_timeout = ud->timeout; + e.entry_timeout = ud->timeout; + fuse_reply_entry(req, &e); + fuse_log(FUSE_LOG_DEBUG, "flush success \n"); + fuse_reply_err(req, 0); +} + static void init_gekkofs() { // TODO how to handle mount point @@ -553,7 +588,7 @@ init_ll_ops(fuse_lowlevel_ops* ops) { // directory ops->lookup = lookup_handler; - // ops->mkdir + ops->mkdir = mkdir_handler; // ops->rmdir ops->readdir = readdir_handler; ops->opendir = opendir_handler; diff --git a/tests/integration/fuse/test_basic_operations.py b/tests/integration/fuse/test_basic_operations.py index ee252e3c8..a074f37b1 100644 --- a/tests/integration/fuse/test_basic_operations.py +++ b/tests/integration/fuse/test_basic_operations.py @@ -45,6 +45,8 @@ def test_read(gkfs_daemon, fuse_client): file = gkfs_daemon.mountdir / "file" file2 = gkfs_daemon.mountdir / "file2" + dir = gkfs_daemon.mountdir / "dir" + sh.bash("-c", "echo baum > " + str(file)) assert sh.ls(fuse_client.mountdir) == "file\n" assert sh.cat(file) == "baum\n" @@ -52,3 +54,4 @@ def test_read(gkfs_daemon, fuse_client): assert sh.wc("-c", str(file2)) == "0 " + str(file2) + "\n" sh.truncate("-s", "20", str(file2)) assert sh.wc("-c", str(file2)) == "20 " + str(file2) + "\n" + sh.mkdir(str(dir)) -- GitLab From 5d44c3dec6064e2615c4bccd44ebf5299ab42475 Mon Sep 17 00:00:00 2001 From: sevenuz Date: Tue, 12 Aug 2025 13:37:01 +0200 Subject: [PATCH 12/32] fix mkdir, improve test --- src/client/fuse/fuse_client.cpp | 22 ++++++++++++++----- .../integration/fuse/test_basic_operations.py | 6 +++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 092bec3f0..6bd2b1b3e 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -66,6 +66,15 @@ udata(fuse_req_t req) { return (struct u_data*) fuse_req_userdata(req); } +static std::string +get_path(const Inode* inode, const char* name) { + if(inode->path == "/") { + return inode->path + name; + } else { + return inode->path + "/" + name; + } +} + static const struct fuse_opt lo_opts[] = { {"writeback", offsetof(struct u_data, writeback), 1}, {"no_writeback", offsetof(struct u_data, writeback), 0}, @@ -135,7 +144,9 @@ lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { fuse_reply_err(req, ENOENT); return; } - std::string child = parent_inode->path + name; + 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()); @@ -299,7 +310,7 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, fuse_reply_err(req, ENOENT); return; } - std::string path = parent_inode->path + name; + std::string path = get_path(parent_inode, name); int rc = gkfs::syscall::gkfs_create(path, mode); if(rc == -1) { fuse_log(FUSE_LOG_DEBUG, "here here mode %i flags %i \n", mode, @@ -507,7 +518,9 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name, fuse_reply_err(req, ENOENT); return; } - std::string path = parent_inode->path + name; + std::string path = get_path(parent_inode, name); + fuse_log(FUSE_LOG_DEBUG, "mkdir parent %s name %s\n", + parent_inode->path.c_str(), name); int rc = gkfs::syscall::gkfs_create(path, mode | S_IFDIR); if(rc == -1) { fuse_reply_err(req, 1); @@ -529,8 +542,6 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name, e.attr_timeout = ud->timeout; e.entry_timeout = ud->timeout; fuse_reply_entry(req, &e); - fuse_log(FUSE_LOG_DEBUG, "flush success \n"); - fuse_reply_err(req, 0); } static void @@ -557,6 +568,7 @@ init_gekkofs() { } ino_map[FUSE_ROOT_ID] = {root_path, {}, 1}; ino_map[FUSE_ROOT_ID].st = st; + path_map[root_path] = FUSE_ROOT_ID; std::cout << "root node allocated" << std::endl; } diff --git a/tests/integration/fuse/test_basic_operations.py b/tests/integration/fuse/test_basic_operations.py index a074f37b1..b1baedc82 100644 --- a/tests/integration/fuse/test_basic_operations.py +++ b/tests/integration/fuse/test_basic_operations.py @@ -55,3 +55,9 @@ def test_read(gkfs_daemon, fuse_client): sh.truncate("-s", "20", str(file2)) assert sh.wc("-c", str(file2)) == "20 " + str(file2) + "\n" sh.mkdir(str(dir)) + assert sh.ls(fuse_client.mountdir) == "dir file file2\n" + sh.cd(str(dir)) + assert sh.pwd() == str(dir) + "\n" + sh.mkdir("-p", "fu/bar") + assert sh.ls() == "fu\n" + sh.cd("fu") -- GitLab From d3451423227fd9928d3287917994cdee78f5f86a Mon Sep 17 00:00:00 2001 From: sevenuz Date: Tue, 12 Aug 2025 15:33:55 +0200 Subject: [PATCH 13/32] unlink, rmdir, releasedir and access handlers --- src/client/fuse/fuse_client.cpp | 80 +++++++++++++++++-- .../integration/fuse/test_basic_operations.py | 11 ++- 2 files changed, 81 insertions(+), 10 deletions(-) diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 6bd2b1b3e..21827f65c 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -342,6 +342,25 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, fuse_reply_create(req, &e, fi); } +/// TODO normally, the file should only be removed if the lookup count is zero, +/// problem? +static void +unlink_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { + fuse_log(FUSE_LOG_DEBUG, "unlink handler \n"); + auto* parent_inode = get_inode(parent); + if(!parent_inode) { + fuse_reply_err(req, ENOENT); + return; + } + std::string path = get_path(parent_inode, name); + int rc = gkfs::syscall::gkfs_remove(path); + if(rc == -1) { + fuse_reply_err(req, 1); + return; + } + fuse_reply_err(req, 0); +} + static void opendir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { fuse_log(FUSE_LOG_DEBUG, "opendir handler \n"); @@ -443,6 +462,24 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, free(buf); } +static void +releasedir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "releasedir handler \n"); + GkfsDir* dir_ptr = reinterpret_cast(fi->fh); + if(CTX->interception_enabled() && CTX->file_map()->exist(dir_ptr->fd)) { + int fd = dir_ptr->fd; + int ret = gkfs::syscall::gkfs_close(fd); // Close GekkoFS internal FD + + if(dir_ptr->path) { // Check if path was strdup'd + free(dir_ptr->path); + } + free(dir_ptr); // Free the DIR struct itself + fuse_reply_err(req, ret); + return; + } + fuse_reply_err(req, 0); +} + /// releases file descriptor, not connected to lookup_count static void release_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { @@ -495,17 +532,30 @@ 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 void +access_handler(fuse_req_t req, fuse_ino_t ino, int mask) { + fuse_log(FUSE_LOG_DEBUG, "access handler \n"); + auto* inode = get_inode(ino); + if(!inode) { + fuse_reply_err(req, ENOENT); + return; + } + int lc = gkfs::syscall::gkfs_access(inode->path, mask, true); + if(lc < 0) { + fuse_reply_err(req, 1); + return; + } fuse_reply_err(req, 0); } @@ -544,6 +594,22 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name, fuse_reply_entry(req, &e); } +static void +rmdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { + auto* parent_inode = get_inode(parent); + if(!parent_inode) { + fuse_reply_err(req, ENOENT); + return; + } + std::string path = get_path(parent_inode, name); + int rc = gkfs::syscall::gkfs_rmdir(path); + if(rc == -1) { + fuse_reply_err(req, 1); + return; + } + fuse_reply_err(req, 0); +} + static void init_gekkofs() { // TODO how to handle mount point @@ -579,7 +645,7 @@ init_ll_ops(fuse_lowlevel_ops* ops) { ops->setattr = setattr_handler; ops->open = open_handler; ops->create = create_handler; - // ops->unlink + ops->unlink = unlink_handler; ops->forget = forget_handler; // ops->forget_multi // ops->readlink @@ -601,10 +667,10 @@ init_ll_ops(fuse_lowlevel_ops* ops) { // directory ops->lookup = lookup_handler; ops->mkdir = mkdir_handler; - // ops->rmdir + ops->rmdir = rmdir_handler; ops->readdir = readdir_handler; ops->opendir = opendir_handler; - // ops->releasedir + ops->releasedir = releasedir_handler; // ops->fsyncdir = nullptr; // ops->readdirplus @@ -613,7 +679,7 @@ init_ll_ops(fuse_lowlevel_ops* ops) { ops->read = read_handler; // permission - // ops->access + ops->access = access_handler; // misc ops->init = init_handler; diff --git a/tests/integration/fuse/test_basic_operations.py b/tests/integration/fuse/test_basic_operations.py index b1baedc82..e031d9da1 100644 --- a/tests/integration/fuse/test_basic_operations.py +++ b/tests/integration/fuse/test_basic_operations.py @@ -58,6 +58,11 @@ def test_read(gkfs_daemon, fuse_client): assert sh.ls(fuse_client.mountdir) == "dir file file2\n" sh.cd(str(dir)) assert sh.pwd() == str(dir) + "\n" - sh.mkdir("-p", "fu/bar") - assert sh.ls() == "fu\n" - sh.cd("fu") + sh.mkdir("-p", "foo/bar") + assert sh.ls() == "foo\n" + sh.cd("foo") + sh.rmdir("bar") + sh.cd("..") + sh.rmdir("foo") + sh.rm(str(file2)) + assert sh.ls(fuse_client.mountdir) == "dir file\n" -- GitLab From 6926bcdc37d9ae7c54b13a67dc62c0e06af0d77a Mon Sep 17 00:00:00 2001 From: sevenuz Date: Tue, 12 Aug 2025 16:21:13 +0200 Subject: [PATCH 14/32] lseek --- src/client/fuse/fuse_client.cpp | 14 +++++++++++++- src/client/gkfs_functions.cpp | 4 ++-- tests/integration/fuse/test_basic_operations.py | 13 +++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 21827f65c..07c7a2983 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -251,6 +251,18 @@ open_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { fuse_reply_open(req, fi); } +static void +lseek_handler(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, + struct fuse_file_info* fi) { + fuse_log(FUSE_LOG_DEBUG, "lseek handler \n"); + int lc = gkfs::syscall::gkfs_lseek(fi->fh, off, whence); + if(lc < 0) { + fuse_reply_err(req, 1); + return; + } + fuse_reply_lseek(req, lc); +} + static void read_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info* fi) { @@ -657,6 +669,7 @@ init_ll_ops(fuse_lowlevel_ops* ops) { ops->release = release_handler; // ops->fsync // ops->write_buf + ops->lseek = lseek_handler; // xattr // ops->setxattr @@ -695,7 +708,6 @@ init_ll_ops(fuse_lowlevel_ops* ops) { // ops->retrive_reply // ops->fallocate // ops->copy_file_range - // ops->lseek // ops->tmpfile // ops->statx } diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index 6f9920df2..ff9046b0b 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -809,7 +809,7 @@ gkfs_statvfs(struct statvfs* buf) { * @param fd * @param offset * @param whence - * @return 0 on success, -1 on failure + * @return position on success, -1 on failure */ off_t gkfs_lseek(unsigned int fd, off_t offset, unsigned int whence) { @@ -822,7 +822,7 @@ gkfs_lseek(unsigned int fd, off_t offset, unsigned int whence) { * @param gkfs_fd * @param offset * @param whence - * @return 0 on success, -1 on failure + * @return position on success, -1 on failure */ off_t gkfs_lseek(shared_ptr gkfs_fd, off_t offset, diff --git a/tests/integration/fuse/test_basic_operations.py b/tests/integration/fuse/test_basic_operations.py index e031d9da1..ed5d91306 100644 --- a/tests/integration/fuse/test_basic_operations.py +++ b/tests/integration/fuse/test_basic_operations.py @@ -40,6 +40,7 @@ from harness.logger import logger nonexisting = "nonexisting" +# somehow are multiple test causing an error in the fuse client... def test_read(gkfs_daemon, fuse_client): file = gkfs_daemon.mountdir / "file" @@ -66,3 +67,15 @@ def test_read(gkfs_daemon, fuse_client): sh.rmdir("foo") sh.rm(str(file2)) assert sh.ls(fuse_client.mountdir) == "dir file\n" + + # lseek test + path = gkfs_daemon.mountdir / "lseek_file" + with open(path, "wb") as f: + f.write(b"HelloWorld") # 10 bytes + fd = os.open(path, os.O_RDONLY) + pos = os.lseek(fd, 5, os.SEEK_SET) # absolute seek + assert pos == 5 + data = os.read(fd, 5) + assert data == b"World" + os.close(fd) + os.remove(path) -- GitLab From 2ff62a6482353d7d5aadcbd43ad1d95d0012f7c4 Mon Sep 17 00:00:00 2001 From: sevenuz Date: Wed, 13 Aug 2025 17:45:06 +0200 Subject: [PATCH 15/32] symlink support, fsync, tmpfile (not supported though) --- include/client/fuse/fuse_client.hpp | 1 + src/client/fuse/fuse_client.cpp | 125 +++++++++++++++--- src/client/gkfs_functions.cpp | 6 +- tests/integration/conftest.template | 20 ++- .../integration/fuse/test_basic_operations.py | 9 +- 5 files changed, 139 insertions(+), 22 deletions(-) diff --git a/include/client/fuse/fuse_client.hpp b/include/client/fuse/fuse_client.hpp index 9536f58e6..3a611c28e 100644 --- a/include/client/fuse/fuse_client.hpp +++ b/include/client/fuse/fuse_client.hpp @@ -81,6 +81,7 @@ struct __dirstream { #include #include #include +#include #ifdef __FreeBSD__ #include diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 07c7a2983..8f27816da 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -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; } @@ -253,7 +253,7 @@ open_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { static void lseek_handler(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, - struct fuse_file_info* fi) { + struct fuse_file_info* fi) { fuse_log(FUSE_LOG_DEBUG, "lseek handler \n"); int lc = gkfs::syscall::gkfs_lseek(fi->fh, off, whence); if(lc < 0) { @@ -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] \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; } diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index ff9046b0b..b12bfe073 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -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) { diff --git a/tests/integration/conftest.template b/tests/integration/conftest.template index 9b3f318e6..f0971f690 100644 --- a/tests/integration/conftest.template +++ b/tests/integration/conftest.template @@ -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): @@ -78,7 +78,7 @@ def caplog(test_workspace, request, _caplog): def pytest_runtest_logreport(report): """ - Pytest hook called after a test phase (setup, call, teardownd) + Pytest hook called after a test phase (setup, call, teardownd) has completed. """ @@ -123,7 +123,7 @@ def gkfs_daemon(request): return request.getfixturevalue(request.param) @pytest.fixture -def gkfs_daemon_proxy(test_workspace, request): +def gkfs_daemon_proxy(test_workspace, request): interface = request.config.getoption('--interface') daemon = Daemon(interface, "rocksdb", test_workspace, True) @@ -177,7 +177,7 @@ def gkfs_shell(test_workspace): """ return ShellClient(test_workspace) - + @pytest.fixture def gkfs_shell_proxy(test_workspace): """ @@ -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() diff --git a/tests/integration/fuse/test_basic_operations.py b/tests/integration/fuse/test_basic_operations.py index ed5d91306..c472be0e3 100644 --- a/tests/integration/fuse/test_basic_operations.py +++ b/tests/integration/fuse/test_basic_operations.py @@ -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" -- GitLab From 7a1b3b4dc5c7b05c279c9231f895307a35ee790a Mon Sep 17 00:00:00 2001 From: sevenuz Date: Wed, 13 Aug 2025 18:24:04 +0200 Subject: [PATCH 16/32] renaming support --- src/client/fuse/fuse_client.cpp | 92 ++++++++++++++++++- .../integration/fuse/test_basic_operations.py | 4 + 2 files changed, 92 insertions(+), 4 deletions(-) diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 8f27816da..f3639d1e0 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -68,7 +68,7 @@ udata(fuse_req_t req) { static std::string get_path(const Inode* inode, const char* name) { - if (name[0] == '/') { + if(name[0] == '/') { return std::string(name); } if(inode->path == "/") { @@ -676,9 +676,10 @@ symlink_handler(fuse_req_t req, const char* linkname, fuse_ino_t parent, std::string path = get_path(parent_inode, name); std::string target = get_path(parent_inode, linkname); - if (target.rfind(ud->source, 0) == 0) { + if(target.rfind(ud->source, 0) == 0) { // starts with mount path - target = get_path(parent_inode, target.substr(strlen(ud->source)).c_str()); + 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(), @@ -712,6 +713,89 @@ symlink_handler(fuse_req_t req, const char* linkname, fuse_ino_t parent, #endif } +static void +rename_handler(fuse_req_t req, fuse_ino_t old_parent, const char* old_name, + fuse_ino_t new_parent, const char* new_name, + unsigned int flags) { + +#ifdef HAS_RENAME + auto* old_parent_inode = get_inode(old_parent); + if(!old_parent_inode) { + fuse_reply_err(req, ENOENT); + return; + } + auto* new_parent_inode = get_inode(new_parent); + if(!new_parent_inode) { + fuse_reply_err(req, ENOENT); + return; + } + std::string old_path = get_path(old_parent_inode, old_name); + std::string new_path = get_path(new_parent_inode, new_name); + + struct stat st_src{}; + if(gkfs::syscall::gkfs_stat(old_path, &st_src) < 0) { + fuse_reply_err(req, errno == 0 ? ENOENT : errno); + return; + } + + struct stat st_dst{}; + const bool dst_exists = (gkfs::syscall::gkfs_stat(new_path, &st_dst) == 0); + + if((flags & RENAME_NOREPLACE) && dst_exists) { + fuse_reply_err(req, EEXIST); + return; + } + + if(flags & RENAME_EXCHANGE) { + fuse_reply_err(req, EOPNOTSUPP); + return; + } + + int rc = gkfs::syscall::gkfs_rename(old_path, new_path); + if(rc < 0) { + fuse_reply_err(req, 1); + return; + } + + fuse_ino_t src_ino = 0; + auto it_src = path_map.find(old_path); + if(it_src != path_map.end()) { + 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; + } + + // If destination existed and was overwritten, detach its mapping + if(dst_exists) { + auto it_dst = path_map.find(new_path); + if(it_dst != path_map.end()) { + fuse_ino_t dst_ino = it_dst->second; + path_map.erase(it_dst); + + // Mark the old dst inode as disconnected (no pathname). + // Keep it alive until lookup_count==0 and open_count==0, then free + // in forget(). + auto& dst_rec = ino_map[dst_ino]; + dst_rec.path.clear(); + } + } + + // 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); +#endif +} static void init_gekkofs() { @@ -754,7 +838,7 @@ init_ll_ops(fuse_lowlevel_ops* ops) { ops->readlink = readlink_handler; // ops->mknod ops->symlink = symlink_handler; - // ops->rename + ops->rename = rename_handler; // ops->link ops->flush = flush_handler; ops->release = release_handler; diff --git a/tests/integration/fuse/test_basic_operations.py b/tests/integration/fuse/test_basic_operations.py index c472be0e3..8c0142a84 100644 --- a/tests/integration/fuse/test_basic_operations.py +++ b/tests/integration/fuse/test_basic_operations.py @@ -86,3 +86,7 @@ def test_read(gkfs_daemon, fuse_client): sh.ln("-s", str(file), "link") assert sh.ls(str(dir)) == "link\n" assert sh.cat("link") == "baum\n" + + # renaming + sh.mv("link", "../lonk") + assert sh.ls("..") == "dir file lonk\n" -- GitLab From 6f4aa124a1f6dc31fa8e3b5685ad5abf8b604e59 Mon Sep 17 00:00:00 2001 From: sevenuz Date: Thu, 14 Aug 2025 15:52:37 +0200 Subject: [PATCH 17/32] fuse tests ci embedding --- .gitlab-ci.yml | 2 +- tests/integration/CMakeLists.txt | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 58a446ca7..67a648014 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -159,7 +159,7 @@ gkfs:integration: needs: ['gkfs'] # we need to remove gkfs dependencies on manual parallel: matrix: - - SUBTEST: [ data, status, syscalls, directories, operations, position, shell, rename ] + - SUBTEST: [ data, status, syscalls, directories, operations, position, shell, rename, fuse ] rules: - if: '$CI_MERGE_REQUEST_EVENT_TYPE == "detached"' when: never diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index 6941dfc5b..402046455 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -248,6 +248,16 @@ if (GKFS_INSTALL_TESTS) PATTERN ".pytest_cache" EXCLUDE ) endif () + + if (GKFS_BUILD_FUSE) + install(DIRECTORY fuse + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/integration + FILES_MATCHING + REGEX ".*\\.py" + PATTERN "__pycache__" EXCLUDE + PATTERN ".pytest_cache" EXCLUDE + ) + endif () endif () -- GitLab From a2cef5056a2687645486d8525e912d0fa2b31baf Mon Sep 17 00:00:00 2001 From: sevenuz Date: Fri, 15 Aug 2025 11:04:17 +0200 Subject: [PATCH 18/32] fix fuse fixture for CI? --- src/client/CMakeLists.txt | 1 - tests/integration/harness/gkfs.py | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index b685d4a56..ed6b57004 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -121,7 +121,6 @@ target_link_libraries(fuse_client PRIVATE gkfs_common metadata distributor env_util arithmetic path_util rpc_utils ${FUSE3_LIBRARIES} gkfs_user_lib - gkfs_libc_intercept ) target_include_directories(fuse_client diff --git a/tests/integration/harness/gkfs.py b/tests/integration/harness/gkfs.py index ccbeff673..6b906c587 100644 --- a/tests/integration/harness/gkfs.py +++ b/tests/integration/harness/gkfs.py @@ -1683,11 +1683,15 @@ class FuseClient: def shutdown(self): try: self._proc.terminate() + time.sleep(1) # give fuse time to unmount err = self._proc.wait() + except ProcessLookupError: + print("Fuse client already gone at shutdown") + pass except sh.SignalException_SIGTERM: pass except Exception: - raise + pass @property -- GitLab From 05fe6d53d072c2426aa3ff930b850af87cb23d11 Mon Sep 17 00:00:00 2001 From: sevenuz Date: Wed, 20 Aug 2025 12:56:01 +0200 Subject: [PATCH 19/32] fix create error on distributed lookup --- src/client/fuse/fuse_client.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index f3639d1e0..61fe681e7 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -324,15 +324,16 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, } std::string path = get_path(parent_inode, name); int rc = gkfs::syscall::gkfs_create(path, mode); + int errno_bu = errno; if(rc == -1) { - fuse_log(FUSE_LOG_DEBUG, "here here mode %i flags %i \n", mode, - fi->flags); - fuse_reply_err(req, 1); - return; + fuse_log(FUSE_LOG_DEBUG, + "create failed, here here mode %i flags %i errno %i\n", mode, + fi->flags, errno); } int fd = gkfs::syscall::gkfs_open(path, mode, fi->flags); if(fd < 0) { - fuse_reply_err(req, ENOENT); + fuse_log(FUSE_LOG_DEBUG, "create -> open failed errno\n", errno); + fuse_reply_err(req, rc < 0 ? errno_bu : ENOENT); return; } fi->fh = fd; @@ -340,7 +341,7 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, int sc = gkfs::syscall::gkfs_stat(path, &st); if(sc == -1) { fuse_log(FUSE_LOG_DEBUG, "thats why its not allowed \n"); - fuse_reply_err(req, 1); + fuse_reply_err(req, ENOENT); return; } fuse_ino_t ino = alloc_inode(path); -- GitLab From eecede825f70d3497def0c51041dddb2894c1918 Mon Sep 17 00:00:00 2001 From: sevenuz Date: Wed, 20 Aug 2025 18:41:32 +0200 Subject: [PATCH 20/32] fix read and write for large sizes --- src/client/fuse/fuse_client.cpp | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 61fe681e7..9f0877730 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -273,14 +273,10 @@ read_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, return; } std::vector buf(size); - int lc = gkfs::syscall::gkfs_lseek(fi->fh, off, SEEK_SET); - if(lc < 0) { - fuse_reply_err(req, 1); - return; - } - int rc = gkfs::syscall::gkfs_read(fi->fh, buf.data(), size); + int rc = gkfs::syscall::gkfs_pread(fi->fh, buf.data(), size, off); if(rc < 0) { - fuse_reply_err(req, 1); + fuse_log(FUSE_LOG_DEBUG, "read fail \n"); + fuse_reply_err(req, errno); return; } fuse_reply_buf(req, buf.data(), rc); @@ -295,18 +291,10 @@ write_handler(fuse_req_t req, fuse_ino_t ino, const char* buf, size_t size, fuse_reply_err(req, ENOENT); return; } - int lc = gkfs::syscall::gkfs_lseek(fi->fh, off, SEEK_SET); - if(lc < 0) { - fuse_reply_err(req, 1); - return; - } - int rc = gkfs::syscall::gkfs_write(fi->fh, buf, size); - if(rc < 0) { - fuse_reply_err(req, 1); - return; - } + int rc = gkfs::syscall::gkfs_pwrite(fi->fh, buf, size, off); if(rc < 0) { - fuse_reply_err(req, 1); + fuse_log(FUSE_LOG_DEBUG, "write fail \n"); + fuse_reply_err(req, errno); return; } fuse_reply_write(req, rc); @@ -1021,5 +1009,5 @@ main(int argc, char* argv[]) { } fuse_session_unmount(se); - return ret ? 1 : 0; + return ret < 0 ? 1 : 0; } -- GitLab From 0741930b6592897a83b94175310e52b94dbcdb9a Mon Sep 17 00:00:00 2001 From: sevenuz Date: Mon, 1 Sep 2025 16:55:51 +0200 Subject: [PATCH 21/32] readahead and direct io as client options --- include/client/fuse/fuse_client.hpp | 4 +++- src/client/fuse/fuse_client.cpp | 23 ++++++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/include/client/fuse/fuse_client.hpp b/include/client/fuse/fuse_client.hpp index 3a611c28e..beec55ef5 100644 --- a/include/client/fuse/fuse_client.hpp +++ b/include/client/fuse/fuse_client.hpp @@ -115,9 +115,11 @@ struct u_data { pthread_mutex_t mutex; int debug; int writeback; + int direct_io; + int max_readahead; int flock; int xattr; - char* source; + char* mountpoint; double timeout; int cache; int timeout_set; diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 9f0877730..73c6d66dc 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -81,6 +81,9 @@ get_path(const Inode* inode, const char* name) { static const struct fuse_opt lo_opts[] = { {"writeback", offsetof(struct u_data, writeback), 1}, {"no_writeback", offsetof(struct u_data, writeback), 0}, + {"direct_io", offsetof(struct u_data, direct_io), 1}, + {"no_direct_io", offsetof(struct u_data, direct_io), 0}, + {"max_readahead=%ui", offsetof(struct u_data, max_readahead), 0}, {"flock", offsetof(struct u_data, flock), 1}, {"no_flock", offsetof(struct u_data, flock), 0}, {"xattr", offsetof(struct u_data, xattr), 1}, @@ -97,7 +100,9 @@ static void passthrough_ll_help(void) { printf(" -o writeback Enable writeback\n" " -o no_writeback Disable write back\n" - " -o source=/home/dir Source directory to be mounted\n" + " -o direct_io Enables direct io\n" + " -o no_direct_io Disable direct io\n" + " -o max_readahead=1 Amount of allowed readaheads\n" " -o flock Enable flock\n" " -o no_flock Disable flock\n" " -o xattr Enable xattr\n" @@ -111,8 +116,8 @@ passthrough_ll_help(void) { static void init_handler(void* userdata, struct fuse_conn_info* conn) { - fuse_log(FUSE_LOG_DEBUG, "init handler \n"); - // struct u_data* lo = (struct u_data*) userdata; + struct u_data* ud = (struct u_data*) userdata; + fuse_log(FUSE_LOG_DEBUG, "init handler readahead %i direct_io %i \n", ud->max_readahead, ud->direct_io); // bool has_flag; // TODO check other capabilities e.g. FUSE_CAP_READDIRPLUS @@ -130,6 +135,7 @@ init_handler(void* userdata, struct fuse_conn_info* conn) { /* Disable the receiving and processing of FUSE_INTERRUPT requests */ // conn->no_interrupt = 1; + conn->max_readahead = ud->max_readahead; } static void @@ -235,6 +241,7 @@ setattr_handler(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, static void open_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { fuse_log(FUSE_LOG_DEBUG, "open handler \n"); + auto* ud = udata(req); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); @@ -248,6 +255,7 @@ open_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { return; } fi->fh = fd; // TODO file handle == file descriptor? + fi->direct_io = ud->direct_io; fuse_reply_open(req, fi); } @@ -325,6 +333,7 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, return; } fi->fh = fd; + fi->direct_io = ud->direct_io; struct stat st; int sc = gkfs::syscall::gkfs_stat(path, &st); if(sc == -1) { @@ -665,10 +674,10 @@ symlink_handler(fuse_req_t req, const char* linkname, fuse_ino_t parent, std::string path = get_path(parent_inode, name); std::string target = get_path(parent_inode, linkname); - if(target.rfind(ud->source, 0) == 0) { + if(target.rfind(ud->mountpoint, 0) == 0) { // starts with mount path target = get_path(parent_inode, - target.substr(strlen(ud->source)).c_str()); + target.substr(strlen(ud->mountpoint)).c_str()); } fuse_log(FUSE_LOG_DEBUG, "mk symlink path %s target %s\n", path.c_str(), @@ -879,7 +888,7 @@ init_ll_ops(fuse_lowlevel_ops* ops) { void err_cleanup1(fuse_cmdline_opts opts, fuse_args& args, struct u_data& ud) { free(opts.mountpoint); - free(ud.source); + free(ud.mountpoint); fuse_opt_free_args(&args); std::cout << "# Resources released" << std::endl; } @@ -964,7 +973,7 @@ main(int argc, char* argv[]) { } - ud.source = strdup(opts.mountpoint); + ud.mountpoint = strdup(opts.mountpoint); init_gekkofs(); -- GitLab From f0bc1d9a4bf3226e72ab7c5d658ca5ca27bb72f5 Mon Sep 17 00:00:00 2001 From: sevenuz Date: Tue, 2 Sep 2025 11:38:56 +0200 Subject: [PATCH 22/32] fuse: fifo support for local nodes --- include/client/fuse/fuse_client.hpp | 5 +- src/client/fuse/fuse_client.cpp | 151 ++++++++++++++++++++++++---- 2 files changed, 138 insertions(+), 18 deletions(-) diff --git a/include/client/fuse/fuse_client.hpp b/include/client/fuse/fuse_client.hpp index beec55ef5..1f7e6f88c 100644 --- a/include/client/fuse/fuse_client.hpp +++ b/include/client/fuse/fuse_client.hpp @@ -76,7 +76,9 @@ struct __dirstream { #include #include #include +#include #include +#include #include #include #include @@ -117,7 +119,8 @@ struct u_data { int writeback; int direct_io; int max_readahead; - int flock; + int fifo; + int access; int xattr; char* mountpoint; double timeout; diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 73c6d66dc..47ca974db 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -43,7 +43,9 @@ static struct fuse_lowlevel_ops ll_ops; static std::mutex ino_mutex; static std::unordered_map ino_map; static std::unordered_map path_map; +static std::unordered_map local_fifos; static fuse_ino_t next_ino = 2; // reserve 1 for root +static const std::string fifo_path = "/tmp/gekkofs_fifos/"; static fuse_ino_t alloc_inode(const std::string& path) { @@ -84,8 +86,10 @@ static const struct fuse_opt lo_opts[] = { {"direct_io", offsetof(struct u_data, direct_io), 1}, {"no_direct_io", offsetof(struct u_data, direct_io), 0}, {"max_readahead=%ui", offsetof(struct u_data, max_readahead), 0}, - {"flock", offsetof(struct u_data, flock), 1}, - {"no_flock", offsetof(struct u_data, flock), 0}, + {"fifo", offsetof(struct u_data, fifo), 1}, + {"no_fifo", offsetof(struct u_data, fifo), 0}, + {"access", offsetof(struct u_data, access), 1}, + {"no_access", offsetof(struct u_data, access), 0}, {"xattr", offsetof(struct u_data, xattr), 1}, {"no_xattr", offsetof(struct u_data, xattr), 0}, {"timeout=%lf", offsetof(struct u_data, timeout), 0}, @@ -103,8 +107,10 @@ passthrough_ll_help(void) { " -o direct_io Enables direct io\n" " -o no_direct_io Disable direct io\n" " -o max_readahead=1 Amount of allowed readaheads\n" - " -o flock Enable flock\n" - " -o no_flock Disable flock\n" + " -o fifo Enable fifo, disables GekkoFS access check\n" + " -o no_fifo Disable fifo\n" + " -o access Enable GekkoFS access check if fifo is deactivated\n" + " -o no_access Disable access handler\n" " -o xattr Enable xattr\n" " -o no_xattr Disable xattr\n" " -o timeout=1.0 Caching timeout\n" @@ -117,7 +123,8 @@ passthrough_ll_help(void) { static void init_handler(void* userdata, struct fuse_conn_info* conn) { struct u_data* ud = (struct u_data*) userdata; - fuse_log(FUSE_LOG_DEBUG, "init handler readahead %i direct_io %i \n", ud->max_readahead, ud->direct_io); + fuse_log(FUSE_LOG_DEBUG, "init handler readahead %i direct_io %i \n", + ud->max_readahead, ud->direct_io); // bool has_flag; // TODO check other capabilities e.g. FUSE_CAP_READDIRPLUS @@ -156,6 +163,21 @@ lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { std::string child = get_path(parent_inode, name); fuse_log(FUSE_LOG_DEBUG, "lookup %s\n", child.c_str()); + if(ud->fifo) { + auto iit = local_fifos.find(child); + if(iit != local_fifos.end()) { + const struct stat& st = iit->second; + + fuse_entry_param e{}; + e.ino = st.st_ino; + e.attr = st; + e.attr_timeout = ud->timeout; + e.entry_timeout = ud->timeout; + + fuse_reply_entry(req, &e); + return; + } + } // See if we already have this path auto it = path_map.find(child); @@ -366,12 +388,25 @@ tmpfile_handler(fuse_req_t req, fuse_ino_t parent, mode_t mode, static void unlink_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { fuse_log(FUSE_LOG_DEBUG, "unlink handler \n"); + auto* ud = udata(req); auto* parent_inode = get_inode(parent); if(!parent_inode) { fuse_reply_err(req, ENOENT); return; } std::string path = get_path(parent_inode, name); + + if(ud->fifo) { + auto it = local_fifos.find(path); + if(it != local_fifos.end()) { + std::string real = fifo_path + std::string(name); + unlink(real.c_str()); + local_fifos.erase(it); + fuse_reply_err(req, 0); + return; + } + } + int rc = gkfs::syscall::gkfs_remove(path); if(rc == -1) { fuse_reply_err(req, 1); @@ -429,6 +464,7 @@ static void readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info* fi) { fuse_log(FUSE_LOG_DEBUG, "readdir handler \n"); + auto* ud = udata(req); auto* dir_ptr = reinterpret_cast(fi->fh); if(!dir_ptr) { fuse_reply_err(req, EBADF); @@ -458,7 +494,7 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, auto de = open_dir->getdent(pos); struct stat st{}; - // TODO cannot be right, right? + // TODO cannot be right, right? no stat? st.st_ino = std::hash()(open_dir->path() + "/" + de.name()); st.st_mode = (de.type() == gkfs::filemap::FileType::regular) ? S_IFREG @@ -475,6 +511,34 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, pos += 1; } + if(ud->fifo) { + int i = 0; + for(auto const& kv : local_fifos) { + if(i < off) { + i++; + continue; + } + std::filesystem::path vpath = kv.first; + const struct stat& st = kv.second; + + // check if FIFO belongs to this directory + if(vpath.parent_path() != open_dir->path()) + continue; + + std::string fname = vpath.filename(); + + size_t entry_size = fuse_add_direntry(req, buf + bytes_filled, + size - bytes_filled, + fname.c_str(), &st, pos + 1); + + if(entry_size > size - bytes_filled) + break; + + bytes_filled += entry_size; + pos += 1; + } + } + open_dir->pos(pos); // update internal position if needed fuse_reply_buf(req, buf, bytes_filled); @@ -572,17 +636,24 @@ fsync_handler(fuse_req_t req, fuse_ino_t ino, int datasync, static void access_handler(fuse_req_t req, fuse_ino_t ino, int mask) { fuse_log(FUSE_LOG_DEBUG, "access handler \n"); - auto* inode = get_inode(ino); - if(!inode) { - fuse_reply_err(req, ENOENT); - return; - } - int lc = gkfs::syscall::gkfs_access(inode->path, mask, true); - if(lc < 0) { - fuse_reply_err(req, 1); - return; + auto* ud = udata(req); + if(ud->access && !ud->fifo) { + auto* inode = get_inode(ino); + if(!inode) { + fuse_reply_err(req, ENOENT); + return; + } + int lc = gkfs::syscall::gkfs_access(inode->path, mask, true); + if(lc < 0) { + fuse_reply_err(req, 1); + return; + } + fuse_reply_err(req, 0); + } else { + // deactivates following access requests and it is always treated as + // success + fuse_reply_err(req, ENOSYS); } - fuse_reply_err(req, 0); } static void @@ -795,6 +866,52 @@ rename_handler(fuse_req_t req, fuse_ino_t old_parent, const char* old_name, #endif } +void +mknod_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, + dev_t rdev) { + auto* ud = udata(req); + if(!ud->fifo) { + fuse_reply_err(req, ENOTSUP); + return; + } + auto* parent_inode = get_inode(parent); + if(!parent_inode) { + fuse_reply_err(req, ENOENT); + return; + } + + mkdir(fifo_path.c_str(), 0700); + std::string path = fifo_path + "/" + name; + + if(!S_ISFIFO(mode)) { + fuse_reply_err(req, EINVAL); + return; + } + + if(mkfifo(path.c_str(), mode) == -1) { + fuse_reply_err(req, errno); + return; + } + + struct stat st{}; + if(stat(path.c_str(), &st) == -1) { + fuse_reply_err(req, errno); + return; + } + + // save synthetic entry + std::string vpath = get_path(parent_inode, name); // path in mount namespace + local_fifos[vpath] = st; + + fuse_entry_param e{}; + e.ino = st.st_ino; + e.attr = st; + e.attr_timeout = ud->timeout; + e.entry_timeout = ud->timeout; + + fuse_reply_entry(req, &e); +} + static void init_gekkofs() { // TODO how to handle mount point @@ -834,7 +951,7 @@ init_ll_ops(fuse_lowlevel_ops* ops) { ops->forget = forget_handler; // ops->forget_multi ops->readlink = readlink_handler; - // ops->mknod + ops->mknod = mknod_handler; ops->symlink = symlink_handler; ops->rename = rename_handler; // ops->link -- GitLab From 49abb1d3e14aa7be8c5e8594b1a20b5f863a8a23 Mon Sep 17 00:00:00 2001 From: Julius Athenstaedt Date: Mon, 22 Sep 2025 11:18:18 +0200 Subject: [PATCH 23/32] fix error code in rmdir handler --- src/client/fuse/fuse_client.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 47ca974db..50c338d8c 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -374,15 +374,6 @@ 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 @@ -701,7 +692,7 @@ rmdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { std::string path = get_path(parent_inode, name); int rc = gkfs::syscall::gkfs_rmdir(path); if(rc == -1) { - fuse_reply_err(req, 1); + fuse_reply_err(req, errno); return; } fuse_reply_err(req, 0); @@ -987,7 +978,7 @@ init_ll_ops(fuse_lowlevel_ops* ops) { // misc ops->init = init_handler; ops->destroy = destroy_handler; - ops->tmpfile = tmpfile_handler; + // ops->tmpfile // not supported in fuse < 3 // ops->statfs = nullptr; // ops->flock -- GitLab From a23c8340a29014661792de6eb47321fc062de637 Mon Sep 17 00:00:00 2001 From: Julius Athenstaedt Date: Mon, 22 Sep 2025 14:01:22 +0200 Subject: [PATCH 24/32] improve logging in create handler --- src/client/fuse/fuse_client.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 50c338d8c..39a5bad4b 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -333,7 +333,6 @@ write_handler(fuse_req_t req, fuse_ino_t ino, const char* buf, size_t size, static void create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "create handler \n"); auto* ud = udata(req); auto* parent_inode = get_inode(parent); if(!parent_inode) { @@ -341,6 +340,7 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, return; } std::string path = get_path(parent_inode, name); + fuse_log(FUSE_LOG_DEBUG, "create handler %s\n", path.c_str()); int rc = gkfs::syscall::gkfs_create(path, mode); int errno_bu = errno; if(rc == -1) { @@ -359,7 +359,6 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, struct stat st; int sc = gkfs::syscall::gkfs_stat(path, &st); if(sc == -1) { - fuse_log(FUSE_LOG_DEBUG, "thats why its not allowed \n"); fuse_reply_err(req, ENOENT); return; } -- GitLab From 8d69fc3e1ab1f1fe4a06176d614528fb620c3d15 Mon Sep 17 00:00:00 2001 From: Julius Athenstaedt Date: Thu, 25 Sep 2025 17:17:02 +0200 Subject: [PATCH 25/32] fuse: create direct with open and inode cleanup in rmdir and unlink --- src/client/fuse/fuse_client.cpp | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 39a5bad4b..1db717d13 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -341,17 +341,17 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, } std::string path = get_path(parent_inode, name); fuse_log(FUSE_LOG_DEBUG, "create handler %s\n", path.c_str()); - int rc = gkfs::syscall::gkfs_create(path, mode); - int errno_bu = errno; - if(rc == -1) { - fuse_log(FUSE_LOG_DEBUG, - "create failed, here here mode %i flags %i errno %i\n", mode, - fi->flags, errno); - } - int fd = gkfs::syscall::gkfs_open(path, mode, fi->flags); + // int rc = gkfs::syscall::gkfs_create(path, mode); + // int errno_bu = errno; + // if(rc == -1) { + // fuse_log(FUSE_LOG_DEBUG, + // "create failed, here here mode %i flags %i errno %i\n", mode, + // fi->flags, errno); + // } + int fd = gkfs::syscall::gkfs_open(path, mode, fi->flags | O_CREAT); if(fd < 0) { - fuse_log(FUSE_LOG_DEBUG, "create -> open failed errno\n", errno); - fuse_reply_err(req, rc < 0 ? errno_bu : ENOENT); + fuse_log(FUSE_LOG_DEBUG, "create -> open failed errno %i\n", errno); + fuse_reply_err(req, errno); return; } fi->fh = fd; @@ -398,6 +398,11 @@ 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); + } if(rc == -1) { fuse_reply_err(req, 1); return; @@ -689,11 +694,17 @@ rmdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { return; } std::string path = get_path(parent_inode, name); + fuse_log(FUSE_LOG_DEBUG, "rmdir %s\n", path.c_str()); int rc = gkfs::syscall::gkfs_rmdir(path); if(rc == -1) { 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); + } fuse_reply_err(req, 0); } -- GitLab From a80056abc08cb84347cec6daefa119d595a5026a Mon Sep 17 00:00:00 2001 From: Julius Athenstaedt Date: Thu, 25 Sep 2025 18:18:19 +0200 Subject: [PATCH 26/32] debugging the remove dir problem --- src/client/fuse/fuse_client.cpp | 7 ------- src/client/gkfs_functions.cpp | 8 ++++++++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 1db717d13..0a224a281 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -341,13 +341,6 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, } std::string path = get_path(parent_inode, name); fuse_log(FUSE_LOG_DEBUG, "create handler %s\n", path.c_str()); - // int rc = gkfs::syscall::gkfs_create(path, mode); - // int errno_bu = errno; - // if(rc == -1) { - // fuse_log(FUSE_LOG_DEBUG, - // "create failed, here here mode %i flags %i errno %i\n", mode, - // fi->flags, errno); - // } int fd = gkfs::syscall::gkfs_open(path, mode, fi->flags | O_CREAT); if(fd < 0) { fuse_log(FUSE_LOG_DEBUG, "create -> open failed errno %i\n", errno); diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index b12bfe073..a969abe48 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -1558,6 +1558,14 @@ gkfs_rmdir(const std::string& path) { assert(ret.second); auto open_dir = ret.second; if(open_dir->size() != 2) { + std::cout << "hier kommt der schuh in die tonne" << std::endl; + size_t pos = 0; + while(pos < open_dir->size()) { + auto de = open_dir->getdent(pos); + std::cout << de.name() << std::endl; + pos++; + } + std::cout << "booom" << std::endl; errno = ENOTEMPTY; return -1; } -- GitLab From 876ccb133db9369729d7f5cda2f228a17064625b Mon Sep 17 00:00:00 2001 From: Julius Athenstaedt Date: Fri, 26 Sep 2025 10:45:58 +0200 Subject: [PATCH 27/32] maybe fix duplicated . and .. in dentries --- src/client/rpc/forward_metadata.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/rpc/forward_metadata.cpp b/src/client/rpc/forward_metadata.cpp index 7a7527bee..d22842d14 100644 --- a/src/client/rpc/forward_metadata.cpp +++ b/src/client/rpc/forward_metadata.cpp @@ -780,9 +780,6 @@ forward_get_dirents(const string& path) { bool* bool_ptr = reinterpret_cast(base_ptr); char* names_ptr = reinterpret_cast(base_ptr) + (out.dirents_size() * sizeof(bool)); - // Add special files like an standard fs. - open_dir->add(".", gkfs::filemap::FileType::directory); - open_dir->add("..", gkfs::filemap::FileType::directory); for(std::size_t j = 0; j < out.dirents_size(); j++) { gkfs::filemap::FileType ftype = @@ -804,6 +801,9 @@ forward_get_dirents(const string& path) { open_dir->add(name, ftype); } } + // Add special files like an standard fs. + open_dir->add(".", gkfs::filemap::FileType::directory); + open_dir->add("..", gkfs::filemap::FileType::directory); return make_pair(err, open_dir); } -- GitLab From 1dd22d09955d3b2624806e41dc39a2b664398fd4 Mon Sep 17 00:00:00 2001 From: Julius Athenstaedt Date: Fri, 26 Sep 2025 11:41:10 +0200 Subject: [PATCH 28/32] feature flag for writeback --- src/client/fuse/fuse_client.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 0a224a281..77a3edee3 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -125,14 +125,20 @@ init_handler(void* userdata, struct fuse_conn_info* conn) { struct u_data* ud = (struct u_data*) userdata; fuse_log(FUSE_LOG_DEBUG, "init handler readahead %i direct_io %i \n", ud->max_readahead, ud->direct_io); - // bool has_flag; // TODO check other capabilities e.g. FUSE_CAP_READDIRPLUS - // if(lo->writeback) { - // has_flag = fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE); - // if(lo->debug && has_flag) - // fuse_log(FUSE_LOG_DEBUG, "init_handler: activating writeback\n"); - // } + if(ud->writeback) { +#if FUSE_MAJOR_VERSION > 3 || \ + (FUSE_MAJOR_VERSION == 3 && FUSE_MINOR_VERSION >= 12) + fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE); +#else + // for older fuse versions like on the ubuntu22 + conn->want |= FUSE_CAP_WRITEBACK_CACHE; +#endif + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, + "init_handler: try to activate writeback\n"); + } // if(lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { // has_flag = fuse_set_feature_flag(conn, FUSE_CAP_FLOCK_LOCKS); // if(lo->debug && has_flag) -- GitLab From 8b9581d94433bbfca48b17475b52004b9c33cde1 Mon Sep 17 00:00:00 2001 From: Julius Athenstaedt Date: Fri, 26 Sep 2025 14:30:32 +0200 Subject: [PATCH 29/32] cleanup debug prints --- src/client/gkfs_functions.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index a969abe48..b12bfe073 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -1558,14 +1558,6 @@ gkfs_rmdir(const std::string& path) { assert(ret.second); auto open_dir = ret.second; if(open_dir->size() != 2) { - std::cout << "hier kommt der schuh in die tonne" << std::endl; - size_t pos = 0; - while(pos < open_dir->size()) { - auto de = open_dir->getdent(pos); - std::cout << de.name() << std::endl; - pos++; - } - std::cout << "booom" << std::endl; errno = ENOTEMPTY; return -1; } -- GitLab From 5f9e76292f9107a5074ef318ad7db8e3bb4c8f40 Mon Sep 17 00:00:00 2001 From: Julius Athenstaedt Date: Fri, 26 Sep 2025 17:09:26 +0200 Subject: [PATCH 30/32] debug messages only with debug flag --- src/client/fuse/fuse_client.cpp | 142 ++++++++++++++++++++++---------- 1 file changed, 97 insertions(+), 45 deletions(-) diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 77a3edee3..50e8f2de9 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -123,8 +123,9 @@ passthrough_ll_help(void) { static void init_handler(void* userdata, struct fuse_conn_info* conn) { struct u_data* ud = (struct u_data*) userdata; - fuse_log(FUSE_LOG_DEBUG, "init handler readahead %i direct_io %i \n", - ud->max_readahead, ud->direct_io); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "init handler readahead %i direct_io %i \n", + ud->max_readahead, ud->direct_io); // TODO check other capabilities e.g. FUSE_CAP_READDIRPLUS if(ud->writeback) { @@ -153,21 +154,25 @@ init_handler(void* userdata, struct fuse_conn_info* conn) { static void destroy_handler(void* userdata) { - fuse_log(FUSE_LOG_DEBUG, "destroy handler \n"); + struct u_data* ud = (struct u_data*) userdata; + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "destroy handler \n"); // userdata is GekkoFuse* if passed } static void lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { - fuse_log(FUSE_LOG_DEBUG, "lookup handler ino %u\n", parent); auto* ud = udata(req); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "lookup handler ino %u\n", parent); auto* parent_inode = get_inode(parent); if(!parent_inode) { fuse_reply_err(req, ENOENT); return; } std::string child = get_path(parent_inode, name); - fuse_log(FUSE_LOG_DEBUG, "lookup %s\n", child.c_str()); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "lookup %s\n", child.c_str()); if(ud->fifo) { auto iit = local_fifos.find(child); @@ -212,8 +217,10 @@ lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { static void getattr_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "getattr handler \n"); auto* ud = udata(req); + if(ud->debug) { + fuse_log(FUSE_LOG_DEBUG, "getattr handler \n"); + } auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); @@ -223,7 +230,8 @@ getattr_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { struct stat st; int rc = gkfs::syscall::gkfs_stat(inode->path, &st); if(rc) { - fuse_log(FUSE_LOG_DEBUG, "getattr error %u\n", rc); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "getattr error %u\n", rc); fuse_reply_err(req, ENOENT); return; } @@ -234,8 +242,9 @@ 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 ino %u\n", ino); auto* ud = udata(req); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "setattr handler ino %u\n", ino); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); @@ -246,8 +255,9 @@ setattr_handler(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, 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()); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "setattr truncate failed on %s\n", + inode->path.c_str()); fuse_reply_err(req, EIO); return; } @@ -268,8 +278,9 @@ setattr_handler(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, static void open_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "open handler \n"); auto* ud = udata(req); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "open handler \n"); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); @@ -290,7 +301,9 @@ open_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { static void lseek_handler(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "lseek handler \n"); + auto* ud = udata(req); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "lseek handler \n"); int lc = gkfs::syscall::gkfs_lseek(fi->fh, off, whence); if(lc < 0) { fuse_reply_err(req, 1); @@ -302,7 +315,9 @@ lseek_handler(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, static void read_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "read handler \n"); + auto* ud = udata(req); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "read handler \n"); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); @@ -311,7 +326,8 @@ read_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, std::vector buf(size); int rc = gkfs::syscall::gkfs_pread(fi->fh, buf.data(), size, off); if(rc < 0) { - fuse_log(FUSE_LOG_DEBUG, "read fail \n"); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "read fail \n"); fuse_reply_err(req, errno); return; } @@ -321,7 +337,9 @@ read_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, static void write_handler(fuse_req_t req, fuse_ino_t ino, const char* buf, size_t size, off_t off, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "write handler \n"); + auto* ud = udata(req); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "write handler \n"); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); @@ -329,7 +347,8 @@ write_handler(fuse_req_t req, fuse_ino_t ino, const char* buf, size_t size, } int rc = gkfs::syscall::gkfs_pwrite(fi->fh, buf, size, off); if(rc < 0) { - fuse_log(FUSE_LOG_DEBUG, "write fail \n"); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "write fail \n"); fuse_reply_err(req, errno); return; } @@ -346,10 +365,12 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, return; } std::string path = get_path(parent_inode, name); - fuse_log(FUSE_LOG_DEBUG, "create handler %s\n", path.c_str()); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "create handler %s\n", path.c_str()); int fd = gkfs::syscall::gkfs_open(path, mode, fi->flags | O_CREAT); if(fd < 0) { - fuse_log(FUSE_LOG_DEBUG, "create -> open failed errno %i\n", errno); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "create -> open failed errno %i\n", errno); fuse_reply_err(req, errno); return; } @@ -362,7 +383,8 @@ 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); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "create new inode ino %i\n", ino); ino_map[ino].st = st; fuse_entry_param e = {}; e.ino = ino; @@ -376,8 +398,9 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, /// problem? static void unlink_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { - fuse_log(FUSE_LOG_DEBUG, "unlink handler \n"); auto* ud = udata(req); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "unlink handler \n"); auto* parent_inode = get_inode(parent); if(!parent_inode) { fuse_reply_err(req, ENOENT); @@ -411,14 +434,17 @@ unlink_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { static void opendir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "opendir handler \n"); + auto* ud = udata(req); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "opendir handler \n"); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOTDIR); return; } struct stat st; - fuse_log(FUSE_LOG_DEBUG, "open dir %s \n", inode->path.c_str()); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "open dir %s \n", inode->path.c_str()); if(gkfs::syscall::gkfs_stat(inode->path, &st) != 0 || S_ISREG(st.st_mode)) { fuse_reply_err(req, ENOTDIR); return; @@ -457,8 +483,9 @@ opendir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { static void readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "readdir handler \n"); auto* ud = udata(req); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "readdir handler \n"); auto* dir_ptr = reinterpret_cast(fi->fh); if(!dir_ptr) { fuse_reply_err(req, EBADF); @@ -467,7 +494,8 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, auto open_dir = CTX->file_map()->get_dir(dir_ptr->fd); - fuse_log(FUSE_LOG_DEBUG, "read dir %s \n", open_dir->path().c_str()); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "read dir %s \n", open_dir->path().c_str()); if(open_dir == nullptr) { fuse_reply_err(req, EBADF); @@ -541,7 +569,9 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, static void releasedir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "releasedir handler \n"); + auto* ud = udata(req); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "releasedir handler \n"); GkfsDir* dir_ptr = reinterpret_cast(fi->fh); if(CTX->interception_enabled() && CTX->file_map()->exist(dir_ptr->fd)) { int fd = dir_ptr->fd; @@ -560,27 +590,34 @@ releasedir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { /// releases file descriptor, not connected to lookup_count static void release_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { - fuse_log(FUSE_LOG_DEBUG, "release handler \n"); + auto* ud = udata(req); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "release handler \n"); auto* inode = get_inode(ino); if(!inode) { - fuse_log(FUSE_LOG_DEBUG, "release here \n"); + if(ud->debug) + 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"); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "release there \n"); fuse_reply_err(req, 1); return; } - fuse_log(FUSE_LOG_DEBUG, "release success \n"); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "release success \n"); fuse_reply_err(req, 0); } /// decrement lookup count static void forget_handler(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { - fuse_log(FUSE_LOG_DEBUG, "forget handler \n"); + auto* ud = udata(req); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "forget handler \n"); auto it = ino_map.find(ino); if(it == ino_map.end()) { @@ -597,7 +634,8 @@ forget_handler(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { if(inode.lookup_count == 0) { // && inode.open_count == 0 path_map.erase(inode.path); ino_map.erase(it); - fuse_log(FUSE_LOG_DEBUG, "reached lookup_count 0 \n"); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "reached lookup_count 0 \n"); } fuse_reply_none(req); @@ -606,7 +644,9 @@ forget_handler(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { 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* ud = udata(req); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "flush handler \n"); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); @@ -629,8 +669,9 @@ fsync_handler(fuse_req_t req, fuse_ino_t ino, int datasync, static void access_handler(fuse_req_t req, fuse_ino_t ino, int mask) { - fuse_log(FUSE_LOG_DEBUG, "access handler \n"); auto* ud = udata(req); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "access handler \n"); if(ud->access && !ud->fifo) { auto* inode = get_inode(ino); if(!inode) { @@ -660,8 +701,9 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name, return; } std::string path = get_path(parent_inode, name); - fuse_log(FUSE_LOG_DEBUG, "mkdir parent %s name %s\n", - parent_inode->path.c_str(), name); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "mkdir parent %s name %s\n", + parent_inode->path.c_str(), name); int rc = gkfs::syscall::gkfs_create(path, mode | S_IFDIR); if(rc == -1) { fuse_reply_err(req, 1); @@ -670,12 +712,14 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name, struct stat st; int sc = gkfs::syscall::gkfs_stat(path, &st); if(sc == -1) { - fuse_log(FUSE_LOG_DEBUG, "thats why its not allowed \n"); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "thats why its not allowed \n"); fuse_reply_err(req, 1); return; } fuse_ino_t ino = alloc_inode(path); - fuse_log(FUSE_LOG_DEBUG, "create new inode ino %i\n", ino); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "create new inode ino %i\n", ino); ino_map[ino].st = st; fuse_entry_param e = {}; e.ino = ino; @@ -687,13 +731,15 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name, static void rmdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { + auto* ud = udata(req); auto* parent_inode = get_inode(parent); if(!parent_inode) { fuse_reply_err(req, ENOENT); return; } std::string path = get_path(parent_inode, name); - fuse_log(FUSE_LOG_DEBUG, "rmdir %s\n", path.c_str()); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "rmdir %s\n", path.c_str()); int rc = gkfs::syscall::gkfs_rmdir(path); if(rc == -1) { fuse_reply_err(req, errno); @@ -732,12 +778,14 @@ 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); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "symlink handler linkname %s name %s\n", + linkname, name); auto* parent_inode = get_inode(parent); if(!parent_inode) { - fuse_log(FUSE_LOG_DEBUG, "symlink parent inode ino %i\n", parent); + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "symlink parent inode ino %i\n", parent); fuse_reply_err(req, ENOENT); return; } @@ -751,8 +799,9 @@ symlink_handler(fuse_req_t req, const char* linkname, fuse_ino_t parent, target.substr(strlen(ud->mountpoint)).c_str()); } - fuse_log(FUSE_LOG_DEBUG, "mk symlink path %s target %s\n", path.c_str(), - target.c_str()); + if(ud->debug) + 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); @@ -762,11 +811,14 @@ symlink_handler(fuse_req_t req, const char* linkname, fuse_ino_t parent, // 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"); + if(ud->debug) + 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); + if(ud->debug) + 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 -- GitLab From 3a02345f4bb47a2389f96c9ca2f782ade0259a12 Mon Sep 17 00:00:00 2001 From: Julius Athenstaedt Date: Fri, 3 Oct 2025 16:27:39 +0200 Subject: [PATCH 31/32] remove DIR* dependency from directory handlers --- include/client/fuse/fuse_client.hpp | 13 -------- src/client/fuse/fuse_client.cpp | 47 +++++------------------------ 2 files changed, 7 insertions(+), 53 deletions(-) diff --git a/include/client/fuse/fuse_client.hpp b/include/client/fuse/fuse_client.hpp index 1f7e6f88c..7c9e255a1 100644 --- a/include/client/fuse/fuse_client.hpp +++ b/include/client/fuse/fuse_client.hpp @@ -45,19 +45,6 @@ extern "C" { #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12) #include - -// 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 - size_t allocation; // Space allocated for the block. - size_t size; // Total valid data in the block. - size_t offset; // Current offset into the block. - off_t filepos; // Position of next entry to read. - // Directory block. - char* path; - char data[1] __attribute__((aligned(__alignof__(void*)))); -}; // Originally its 0, but C++ does not permit it } #include diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 50e8f2de9..77023d912 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -442,41 +442,20 @@ opendir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { fuse_reply_err(req, ENOTDIR); return; } - struct stat st; if(ud->debug) fuse_log(FUSE_LOG_DEBUG, "open dir %s \n", inode->path.c_str()); - if(gkfs::syscall::gkfs_stat(inode->path, &st) != 0 || S_ISREG(st.st_mode)) { - fuse_reply_err(req, ENOTDIR); - return; - } const int fd = gkfs::syscall::gkfs_opendir(inode->path); - if(fd < 0) { - fuse_reply_err(req, ENOTDIR); - return; - } - // Simulate DIR structure for GekkoFS - DIR* dirp = - static_cast(malloc(sizeof(DIR) + inode->path.length() + - 1)); // Approximate size for DIR and path - if(dirp == nullptr) { - gkfs::syscall::gkfs_close(fd); // Clean up opened GekkoFS fd - fuse_reply_err(req, ENOMEM); - return; - } + if(ud->debug) + fuse_log(FUSE_LOG_DEBUG, "\t with fd %i \n", fd); - GkfsDir* gkfs_dir = reinterpret_cast(dirp); - gkfs_dir->fd = fd; - gkfs_dir->path = strdup(inode->path.c_str()); // strdup allocates memory - if(!gkfs_dir->path) { - free(dirp); - gkfs::syscall::gkfs_close(fd); - fuse_reply_err(req, ENOMEM); + if(fd < 0) { + fuse_reply_err(req, ENOTDIR); return; } - fi->fh = (uint64_t) dirp; // pointer trick + fi->fh = (uint64_t) fd; fuse_reply_open(req, fi); } @@ -486,13 +465,8 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, auto* ud = udata(req); if(ud->debug) fuse_log(FUSE_LOG_DEBUG, "readdir handler \n"); - auto* dir_ptr = reinterpret_cast(fi->fh); - if(!dir_ptr) { - fuse_reply_err(req, EBADF); - return; - } - auto open_dir = CTX->file_map()->get_dir(dir_ptr->fd); + auto open_dir = CTX->file_map()->get_dir(fi->fh); if(ud->debug) fuse_log(FUSE_LOG_DEBUG, "read dir %s \n", open_dir->path().c_str()); @@ -516,7 +490,6 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, auto de = open_dir->getdent(pos); struct stat st{}; - // TODO cannot be right, right? no stat? st.st_ino = std::hash()(open_dir->path() + "/" + de.name()); st.st_mode = (de.type() == gkfs::filemap::FileType::regular) ? S_IFREG @@ -572,15 +545,9 @@ releasedir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { auto* ud = udata(req); if(ud->debug) fuse_log(FUSE_LOG_DEBUG, "releasedir handler \n"); - GkfsDir* dir_ptr = reinterpret_cast(fi->fh); - if(CTX->interception_enabled() && CTX->file_map()->exist(dir_ptr->fd)) { - int fd = dir_ptr->fd; + if(CTX->interception_enabled() && CTX->file_map()->exist(fi->fh)) { int ret = gkfs::syscall::gkfs_close(fd); // Close GekkoFS internal FD - if(dir_ptr->path) { // Check if path was strdup'd - free(dir_ptr->path); - } - free(dir_ptr); // Free the DIR struct itself fuse_reply_err(req, ret); return; } -- GitLab From 934ce5df1fc6ff769f34cdeaced00700a4930c14 Mon Sep 17 00:00:00 2001 From: Julius Athenstaedt Date: Fri, 10 Oct 2025 14:44:32 +0200 Subject: [PATCH 32/32] refactor fuse logging, so it is only working on DEBUG build --- src/client/fuse/fuse_client.cpp | 150 ++++++++++++-------------------- 1 file changed, 55 insertions(+), 95 deletions(-) diff --git a/src/client/fuse/fuse_client.cpp b/src/client/fuse/fuse_client.cpp index 77023d912..b9a119799 100644 --- a/src/client/fuse/fuse_client.cpp +++ b/src/client/fuse/fuse_client.cpp @@ -47,6 +47,17 @@ static std::unordered_map local_fifos; static fuse_ino_t next_ino = 2; // reserve 1 for root static const std::string fifo_path = "/tmp/gekkofs_fifos/"; +#ifdef GKFS_DEBUG_BUILD +#define DEBUG_INFO(ud, fmt, ...) \ + do { \ + if((ud) && (ud)->debug) { \ + fuse_log(FUSE_LOG_DEBUG, "[DEBUG] " fmt "\n", ##__VA_ARGS__); \ + } \ + } while(0) +#else +#define DEBUG_INFO(...) /* No debug output */ +#endif + static fuse_ino_t alloc_inode(const std::string& path) { std::lock_guard lk(ino_mutex); @@ -123,9 +134,8 @@ passthrough_ll_help(void) { static void init_handler(void* userdata, struct fuse_conn_info* conn) { struct u_data* ud = (struct u_data*) userdata; - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "init handler readahead %i direct_io %i \n", - ud->max_readahead, ud->direct_io); + DEBUG_INFO(ud, "init handler readahead %i direct_io %i", ud->max_readahead, + ud->direct_io); // TODO check other capabilities e.g. FUSE_CAP_READDIRPLUS if(ud->writeback) { @@ -136,9 +146,8 @@ init_handler(void* userdata, struct fuse_conn_info* conn) { // for older fuse versions like on the ubuntu22 conn->want |= FUSE_CAP_WRITEBACK_CACHE; #endif - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, - "init_handler: try to activate writeback\n"); + DEBUG_INFO(ud, "init_handler: try to activate writeback", + ud->max_readahead, ud->direct_io); } // if(lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { // has_flag = fuse_set_feature_flag(conn, FUSE_CAP_FLOCK_LOCKS); @@ -155,24 +164,21 @@ init_handler(void* userdata, struct fuse_conn_info* conn) { static void destroy_handler(void* userdata) { struct u_data* ud = (struct u_data*) userdata; - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "destroy handler \n"); + DEBUG_INFO(ud, "destroy handler"); // userdata is GekkoFuse* if passed } static void lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { auto* ud = udata(req); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "lookup handler ino %u\n", parent); + DEBUG_INFO(ud, "lookup handler ino %u", parent); auto* parent_inode = get_inode(parent); if(!parent_inode) { fuse_reply_err(req, ENOENT); return; } std::string child = get_path(parent_inode, name); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "lookup %s\n", child.c_str()); + DEBUG_INFO(ud, "lookup %s", child.c_str()); if(ud->fifo) { auto iit = local_fifos.find(child); @@ -218,9 +224,7 @@ lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { static void getattr_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { auto* ud = udata(req); - if(ud->debug) { - fuse_log(FUSE_LOG_DEBUG, "getattr handler \n"); - } + DEBUG_INFO(ud, "getattr handler"); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); @@ -230,9 +234,8 @@ getattr_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { struct stat st; int rc = gkfs::syscall::gkfs_stat(inode->path, &st); if(rc) { - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "getattr error %u\n", rc); - fuse_reply_err(req, ENOENT); + DEBUG_INFO(ud, "getattr error %u", rc); + fuse_reply_err(req, errno); return; } inode->st = st; @@ -243,8 +246,7 @@ static void setattr_handler(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, struct fuse_file_info* fi) { auto* ud = udata(req); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "setattr handler ino %u\n", ino); + DEBUG_INFO(ud, "setattr handler ino %u", ino); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); @@ -255,9 +257,8 @@ setattr_handler(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, off_t new_size = attr->st_size; int res = gkfs::syscall::gkfs_truncate(inode->path, new_size); if(res < 0) { - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "setattr truncate failed on %s\n", - inode->path.c_str()); + DEBUG_INFO(ud, "setattr truncate failed on %s", + inode->path.c_str()); fuse_reply_err(req, EIO); return; } @@ -279,8 +280,7 @@ setattr_handler(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, static void open_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { auto* ud = udata(req); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "open handler \n"); + DEBUG_INFO(ud, "open handler"); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); @@ -302,8 +302,7 @@ static void lseek_handler(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, struct fuse_file_info* fi) { auto* ud = udata(req); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "lseek handler \n"); + DEBUG_INFO(ud, "lseek handler"); int lc = gkfs::syscall::gkfs_lseek(fi->fh, off, whence); if(lc < 0) { fuse_reply_err(req, 1); @@ -316,8 +315,7 @@ static void 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); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "read handler \n"); + DEBUG_INFO(ud, "read handler"); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); @@ -326,8 +324,7 @@ read_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, std::vector buf(size); int rc = gkfs::syscall::gkfs_pread(fi->fh, buf.data(), size, off); if(rc < 0) { - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "read fail \n"); + DEBUG_INFO(ud, "read fail"); fuse_reply_err(req, errno); return; } @@ -338,8 +335,7 @@ static void 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); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "write handler \n"); + DEBUG_INFO(ud, "write handler"); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); @@ -347,8 +343,7 @@ write_handler(fuse_req_t req, fuse_ino_t ino, const char* buf, size_t size, } int rc = gkfs::syscall::gkfs_pwrite(fi->fh, buf, size, off); if(rc < 0) { - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "write fail \n"); + DEBUG_INFO(ud, "write fail"); fuse_reply_err(req, errno); return; } @@ -365,12 +360,10 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, return; } std::string path = get_path(parent_inode, name); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "create handler %s\n", path.c_str()); + DEBUG_INFO(ud, "create handler %s", path.c_str()); int fd = gkfs::syscall::gkfs_open(path, mode, fi->flags | O_CREAT); if(fd < 0) { - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "create -> open failed errno %i\n", errno); + DEBUG_INFO(ud, "create -> open failed errno %i", errno); fuse_reply_err(req, errno); return; } @@ -383,8 +376,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); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "create new inode ino %i\n", ino); + DEBUG_INFO(ud, "create new inode ino %i", ino); ino_map[ino].st = st; fuse_entry_param e = {}; e.ino = ino; @@ -399,8 +391,7 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, static void unlink_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { auto* ud = udata(req); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "unlink handler \n"); + DEBUG_INFO(ud, "unlink handler"); auto* parent_inode = get_inode(parent); if(!parent_inode) { fuse_reply_err(req, ENOENT); @@ -435,20 +426,17 @@ unlink_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { static void opendir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { auto* ud = udata(req); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "opendir handler \n"); + DEBUG_INFO(ud, "opendir handler"); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOTDIR); return; } - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "open dir %s \n", inode->path.c_str()); + DEBUG_INFO(ud, "open dir %s", inode->path.c_str()); const int fd = gkfs::syscall::gkfs_opendir(inode->path); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "\t with fd %i \n", fd); + DEBUG_INFO(ud, "\t with fd %i \n", fd); if(fd < 0) { fuse_reply_err(req, ENOTDIR); @@ -463,13 +451,11 @@ static void readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info* fi) { auto* ud = udata(req); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "readdir handler \n"); + DEBUG_INFO(ud, "readdir handler"); auto open_dir = CTX->file_map()->get_dir(fi->fh); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "read dir %s \n", open_dir->path().c_str()); + DEBUG_INFO(ud, "read dir %s", open_dir->path().c_str()); if(open_dir == nullptr) { fuse_reply_err(req, EBADF); @@ -543,8 +529,7 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, static void releasedir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { auto* ud = udata(req); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "releasedir handler \n"); + DEBUG_INFO(ud, "releasedir handler"); if(CTX->interception_enabled() && CTX->file_map()->exist(fi->fh)) { int ret = gkfs::syscall::gkfs_close(fd); // Close GekkoFS internal FD @@ -558,24 +543,17 @@ releasedir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { static void release_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { auto* ud = udata(req); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "release handler \n"); + DEBUG_INFO(ud, "release handler"); auto* inode = get_inode(ino); if(!inode) { - if(ud->debug) - 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) { - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "release there \n"); fuse_reply_err(req, 1); return; } - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "release success \n"); fuse_reply_err(req, 0); } @@ -583,8 +561,7 @@ release_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { static void forget_handler(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { auto* ud = udata(req); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "forget handler \n"); + DEBUG_INFO(ud, "forget handler"); auto it = ino_map.find(ino); if(it == ino_map.end()) { @@ -601,8 +578,7 @@ forget_handler(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { if(inode.lookup_count == 0) { // && inode.open_count == 0 path_map.erase(inode.path); ino_map.erase(it); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "reached lookup_count 0 \n"); + DEBUG_INFO(ud, "reached lookup_count 0"); } fuse_reply_none(req); @@ -612,8 +588,7 @@ forget_handler(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { static void flush_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { auto* ud = udata(req); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "flush handler \n"); + DEBUG_INFO(ud, "flush handler"); auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); @@ -637,8 +612,7 @@ fsync_handler(fuse_req_t req, fuse_ino_t ino, int datasync, static void access_handler(fuse_req_t req, fuse_ino_t ino, int mask) { auto* ud = udata(req); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "access handler \n"); + DEBUG_INFO(ud, "access handler"); if(ud->access && !ud->fifo) { auto* inode = get_inode(ino); if(!inode) { @@ -668,9 +642,7 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name, return; } std::string path = get_path(parent_inode, name); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "mkdir parent %s name %s\n", - parent_inode->path.c_str(), name); + DEBUG_INFO(ud, "mkdir parent %s name %s", parent_inode->path.c_str(), name); int rc = gkfs::syscall::gkfs_create(path, mode | S_IFDIR); if(rc == -1) { fuse_reply_err(req, 1); @@ -679,14 +651,11 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name, struct stat st; int sc = gkfs::syscall::gkfs_stat(path, &st); if(sc == -1) { - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "thats why its not allowed \n"); fuse_reply_err(req, 1); return; } fuse_ino_t ino = alloc_inode(path); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "create new inode ino %i\n", ino); + DEBUG_INFO(ud, "create new inode ino %i", ino); ino_map[ino].st = st; fuse_entry_param e = {}; e.ino = ino; @@ -705,8 +674,7 @@ rmdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { return; } std::string path = get_path(parent_inode, name); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "rmdir %s\n", path.c_str()); + DEBUG_INFO(ud, "rmdir %s", path.c_str()); int rc = gkfs::syscall::gkfs_rmdir(path); if(rc == -1) { fuse_reply_err(req, errno); @@ -746,13 +714,10 @@ symlink_handler(fuse_req_t req, const char* linkname, fuse_ino_t parent, const char* name) { #ifdef HAS_SYMLINKS auto* ud = udata(req); - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "symlink handler linkname %s name %s\n", - linkname, name); + DEBUG_INFO(ud, "symlink handler linkname %s name %s", linkname, name); auto* parent_inode = get_inode(parent); if(!parent_inode) { - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "symlink parent inode ino %i\n", parent); + DEBUG_INFO(ud, "symlink parent inode ino %i", parent); fuse_reply_err(req, ENOENT); return; } @@ -766,9 +731,7 @@ symlink_handler(fuse_req_t req, const char* linkname, fuse_ino_t parent, target.substr(strlen(ud->mountpoint)).c_str()); } - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "mk symlink path %s target %s\n", path.c_str(), - target.c_str()); + DEBUG_INFO(ud, "mk symlink path %s target %s", path.c_str()); int rc = gkfs::syscall::gkfs_mk_symlink(path, target); if(rc < 0) { fuse_reply_err(req, 1); @@ -778,14 +741,11 @@ symlink_handler(fuse_req_t req, const char* linkname, fuse_ino_t parent, // Stat the new symlink so we can reply with entry info struct stat st; if(gkfs::syscall::gkfs_stat(path, &st) < 0) { - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "stat failed\n"); + DEBUG_INFO(ud, "stat failed %i", errno); fuse_reply_err(req, errno); return; } - if(ud->debug) - fuse_log(FUSE_LOG_DEBUG, "stat mode %i, iflink %i\n", st.st_mode, - S_IFLNK); + DEBUG_INFO(ud, "stat mode %i, iflink %i", 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 @@ -980,7 +940,7 @@ init_ll_ops(fuse_lowlevel_ops* ops) { // ops->write_buf ops->lseek = lseek_handler; - // xattr + // xattr for arbitrary key value fields // ops->setxattr // ops->getxattr // ops->listxattr @@ -1018,7 +978,7 @@ init_ll_ops(fuse_lowlevel_ops* ops) { // ops->retrive_reply // ops->fallocate // ops->copy_file_range - // ops->statx + // ops->statx // not supported in fuse < 3 } void -- GitLab