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
......
This diff is collapsed.
...@@ -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) {
......