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
#define GKFS_CLIENT_FUSE_CONTEXT_HPP
#include "fuse_log.h"
#include <utility>
extern "C" {
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#include <fuse3/fuse_lowlevel.h>
......@@ -37,42 +78,32 @@ 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 <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <filesystem>
#include <unordered_map>
#include <string>
#include <mutex>
#ifdef __FreeBSD__
#include <sys/socket.h>
#include <sys/un.h>
#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
do_fallocate(int fd, int mode, off_t offset, off_t length) {
#ifdef HAVE_FALLOCATE
......@@ -103,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<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
* operation
......@@ -110,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;
......@@ -148,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;
......
......@@ -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
......
/*
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 <cstdio>
#include <cstdlib>
#include <dirent.h>
#include <iostream>
struct lo_inode {
struct lo_inode* next; /* protected by lo->mutex */
......@@ -85,6 +131,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,1095 +150,368 @@ lo_init(void* userdata, struct fuse_conn_info* conn) {
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;
// Simplified inode structure
struct Inode {
std::string path;
struct stat st;
uint64_t lookup_count;
};
static std::mutex ino_mutex;
static std::unordered_map<fuse_ino_t, Inode> 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<std::mutex> 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<std::mutex> 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;
}
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;
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;
}
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_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);
}
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);
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;
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);
// 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;
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);
inode->st = st;
fuse_reply_attr(req, &st, 1.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);
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;
}
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)
// TODO how to change attr?
int rc = 0;
if(rc) {
fuse_reply_err(req, rc);
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;
}
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);
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;
}
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);
fi->fh = fd; // TODO file handle == file descriptor?
fuse_reply_open(req, fi);
}
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);
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;
}
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;
std::vector<char> buf(size);
int lc = gkfs::syscall::gkfs_lseek(fi->fh, off, SEEK_SET);
if(lc < 0) {
fuse_reply_err(req, 1);
return;
}
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);
int rc = gkfs::syscall::gkfs_read(fi->fh, buf.data(), size);
if(rc < 0) {
fuse_reply_err(req, 1);
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'));
fuse_reply_buf(req, buf.data(), rc);
}
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);
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;
}
if(entsize > rem) {
if(entry_ino != 0)
lo_forget_one(req, entry_ino, 1);
break;
int lc = gkfs::syscall::gkfs_lseek(fi->fh, off, SEEK_SET);
if(lc < 0) {
fuse_reply_err(req, 1);
return;
}
p += entsize;
rem -= entsize;
d->entry = NULL;
d->offset = nextoff;
int rc = gkfs::syscall::gkfs_write(fi->fh, buf, size);
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);
if(rc < 0) {
fuse_reply_err(req, 1);
return;
}
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);
fuse_reply_write(req, rc);
}
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) {
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;
}
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);
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;
}
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);
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;
}
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_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
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);
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;
}
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;
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;
}
/* 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);
const int fd = gkfs::syscall::gkfs_opendir(inode->path);
if(fd < 0) {
fuse_reply_err(req, ENOTDIR);
return;
}
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);
// Simulate DIR structure for GekkoFS
DIR* dirp =
static_cast<DIR*>(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;
}
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);
GkfsDir* gkfs_dir = reinterpret_cast<GkfsDir*>(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;
}
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);
fi->fh = (uint64_t) dirp; // pointer trick
fuse_reply_open(req, fi);
}
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_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);
fuse_log(FUSE_LOG_DEBUG, "readdir handler \n");
auto* dir_ptr = reinterpret_cast<GkfsDir*>(fi->fh);
if(!dir_ptr) {
fuse_reply_err(req, EBADF);
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) {
value = (char*) malloc(size);
if(!value)
goto out_err;
fuse_log(FUSE_LOG_DEBUG, "read dir %s \n", open_dir->path().c_str());
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);
if(open_dir == nullptr) {
fuse_reply_err(req, EBADF);
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);
// Allocate a buffer to accumulate entries
char* buf = static_cast<char*>(malloc(size));
if(!buf) {
fuse_reply_err(req, ENOMEM);
return;
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
ret = setxattr(procname, name, value, size, flags);
saverr = ret == -1 ? errno : 0;
size_t bytes_filled = 0;
off_t pos = off;
out:
fuse_reply_err(req, saverr);
}
while(pos < open_dir->size()) {
auto de = open_dir->getdent(pos);
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);
}
struct stat st{};
// TODO cannot be right, right?
st.st_ino =
std::hash<std::string>()(open_dir->path() + "/" + de.name());
st.st_mode = (de.type() == gkfs::filemap::FileType::regular) ? S_IFREG
: S_IFDIR;
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);
saverr = ret == -1 ? errno : 0;
if(entry_size > size - bytes_filled)
break; // not enough space left
out:
fuse_reply_err(req, saverr);
bytes_filled += entry_size;
pos += 1;
}
#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) {
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,
.create = create_handler,
// getlk
// setlk
// bmap
// ioctl
.write_buf = lo_write_buf,
//.write_buf = lo_write_buf,
// poll
// retrive_reply
.forget_multi = lo_forget_multi,
.flock = lo_flock,
.fallocate = lo_fallocate,
.readdirplus = lo_readdirplus,
//.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
};
......@@ -1212,6 +532,32 @@ 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;
}
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) {
......@@ -1281,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;
......@@ -1313,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);
......
......@@ -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) {
......
......@@ -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<int, shared_ptr<gkfs::filemap::OpenDir>>
forward_get_dirents(const string& path) {
......