Skip to content
Commits on Source (2)
/*
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
<https://www.gnu.org/licenses/>.
SPDX-License-Identifier: LGPL-3.0-or-later
*/
#ifndef GKFS_CLIENT_FUSE_CONTEXT_HPP #ifndef GKFS_CLIENT_FUSE_CONTEXT_HPP
#define GKFS_CLIENT_FUSE_CONTEXT_HPP #define GKFS_CLIENT_FUSE_CONTEXT_HPP
#include "fuse_log.h"
#include <utility>
extern "C" { extern "C" {
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12) #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse3/fuse_lowlevel.h> #include <fuse3/fuse_lowlevel.h>
...@@ -37,42 +78,32 @@ struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { ...@@ -37,42 +78,32 @@ struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct {
}; };
#endif #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 <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <filesystem>
#include <unordered_map>
#include <string>
#include <mutex>
#ifdef __FreeBSD__ #ifdef __FreeBSD__
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#endif #endif
// GekkoFS Project Headers
#include <common/path_util.hpp>
#include <client/hooks.hpp>
#include <client/logging.hpp> // Assumed to provide LOG and CTX
#include <client/open_dir.hpp>
#include <client/open_file_map.hpp>
#include <client/preload.hpp>
#include <client/preload_context.hpp>
#include <client/user_functions.hpp>
#include <client/gkfs_libc.hpp>
static inline int static inline int
do_fallocate(int fd, int mode, off_t offset, off_t length) { do_fallocate(int fd, int mode, off_t offset, off_t length) {
#ifdef HAVE_FALLOCATE #ifdef HAVE_FALLOCATE
...@@ -103,6 +134,142 @@ do_fallocate(int fd, int mode, off_t offset, off_t length) { ...@@ -103,6 +134,142 @@ do_fallocate(int fd, int mode, off_t offset, off_t length) {
#endif // HAVE_FALLOCATE #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<int, std::string>
lol_path(int dfd, const char* path, int flags) {
std::string resolved = "";
int err = 0;
if(CTX->interception_enabled()) {
// AT_SYMLINK_NOFOLLOW means lstat behavior, otherwise stat behavior.
bool follow_link = !(flags & AT_SYMLINK_NOFOLLOW);
// Path resolution needs to know if it's for lstat or stat context for
// the final component. resolve_gkfs_path's `resolve_last_link`
// parameter handles this.
switch(resolve_gkfs_path(dfd, path, resolved, flags, follow_link)) {
case PathStatus::Internal:
break;
case PathStatus::Error:
err = -1;
break;
default: // External
break;
}
}
return std::make_pair(err, resolved);
}
static inline int
lol_fstatat(int dfd, const char* path, struct stat* buf, int flags) {
if(CTX->interception_enabled()) {
std::string resolved;
// AT_SYMLINK_NOFOLLOW means lstat behavior, otherwise stat behavior.
bool follow_link = !(flags & AT_SYMLINK_NOFOLLOW);
// Path resolution needs to know if it's for lstat or stat context for
// the final component. resolve_gkfs_path's `resolve_last_link`
// parameter handles this.
switch(resolve_gkfs_path(dfd, path, resolved, flags, follow_link)) {
case PathStatus::Internal:
return gkfs::syscall::gkfs_stat(resolved, buf, follow_link);
case PathStatus::Error:
return -1; // errno set
default: // External
break;
}
}
return -1;
}
static inline int
lol_openat(int dfd, const char* path, mode_t mode, int flags) {
if(CTX->interception_enabled()) {
std::string resolved;
// AT_SYMLINK_NOFOLLOW means lstat behavior, otherwise stat behavior.
bool follow_link = !(flags & AT_SYMLINK_NOFOLLOW);
// Path resolution needs to know if it's for lstat or stat context for
// the final component. resolve_gkfs_path's `resolve_last_link`
// parameter handles this.
switch(resolve_gkfs_path(dfd, path, resolved, flags, follow_link)) {
case PathStatus::Internal:
fuse_log(FUSE_LOG_DEBUG, "lol_openat internal\n");
return gkfs::syscall::gkfs_open(resolved, mode, follow_link);
case PathStatus::Error:
return -1; // errno set
default: // External
break;
}
}
return -1;
}
/* /*
* Creates files on the underlying file system in response to a FUSE_MKNOD * Creates files on the underlying file system in response to a FUSE_MKNOD
* operation * operation
...@@ -110,18 +277,28 @@ do_fallocate(int fd, int mode, off_t offset, off_t length) { ...@@ -110,18 +277,28 @@ do_fallocate(int fd, int mode, off_t offset, off_t length) {
static inline int static inline int
mknod_wrapper(int dirfd, const char* path, const char* link, int mode, mknod_wrapper(int dirfd, const char* path, const char* link, int mode,
dev_t rdev) { dev_t rdev) {
int res; fuse_log(FUSE_LOG_DEBUG, "mknod_wrapper \n");
int res = -1;
if(S_ISREG(mode)) { 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) if(res >= 0)
res = close(res); res = gkfs::syscall::gkfs_close(res);
} else if(S_ISDIR(mode)) { } 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) { } 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)) { } 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__ #ifdef __FreeBSD__
} else if(S_ISSOCK(mode)) { } else if(S_ISSOCK(mode)) {
struct sockaddr_un su; struct sockaddr_un su;
...@@ -148,7 +325,10 @@ mknod_wrapper(int dirfd, const char* path, const char* link, int mode, ...@@ -148,7 +325,10 @@ mknod_wrapper(int dirfd, const char* path, const char* link, int mode,
} }
#endif #endif
} else { } 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; return res;
......
...@@ -118,8 +118,10 @@ target_sources(fuse_client ...@@ -118,8 +118,10 @@ target_sources(fuse_client
) )
target_link_libraries(fuse_client target_link_libraries(fuse_client
PRIVATE gkfs_common metadata distributor env_util arithmetic path_util rpc_utils
${FUSE3_LIBRARIES} ${FUSE3_LIBRARIES}
gkfs_user_lib gkfs_user_lib
gkfs_libc_intercept
) )
target_include_directories(fuse_client target_include_directories(fuse_client
......
/*
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
<https://www.gnu.org/licenses/>.
SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "client/env.hpp"
#include "common/env_util.hpp"
#include <cerrno>
#include <client/fuse/fuse_client.hpp> #include <client/fuse/fuse_client.hpp>
#include <cstdio>
#include <cstdlib>
#include <dirent.h>
#include <iostream>
struct lo_inode { struct lo_inode {
struct lo_inode* next; /* protected by lo->mutex */ struct lo_inode* next; /* protected by lo->mutex */
...@@ -85,6 +131,7 @@ lo_debug(fuse_req_t req) { ...@@ -85,6 +131,7 @@ lo_debug(fuse_req_t req) {
static void static void
lo_init(void* userdata, struct fuse_conn_info* conn) { lo_init(void* userdata, struct fuse_conn_info* conn) {
// TODO init gkfs
struct lo_data* lo = (struct lo_data*) userdata; struct lo_data* lo = (struct lo_data*) userdata;
bool has_flag; bool has_flag;
...@@ -103,1095 +150,368 @@ lo_init(void* userdata, struct fuse_conn_info* conn) { ...@@ -103,1095 +150,368 @@ lo_init(void* userdata, struct fuse_conn_info* conn) {
conn->no_interrupt = 1; conn->no_interrupt = 1;
} }
static void // Simplified inode structure
lo_destroy(void* userdata) { struct Inode {
struct lo_data* lo = (struct lo_data*) userdata; std::string path;
struct stat st;
while(lo->root.next != &lo->root) { uint64_t lookup_count;
struct lo_inode* next = lo->root.next; };
lo->root.next = next->next; static std::mutex ino_mutex;
close(next->fd); static std::unordered_map<fuse_ino_t, Inode> ino_map;
free(next); static fuse_ino_t next_ino = 2; // reserve 1 for root
}
} static fuse_ino_t
alloc_inode(const std::string& path) {
static void std::lock_guard<std::mutex> lk(ino_mutex);
lo_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { fuse_ino_t ino = next_ino++;
int res; ino_map[ino] = {path, {}, 1};
struct stat buf; return ino;
struct lo_data* lo = lo_data(req); }
int fd = fi ? fi->fh : lo_fd(req, ino); static Inode*
get_inode(fuse_ino_t ino) {
(void) fi; std::lock_guard<std::mutex> lk(ino_mutex);
auto it = ino_map.find(ino);
res = fstatat(fd, "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); return it != ino_map.end() ? &it->second : nullptr;
if(res == -1) }
return (void) fuse_reply_err(req, errno);
static void
fuse_reply_attr(req, &buf, lo->timeout); 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);
static void if(!parent_inode) {
lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int valid, fuse_log(FUSE_LOG_DEBUG, "this error 1 \n", parent);
struct fuse_file_info* fi) { fuse_reply_err(req, ENOENT);
int saverr; return;
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; std::string child = parent_inode->path + name;
fuse_log(FUSE_LOG_DEBUG, "lookup %s\n", child.c_str());
if(lo_debug(req)) struct stat st;
fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", int rc = gkfs::syscall::gkfs_stat(child, &st);
(unsigned long long) parent, name, if(rc < 0) {
(unsigned long long) e->ino); fuse_log(FUSE_LOG_DEBUG, "that error 2 \n", parent);
fuse_reply_err(req, ENOENT);
return 0; return;
out_err:
saverr = errno;
if(newfd != -1)
close(newfd);
return saverr;
} }
fuse_ino_t ino = alloc_inode(child);
static void ino_map[ino].st = st;
lo_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) { fuse_entry_param e = {};
struct fuse_entry_param e; e.ino = ino;
int err; e.attr = st;
e.attr_timeout = 1.0;
if(lo_debug(req)) e.entry_timeout = 1.0;
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); fuse_reply_entry(req, &e);
} }
static void static void
lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, const char* name, getattr_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
mode_t mode, dev_t rdev, const char* link) { fuse_log(FUSE_LOG_DEBUG, "getattr handler \n");
int res; auto* inode = get_inode(ino);
int saverr; if(!inode) {
struct lo_inode* dir = lo_inode(req, parent); fuse_reply_err(req, ENOENT);
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; 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);
} }
// query GekkoFS for latest attr
static void struct stat st;
lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode) { int rc = gkfs::syscall::gkfs_stat(inode->path, &st);
lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL); if(rc) {
} fuse_log(FUSE_LOG_DEBUG, "getattr error %u\n", rc);
fuse_reply_err(req, ENOENT);
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; return;
out_err:
saverr = errno;
fuse_reply_err(req, saverr);
} }
inode->st = st;
static void fuse_reply_attr(req, &st, 1.0);
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 static void
lo_rename(fuse_req_t req, fuse_ino_t parent, const char* name, setattr_handler(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set,
fuse_ino_t newparent, const char* newname, unsigned int flags) { struct fuse_file_info* fi) {
int res; fuse_log(FUSE_LOG_DEBUG, "setattr handler \n");
auto* inode = get_inode(ino);
if(flags) { if(!inode) {
fuse_reply_err(req, EINVAL); fuse_reply_err(req, ENOENT);
return; return;
} }
// TODO how to change attr?
res = renameat(lo_fd(req, parent), name, lo_fd(req, newparent), newname); int rc = 0;
if(rc) {
fuse_reply_err(req, res == -1 ? errno : 0); fuse_reply_err(req, rc);
}
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; 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);
} }
// 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;
} }
const int mode = 0644; // -rw-r--r-- I think that doesnt matter anyway
static void int fd = gkfs::syscall::gkfs_open(inode->path, mode,
lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { fi->flags); // TODO mode!
struct lo_data* lo = lo_data(req); if(fd < 0) {
struct lo_inode* inode = lo_inode(req, ino); fuse_reply_err(req, ENOENT);
return;
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);
} }
fi->fh = fd; // TODO file handle == file descriptor?
static void fuse_reply_open(req, fi);
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 static void
lo_readlink(fuse_req_t req, fuse_ino_t ino) { read_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
char buf[PATH_MAX + 1]; struct fuse_file_info* fi) {
int res; fuse_log(FUSE_LOG_DEBUG, "read handler \n");
auto* inode = get_inode(ino);
res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf)); if(!inode) {
if(res == -1) fuse_reply_err(req, ENOENT);
return (void) fuse_reply_err(req, errno); return;
if(res == sizeof(buf))
return (void) fuse_reply_err(req, ENAMETOOLONG);
buf[res] = '\0';
fuse_reply_readlink(req, buf);
} }
std::vector<char> buf(size);
struct lo_dirp { int lc = gkfs::syscall::gkfs_lseek(fi->fh, off, SEEK_SET);
DIR* dp; if(lc < 0) {
struct dirent* entry; fuse_reply_err(req, 1);
off_t offset; return;
};
static struct lo_dirp*
lo_dirp(struct fuse_file_info* fi) {
return (struct lo_dirp*) (uintptr_t) fi->fh;
} }
int rc = gkfs::syscall::gkfs_read(fi->fh, buf.data(), size);
static void if(rc < 0) {
lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { fuse_reply_err(req, 1);
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; return;
out_errno:
error = errno;
out_err:
if(d) {
if(fd != -1)
close(fd);
free(d);
} }
fuse_reply_err(req, error); fuse_reply_buf(req, buf.data(), rc);
}
static int
is_dot_or_dotdot(const char* name) {
return name[0] == '.' &&
(name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
} }
static void static void
lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, write_handler(fuse_req_t req, fuse_ino_t ino, const char* buf, size_t size,
struct fuse_file_info* fi, int plus) { off_t off, struct fuse_file_info* fi) {
struct lo_dirp* d = lo_dirp(fi); fuse_log(FUSE_LOG_DEBUG, "write handler \n");
char* buf; auto* inode = get_inode(ino);
char* p; if(!inode) {
size_t rem = size; fuse_reply_err(req, ENOENT);
int err; return;
(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) { int lc = gkfs::syscall::gkfs_lseek(fi->fh, off, SEEK_SET);
if(entry_ino != 0) if(lc < 0) {
lo_forget_one(req, entry_ino, 1); fuse_reply_err(req, 1);
break; return;
} }
int rc = gkfs::syscall::gkfs_write(fi->fh, buf, size);
p += entsize; if(rc < 0) {
rem -= entsize; fuse_reply_err(req, 1);
return;
d->entry = NULL;
d->offset = nextoff;
} }
if(rc < 0) {
err = 0; fuse_reply_err(req, 1);
error: return;
// 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) {
lo_do_readdir(req, ino, size, offset, fi, 0);
} }
static void 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) { struct fuse_file_info* fi) {
lo_do_readdir(req, ino, size, offset, fi, 1); 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;
static void int rc = gkfs::syscall::gkfs_create(path, mode);
lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { if(rc == -1) {
struct lo_dirp* d = lo_dirp(fi); fuse_log(FUSE_LOG_DEBUG, "here here mode %i flags %i \n", mode, fi->flags);
(void) ino; fuse_reply_err(req, 1);
closedir(d->dp); return;
free(d);
fuse_reply_err(req, 0);
} }
struct stat st;
static void int sc = gkfs::syscall::gkfs_stat(path, &st);
lo_tmpfile(fuse_req_t req, fuse_ino_t parent, mode_t mode, if(sc == -1) {
struct fuse_file_info* fi) { fuse_log(FUSE_LOG_DEBUG, "thats why its not allowed \n");
int fd; fuse_reply_err(req, 1);
struct lo_data* lo = lo_data(req); return;
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);
} }
fuse_ino_t ino = alloc_inode(path);
static void ino_map[ino].st = st;
lo_create(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, fuse_entry_param e = {};
struct fuse_file_info* fi) { e.ino = ino;
int fd; e.attr = st;
struct lo_data* lo = lo_data(req); e.attr_timeout = 1.0;
struct fuse_entry_param e; e.entry_timeout = 1.0;
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_reply_create(req, &e, fi);
} }
static void static void
lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, opendir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
struct fuse_file_info* fi) { fuse_log(FUSE_LOG_DEBUG, "opendir handler \n");
int res; auto* inode = get_inode(ino);
int fd = dirfd(lo_dirp(fi)->dp); if(!inode) {
(void) ino; fuse_reply_err(req, ENOTDIR);
if(datasync) return;
res = fdatasync(fd);
else
res = fsync(fd);
fuse_reply_err(req, res == -1 ? errno : 0);
} }
struct stat st;
static void fuse_log(FUSE_LOG_DEBUG, "open dir %s \n", inode->path.c_str());
lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { if(gkfs::syscall::gkfs_stat(inode->path, &st) != 0 || S_ISREG(st.st_mode)) {
int fd; fuse_reply_err(req, ENOTDIR);
char buf[64]; return;
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. const int fd = gkfs::syscall::gkfs_opendir(inode->path);
This breaks atomicity (since the file may change in the if(fd < 0) {
underlying filesystem, so that the kernel's idea of the fuse_reply_err(req, ENOTDIR);
end of the file isn't accurate anymore). In this example, return;
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 // Simulate DIR structure for GekkoFS
lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { DIR* dirp =
(void) ino; static_cast<DIR*>(malloc(sizeof(DIR) + inode->path.length() +
1)); // Approximate size for DIR and path
close(fi->fh); if(dirp == nullptr) {
fuse_reply_err(req, 0); gkfs::syscall::gkfs_close(fd); // Clean up opened GekkoFS fd
fuse_reply_err(req, ENOMEM);
return;
} }
static void GkfsDir* gkfs_dir = reinterpret_cast<GkfsDir*>(dirp);
lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { gkfs_dir->fd = fd;
int res; gkfs_dir->path = strdup(inode->path.c_str()); // strdup allocates memory
(void) ino; if(!gkfs_dir->path) {
res = close(dup(fi->fh)); free(dirp);
fuse_reply_err(req, res == -1 ? errno : 0); gkfs::syscall::gkfs_close(fd);
fuse_reply_err(req, ENOMEM);
return;
} }
static void fi->fh = (uint64_t) dirp; // pointer trick
lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, fuse_reply_open(req, fi);
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 static void
lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
struct fuse_file_info* fi) { struct fuse_file_info* fi) {
struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size); fuse_log(FUSE_LOG_DEBUG, "readdir handler \n");
auto* dir_ptr = reinterpret_cast<GkfsDir*>(fi->fh);
if(lo_debug(req)) if(!dir_ptr) {
fuse_log(FUSE_LOG_DEBUG, fuse_reply_err(req, EBADF);
"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; 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); auto open_dir = CTX->file_map()->get_dir(dir_ptr->fd);
if(size) { fuse_log(FUSE_LOG_DEBUG, "read dir %s \n", open_dir->path().c_str());
value = (char*) malloc(size);
if(!value)
goto out_err;
ret = listxattr(procname, value, size); if(open_dir == nullptr) {
if(ret == -1) fuse_reply_err(req, EBADF);
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; return;
out_err:
saverr = errno;
out:
fuse_reply_err(req, saverr);
goto out_free;
} }
static void // Allocate a buffer to accumulate entries
lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char* name, const char* value, char* buf = static_cast<char*>(malloc(size));
size_t size, int flags) { if(!buf) {
char procname[64]; fuse_reply_err(req, ENOMEM);
struct lo_inode* inode = lo_inode(req, ino); return;
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); size_t bytes_filled = 0;
off_t pos = off;
ret = setxattr(procname, name, value, size, flags);
saverr = ret == -1 ? errno : 0;
out: while(pos < open_dir->size()) {
fuse_reply_err(req, saverr); auto de = open_dir->getdent(pos);
}
static void struct stat st{};
lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char* name) { // TODO cannot be right, right?
char procname[64]; st.st_ino =
struct lo_inode* inode = lo_inode(req, ino); std::hash<std::string>()(open_dir->path() + "/" + de.name());
ssize_t ret; st.st_mode = (de.type() == gkfs::filemap::FileType::regular) ? S_IFREG
int saverr; : S_IFDIR;
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); size_t entry_size =
fuse_add_direntry(req, buf + bytes_filled, size - bytes_filled,
de.name().c_str(), &st, pos + 1);
ret = removexattr(procname, name); if(entry_size > size - bytes_filled)
saverr = ret == -1 ? errno : 0; break; // not enough space left
out: bytes_filled += entry_size;
fuse_reply_err(req, saverr); pos += 1;
} }
#ifdef HAVE_COPY_FILE_RANGE open_dir->pos(pos); // update internal position if needed
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 fuse_reply_buf(req, buf, bytes_filled);
lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, free(buf);
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 static void
lo_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask, init_handler(void* userdata, struct fuse_conn_info* conn) {
struct fuse_file_info* fi) { // userdata is GekkoFuse* if passed
struct lo_data* lo = lo_data(req); // optional: adjust conn->max_write, enable writeback etc.
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 = { static const struct fuse_lowlevel_ops lo_oper = {
.init = lo_init, .init = init_handler, // lo_init,
.destroy = lo_destroy, //.destroy = lo_destroy,
.lookup = lo_lookup, .lookup = lookup_handler,
.forget = lo_forget, //.forget = lo_forget,
.getattr = lo_getattr, .getattr = getattr_handler,
.setattr = lo_setattr, .setattr = setattr_handler,
.readlink = lo_readlink, //.readlink = lo_readlink,
.mknod = lo_mknod, //.mknod = lo_mknod,
.mkdir = lo_mkdir, //.mkdir = lo_mkdir,
.unlink = lo_unlink, //.unlink = lo_unlink,
.rmdir = lo_rmdir, //.rmdir = lo_rmdir,
.symlink = lo_symlink, //.symlink = lo_symlink,
.rename = lo_rename, //.rename = lo_rename,
.link = lo_link, //.link = lo_link,
.open = lo_open, .open = open_handler,
.read = lo_read, .read = read_handler,
// write .write = write_handler,
.flush = lo_flush, //.flush = lo_flush,
.release = lo_release, //.release = lo_release,
.fsync = lo_fsync, //.fsync = lo_fsync,
.opendir = lo_opendir, .opendir = opendir_handler,
.readdir = lo_readdir, .readdir = readdir_handler,
.releasedir = lo_releasedir, //.releasedir = lo_releasedir,
.fsyncdir = lo_fsyncdir, //.fsyncdir = lo_fsyncdir,
.statfs = lo_statfs, //.statfs = lo_statfs,
.setxattr = lo_setxattr, //.setxattr = lo_setxattr,
.getxattr = lo_getxattr, //.getxattr = lo_getxattr,
.listxattr = lo_listxattr, //.listxattr = lo_listxattr,
.removexattr = lo_removexattr, //.removexattr = lo_removexattr,
// access // access
.create = lo_create, .create = create_handler,
// getlk // getlk
// setlk // setlk
// bmap // bmap
// ioctl // ioctl
.write_buf = lo_write_buf, //.write_buf = lo_write_buf,
// poll // poll
// retrive_reply // retrive_reply
.forget_multi = lo_forget_multi, //.forget_multi = lo_forget_multi,
.flock = lo_flock, //.flock = lo_flock,
.fallocate = lo_fallocate, //.fallocate = lo_fallocate,
.readdirplus = lo_readdirplus, //.readdirplus = lo_readdirplus,
#ifdef HAVE_COPY_FILE_RANGE #ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = lo_copy_file_range, //.copy_file_range = lo_copy_file_range,
#endif #endif
.lseek = lo_lseek, //.lseek = lo_lseek,
.tmpfile = lo_tmpfile, //.tmpfile = lo_tmpfile,
#ifdef HAVE_STATX #ifdef HAVE_STATX
.statx = lo_statx, //.statx = lo_statx,
#endif #endif
}; };
...@@ -1212,6 +532,32 @@ main(int argc, char* argv[]) { ...@@ -1212,6 +532,32 @@ main(int argc, char* argv[]) {
lo.root.fd = -1; lo.root.fd = -1;
lo.cache = CACHE_NORMAL; 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;
}
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) if(fuse_parse_cmdline(&args, &opts) != 0)
return 1; return 1;
if(opts.show_help) { if(opts.show_help) {
...@@ -1281,12 +627,14 @@ main(int argc, char* argv[]) { ...@@ -1281,12 +627,14 @@ main(int argc, char* argv[]) {
exit(1); 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) { if(lo.root.fd == -1) {
fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", lo.source); fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", lo.source);
exit(1); exit(1);
} }
fuse_log(FUSE_LOG_DEBUG, "hier 2\n");
se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
if(se == NULL) if(se == NULL)
goto err_out1; goto err_out1;
...@@ -1313,10 +661,13 @@ main(int argc, char* argv[]) { ...@@ -1313,10 +661,13 @@ main(int argc, char* argv[]) {
fuse_session_unmount(se); fuse_session_unmount(se);
err_out3: err_out3:
fuse_log(FUSE_LOG_DEBUG, "hier 3\n");
fuse_remove_signal_handlers(se); fuse_remove_signal_handlers(se);
err_out2: err_out2:
fuse_log(FUSE_LOG_DEBUG, "hier 4\n");
fuse_session_destroy(se); fuse_session_destroy(se);
err_out1: err_out1:
fuse_log(FUSE_LOG_DEBUG, "hier 5\n");
free(opts.mountpoint); free(opts.mountpoint);
fuse_opt_free_args(&args); fuse_opt_free_args(&args);
......
...@@ -185,7 +185,7 @@ test_lock_file(const std::string& path) { ...@@ -185,7 +185,7 @@ test_lock_file(const std::string& path) {
* @param path * @param path
* @param mode * @param mode
* @param flags * @param flags
* @return 0 on success, -1 on failure * @return fd on success, -1 on failure
*/ */
int int
gkfs_open(const std::string& path, mode_t mode, int flags) { 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) { ...@@ -1451,7 +1451,7 @@ gkfs_readv(int fd, const struct iovec* iov, int iovcnt) {
* wrapper function for opening directories * wrapper function for opening directories
* errno may be set * errno may be set
* @param path * @param path
* @return 0 on success or -1 on error * @return fd on success or -1 on error
*/ */
int int
gkfs_opendir(const std::string& path) { gkfs_opendir(const std::string& path) {
......
...@@ -664,7 +664,7 @@ forward_get_metadentry_size(const std::string& path, const int copy) { ...@@ -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. * Send an RPC request to receive all entries of a directory.
* @param open_dir * @param open_dir
* @return error code * @return error code, OpenDir
*/ */
pair<int, shared_ptr<gkfs::filemap::OpenDir>> pair<int, shared_ptr<gkfs::filemap::OpenDir>>
forward_get_dirents(const string& path) { forward_get_dirents(const string& path) {
......