Skip to content
Commits on Source (106)
......@@ -22,7 +22,7 @@ variables:
LIBGKFS_LOG: "all"
LIBGKFS_LOG_OUTPUT: "${CI_PROJECT_DIR}/logs/gkfs_client.log"
GIT_SUBMODULE_STRATEGY: recursive
CCACHE_DIR: "$CI_PROJECT_DIR/ccache"
CCACHE_DIR: "${CI_PROJECT_DIR}/ccache"
# SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache
# GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task
......
......@@ -11,6 +11,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Tests to cover proxy and (malleability) ([!222](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/222))
- New fd generation method ([!225](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/202))
- Use LIBGKFS_PROTECT_FD=1 to enable the original method of assignation and protection.
- Lock system (server level) ([!233](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/233))
- Use PROTECT_FILES_GENERATOR=1 and PROTECT_FILES_CONSUMER=1 to enable. Generator, creates transparent .lockgekko files that blocks the open (for some seconds) of any consumer. Multiple opens / closes for generator are managed.
### Changed
- Tests check ret for -1 instead of 10000 fd ([!320](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/320))
......@@ -21,6 +23,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Refactor and modernize sfind and gfind ([!226](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/226))
- Export extra user library functions ([!227](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/227))
- Update CLI11 and fmt to avoid cmake errors ([!231])(https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/231))
- Update CLI11 in modules ([!232](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/232)).
### Fixed
- Dup3 is supported if O_CLOEXEC is not used (i.e. hexdump) ([!228](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/228))
......@@ -45,6 +48,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Update capstone to version 6.0.0-Alpha1 and syscall intercept to match ([!215](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/215)).
- Update ARM version and make user library optional in cmake ([!330](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/330))
### Removed
### Fixed
## [0.9.3] - 2024-07
......
......@@ -241,6 +241,26 @@ gkfs_define_option(
DEFAULT_VALUE ON
)
gkfs_define_option(
GKFS_BUILD_USER_LIB_AUTOINIT
HELP_TEXT "Compile user lib autoinit"
DEFAULT_VALUE ON
)
#build libc
gkfs_define_option(
GKFS_BUILD_LIBC_INTERCEPTION
HELP_TEXT "Compile libc interception"
DEFAULT_VALUE ON
)
gkfs_define_option(
GKFS_BUILD_LIBC_INTERCEPTION_MANUAL
HELP_TEXT "Compile libc interception with manual init"
DEFAULT_VALUE ON
)
# use old resolve function
gkfs_define_option(
GKFS_USE_LEGACY_PATH_RESOLVE
......@@ -287,6 +307,14 @@ gkfs_define_option(
DESCRIPTION "Compile with lstat usage in path resolve"
)
## external link support
gkfs_define_option(
GKFS_ENABLE_LOCKING
HELP_TEXT "Enable support for locking with fcntl the underlying file descriptor"
DEFAULT_VALUE OFF
DESCRIPTION "It can be very wrong"
)
################################################################################
# Options and variables that control how GekkoFS behaves internally
......
......@@ -248,7 +248,7 @@ include_from_source(cli11
MESSAGE "[${PROJECT_NAME}] Searching for CLI11"
SOURCE_DIR ${GKFS_DEPENDENCIES_PATH}/CLI11
GIT_REPOSITORY https://github.com/CLIUtils/CLI11
GIT_TAG v2.4.2
GIT_TAG v2.5.0
)
if (GKFS_ENABLE_CLIENT_METRICS)
......@@ -316,6 +316,10 @@ if (GKFS_ENABLE_PROMETHEUS)
add_definitions(-DGKFS_ENABLE_PROMETHEUS)
endif ()
if (GKFS_ENABLE_LOCKING)
add_definitions(-DENABLE_LOCKING)
endif ()
configure_file(include/common/cmake_configure.hpp.in include/common/cmake_configure.hpp)
if (GKFS_ENABLE_CLIENT_LOG)
......
......@@ -589,6 +589,11 @@ until the file is closed. The cache does not impact the consistency of the file
When the user creates a fd, this is protected from normal fds with a recolocation. This theoretically protects the fd from being closed from outside. However a new fd assignation system has been developed and is activated by default.
- `LIBGKFS_PROTECT_FD=1` - Enable the original method of assignation and protection.
##### Lightweight File-Locking (server side)
Using two environment variables
- `LIBGKFS_PROTECT_FILES_GENERATOR=1` enables the application as generator, so a open will create (and increase) a file .lockgekko and close will remove it (or decrease its value). The behaviour only uses metadata at server side.
- `LIBGKFS_PROTECT_FILES_CONSUMER=1` enables the application as consumer, so a open will wait until the .lockgekko dissapears. The wait is limited to ~40 seconds.
### Daemon
#### Logging
- `GKFS_DAEMON_LOG_PATH` - Path to the log file of the daemon.
......
Subproject commit 6c7b07a878ad834957b98d0f9ce1dbe0cb204fc9
Subproject commit 4160d259d961cd393fd8d67590a8c7d210207348
......@@ -69,6 +69,10 @@ static constexpr auto METRICS_IP_PORT = ADD_PREFIX("METRICS_IP_PORT");
#endif
static constexpr auto PROTECT_FD = ADD_PREFIX("PROTECT_FD");
static constexpr auto PROTECT_FILES_GENERATOR =
ADD_PREFIX("PROTECT_FILES_GENERATOR");
static constexpr auto PROTECT_FILES_CONSUMER =
ADD_PREFIX("PROTECT_FILES_CONSUMER");
static constexpr auto NUM_REPL = ADD_PREFIX("NUM_REPL");
static constexpr auto PROXY_PID_FILE = ADD_PREFIX("PROXY_PID_FILE");
namespace cache {
......
......@@ -42,11 +42,10 @@
#include <client/open_file_map.hpp>
#include <common/metadata.hpp>
#include <client/intercept.hpp>
struct statfs;
struct statvfs;
struct linux_dirent;
struct linux_dirent64;
namespace gkfs::syscall {
......@@ -56,6 +55,9 @@ gkfs_open(const std::string& path, mode_t mode, int flags);
int
gkfs_create(const std::string& path, mode_t mode);
int
gkfs_libcremove(const std::string& path);
int
gkfs_remove(const std::string& path);
......@@ -67,7 +69,8 @@ gkfs_access(const std::string& path, int mask, bool follow_links = true);
// Implementation of stat,
// Follow links is true by default
int
gkfs_stat(const std::string& path, struct stat* buf, bool follow_links = true);
gkfs_stat(const std::string& path, struct stat* buf, bool follow_links = true,
bool bypass_rename = false);
// Implementation of statx, it uses the normal stat and maps the information to
// the statx structure Follow links is true by default
......
/*
* Copyright 2000-2024 Felix Garcia Carballeira, Diego Camarmas Alonso,
* Alejandro Calderon Mateos, Luis Miguel Sanchez Garcia, Borja Bergua Guerra
*
* This file is part of Expand.
*
* Expand 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.
*
* Expand 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 Expand. If not, see <http://www.gnu.org/licenses/>.
*
* Modifications to support GekkoFS done by :
* 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.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <dlfcn.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/statvfs.h>
#include <sys/types.h>
#include <dirent.h>
#ifndef _STAT_VER
#define _STAT_VER 0
#endif
struct __dirstream {
int fd; // File descriptor.
//__libc_lock_define (, lock) // Mutex lock for this structure. //TODO
size_t allocation; // Space allocated for the block.
size_t size; // Total valid data in the block.
size_t offset; // Current offset into the block.
off_t filepos; // Position of next entry to read.
// Directory block.
char* path;
char data[1] __attribute__((aligned(__alignof__(void*))));
}; // Originally its 0, but C++ does not permit it
int
open64(const char* path, int flags, ...);
int
open(const char* path, int flags, ...);
// openat
int
openat(int __fd, const char* __file, int __oflag, ...);
int
openat64(int dirfd, const char* path, int flags, ...);
int
close(int fd);
int
creat(const char* path, mode_t mode);
int
ftruncate(int fd, off_t length) noexcept;
int
mkstemp(char* templates);
ssize_t
read(int fd, void* buf, size_t nbyte);
ssize_t
write(int fd, const void* buf, size_t nbyte);
int
mkdir(const char* path, mode_t mode);
int
rmdir(const char* path) noexcept;
int
dup(int oldfd) __THROW __wur;
int
dup2(int oldfd, int newfd) __THROW;
int
dup3(int __fd, int __fd2, int __flags) __THROW;
ssize_t
pread(int fd, void* buf, size_t count, off_t offset);
ssize_t
pwrite(int fd, const void* buf, size_t count, off_t offset);
ssize_t
pread64(int fd, void* buf, size_t count, off_t offset);
ssize_t
pwrite64(int fd, const void* buf, size_t count, off_t offset);
off_t
lseek(int fd, off_t offset, int whence) __THROW;
off64_t
lseek64(int fd, off64_t offset, int whence) __THROW;
int
fstat(int fd, struct stat* buf);
int
fstat64(int fd, struct stat64* buf);
int
stat(const char* path, struct stat* buf);
int
stat64(const char* path, struct stat64* buf);
/* This is called on cp <file> <dir>*/
int
fstatat(int dfd, const char* path, struct stat* buf, int flags);
int
__xstat(int ver, const char* path, struct stat* buf);
int
__xstat64(int ver, const char* path, struct stat64* buf);
int
__fxstat64(int ver, int fd, struct stat64* buf);
int
__lxstat(int ver, const char* path, struct stat* buf);
int
__lxstat64(int ver, const char* path, struct stat64* buf);
int
__fxstat(int ver, int fd, struct stat* buf);
int
__fxstatat(int ver, int dfd, const char* path, struct stat* buf, int flags);
int
__fxstatat64(int ver, int dfd, const char* path, struct stat64* buf, int flags);
int
statx(int dirfd, const char* pathname, int flags, unsigned int mask,
struct statx* statxbuf);
int
rename(const char* old_path, const char* new_path);
int
unlink(const char* path) __THROW __nonnull((1));
int
remove(const char* path);
DIR*
opendir(const char* dirname);
DIR*
opendir64(const char* dirname);
int
closedir(DIR* dirp);
long
telldir(DIR* dirp);
void
seekdir(DIR* dirp, long loc);
struct dirent*
readdir(DIR* dirp);
struct dirent64*
readdir64(DIR* dirp);
int
clone(int (*fn)(void*), void* stack, int flags, void* arg, ...);
/* pid_t *parent_tid, void *tls, pid_t *child_tid */
/* ................................................................... */
FILE*
fopen(const char* filename, const char* mode);
FILE*
freopen64(const char* pathname, const char* mode, FILE* stream);
size_t
fread(void* ptr, size_t size, size_t nmemb, FILE* stream);
size_t
fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream);
int
fseek(FILE* stream, long int offset, int whence);
long
ftell(FILE* stream);
void
rewind(FILE* stream);
int
fclose(FILE* stream);
// feof implementation with lseek
int
feof(FILE* stream);
// fgets implementation
char*
fgets(char* str, int size, FILE* stream);
// fputs implementation
int
fputs(const char* str, FILE* stream);
#ifdef __cplusplus
}
#endif
......@@ -53,6 +53,7 @@ extern "C" {
#include <client/void_syscall_intercept.hpp>
#endif
#include <client/intercept.hpp>
/*
* For PowerPC, syscall_no_intercept_wrapper() is defined in the
......@@ -75,9 +76,6 @@ syscall_no_intercept_wrapper(long syscall_number, Args... args) {
#endif
struct statfs;
struct linux_dirent;
struct linux_dirent64;
namespace gkfs::hook {
int
......
......@@ -40,6 +40,37 @@
#ifndef GEKKOFS_INTERCEPT_HPP
#define GEKKOFS_INTERCEPT_HPP
#include <stdint.h>
/*
* linux_dirent is used in getdents() but is privately defined in the linux
* kernel: fs/readdir.c.
*/
struct linux_dirent {
#ifndef __USE_FILE_OFFSET64
unsigned long d_ino;
unsigned long d_off;
#else
uint64_t d_ino;
int64_t d_off;
#endif
unsigned short d_reclen;
unsigned char d_type; // Does it break dirents?
char d_name[1];
};
/*
* linux_dirent64 is used in getdents64() and defined in the linux kernel:
* include/linux/dirent.h. However, it is not part of the kernel-headers and
* cannot be imported.
*/
struct linux_dirent64 {
uint64_t d_ino;
int64_t d_off;
unsigned short d_reclen;
unsigned char d_type;
char d_name[1]; // originally `char d_name[0]` in kernel, but ISO C++
// forbids zero-size array 'd_name'
};
namespace gkfs::preload {
int
......
......@@ -48,6 +48,8 @@
#define CTX gkfs::preload::PreloadContext::getInstance()
namespace gkfs::preload {
void
init_environment();
void
init_ld_env_if_needed();
} // namespace gkfs::preload
......@@ -57,6 +59,12 @@ init_preload() __attribute__((constructor));
void
destroy_preload() __attribute__((destructor));
#elif ENABLE_INIT
void
init_libc() __attribute__((constructor));
void
destroy_libc() __attribute__((destructor));
#endif
void
......@@ -66,4 +74,11 @@ at_child_syscall();
void
at_parent_syscall();
void
at_fork();
void
at_child();
void
at_parent();
#endif // IOINTERCEPT_PRELOAD_HPP
......@@ -140,6 +140,9 @@ private:
int replicas_;
bool protect_fds_{false};
bool protect_files_generator_{false};
bool protect_files_consumer_{false};
std::shared_ptr<gkfs::messagepack::ClientMetrics> write_metrics_;
std::shared_ptr<gkfs::messagepack::ClientMetrics> read_metrics_;
......@@ -322,6 +325,19 @@ public:
void
protect_fds(bool protect);
bool
protect_files_generator() const;
void
protect_files_generator(bool protect);
bool
protect_files_consumer() const;
void
protect_files_consumer(bool protect);
const std::shared_ptr<gkfs::messagepack::ClientMetrics>
write_metrics();
......
......@@ -1343,6 +1343,128 @@ struct mk_symlink {
#endif // HAS_SYMLINKS
#ifdef HAS_RENAME
//==============================================================================
// definitions for rename
struct rename {
// forward declarations of public input/output types for this RPC
class input;
class output;
// traits used so that the engine knows what to do with the RPC
using self_type = rename;
using handle_type = hermes::rpc_handle<self_type>;
using input_type = input;
using output_type = output;
using mercury_input_type = rpc_rename_in_t;
using mercury_output_type = rpc_err_out_t;
// RPC public identifier
// (N.B: we reuse the same IDs assigned by Margo so that the daemon
// understands Hermes RPCs)
constexpr static const uint64_t public_id = 40;
// RPC internal Mercury identifier
constexpr static const hg_id_t mercury_id = 0;
// RPC name
constexpr static const auto name = gkfs::rpc::tag::rename;
// requires response?
constexpr static const auto requires_response = true;
// Mercury callback to serialize input arguments
constexpr static const auto mercury_in_proc_cb =
HG_GEN_PROC_NAME(rpc_rename_in_t);
// Mercury callback to serialize output arguments
constexpr static const auto mercury_out_proc_cb =
HG_GEN_PROC_NAME(rpc_err_out_t);
class input {
template <typename ExecutionContext>
friend hg_return_t
hermes::detail::post_to_mercury(ExecutionContext*);
public:
input(const std::string& path, const std::string& target_path)
: m_path(path), m_target_path(target_path) {}
input(input&& rhs) = default;
input(const input& other) = default;
input&
operator=(input&& rhs) = default;
input&
operator=(const input& other) = default;
std::string
path() const {
return m_path;
}
std::string
target_path() const {
return m_target_path;
}
explicit input(const rpc_rename_in_t& other)
: m_path(other.path), m_target_path(other.target_path) {}
explicit
operator rpc_rename_in_t() {
return {m_path.c_str(), m_target_path.c_str()};
}
private:
std::string m_path;
std::string m_target_path;
};
class output {
template <typename ExecutionContext>
friend hg_return_t
hermes::detail::post_to_mercury(ExecutionContext*);
public:
output() : m_err() {}
output(int32_t err) : m_err(err) {}
output(output&& rhs) = default;
output(const output& other) = default;
output&
operator=(output&& rhs) = default;
output&
operator=(const output& other) = default;
explicit output(const rpc_err_out_t& out) {
m_err = out.err;
}
int32_t
err() const {
return m_err;
}
private:
int32_t m_err;
};
};
#endif // HAS_RENAME
//==============================================================================
// definitions for remove data
struct remove_data {
......
......@@ -43,9 +43,11 @@
#include <cstdint>
#include <vector>
extern "C" {
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
}
struct linux_dirent;
......@@ -60,6 +62,8 @@ gkfs_open(const std::string& path, mode_t mode, int flags);
int
gkfs_create(const std::string& path, mode_t mode);
int
gkfs_libcremove(const std::string& path);
int
gkfs_remove(const std::string& path);
int
......@@ -83,13 +87,29 @@ gkfs_pwrite(int fd, const void* buf, size_t count, off64_t offset);
ssize_t
gkfs_pread(int fd, void* buf, size_t count, off64_t offset);
ssize_t
gkfs_readv(int fd, const struct iovec* iov, int iovcnt);
ssize_t
gkfs_writev(int fd, const struct iovec* iov, int iovcnt);
ssize_t
gkfs_preadv(int fd, const struct iovec* iov, int iovcnt, off_t offset);
ssize_t
gkfs_pwritev(int fd, const struct iovec* iov, int iovcnt, off_t offset);
int
gkfs_stat(const std::string& path, struct stat* buf, bool follow_links = true);
gkfs_stat(const std::string& path, struct stat* buf, bool follow_links = true,
bool bypass_rename = false);
int
gkfs_statx(int dirfs, const std::string& path, int flags, unsigned int mask,
struct statx* buf, bool follow_links = true);
int
gkfs_libcremove(const std::string& path);
int
gkfs_remove(const std::string& path);
......@@ -118,6 +138,16 @@ gkfs_getdents64(unsigned int fd, struct linux_dirent64* dirp,
int
gkfs_truncate(const std::string& path, off_t offset);
#ifdef HAS_SYMLINKS
int
gkfs_mk_symlink(const std::string& path, const std::string& target);
int
gkfs_readlink(const std::string& path, char* buf, int bufsize);
#endif
#ifdef HAS_RENAME
int
gkfs_rename(const std::string& old_path, const std::string& new_path);
#endif
} // namespace syscall
namespace malleable {
......
......@@ -71,6 +71,10 @@ constexpr auto get_dirents_extended = "rpc_srv_get_dirents_extended";
#ifdef HAS_SYMLINKS
constexpr auto mk_symlink = "rpc_srv_mk_symlink";
#endif
#ifdef HAS_RENAME
constexpr auto rename = "rpc_srv_rename";
#endif
constexpr auto write = "rpc_srv_write_data";
constexpr auto read = "rpc_srv_read_data";
constexpr auto truncate = "rpc_srv_trunc_data";
......
......@@ -94,6 +94,12 @@ MERCURY_GEN_PROC(rpc_mk_symlink_in_t, ((hg_const_string_t) (path))((
hg_const_string_t) (target_path)))
#endif
#ifdef HAS_RENAME
MERCURY_GEN_PROC(rpc_rename_in_t, ((hg_const_string_t) (path))(
(hg_const_string_t) (target_path)))
#endif
// data
MERCURY_GEN_PROC(
......
......@@ -72,6 +72,10 @@ DECLARE_MARGO_RPC_HANDLER(rpc_srv_get_dirents_extended)
DECLARE_MARGO_RPC_HANDLER(rpc_srv_mk_symlink)
#endif
#ifdef HAS_RENAME
DECLARE_MARGO_RPC_HANDLER(rpc_srv_rename)
#endif
// data
DECLARE_MARGO_RPC_HANDLER(rpc_srv_remove_data)
......
......@@ -34,6 +34,22 @@
add_library(gkfs_intercept SHARED)
if (GKFS_BUILD_USER_LIB)
add_library(gkfs_user_lib SHARED)
if(GKFS_BUILD_USER_LIB_AUTOINIT)
add_library(gkfs_user_lib_autoinit SHARED)
endif()
endif()
if (GKFS_BUILD_LIBC_INTERCEPTION)
add_library(gkfs_libc_intercept SHARED)
endif()
if (GKFS_BUILD_LIBC_INTERCEPTION_MANUAL)
add_library(gkfs_libc_intercept_manual SHARED)
endif()
target_sources(gkfs_intercept
PRIVATE gkfs_functions.cpp
intercept.cpp
......@@ -55,7 +71,6 @@ target_sources(gkfs_intercept
syscalls/detail/syscall_info.c)
if (GKFS_BUILD_USER_LIB)
add_library(gkfs_user_lib SHARED)
target_sources(
gkfs_user_lib
PRIVATE gkfs_functions.cpp
......@@ -79,9 +94,107 @@ target_sources(
rpc/forward_malleability.cpp
syscalls/detail/syscall_info.c syscalls/util.S
)
if(GKFS_BUILD_USER_LIB_AUTOINIT)
target_sources(
gkfs_user_lib_autoinit
PRIVATE gkfs_functions.cpp
intercept.cpp
hooks.cpp
logging.cpp
open_file_map.cpp
open_dir.cpp
path.cpp
preload.cpp
preload_context.cpp
preload_util.cpp
malleability.cpp
cache.cpp
rpc/rpc_types.cpp
rpc/forward_data.cpp
rpc/forward_data_proxy.cpp
rpc/forward_management.cpp
rpc/forward_metadata.cpp
rpc/forward_metadata_proxy.cpp
rpc/forward_malleability.cpp
syscalls/detail/syscall_info.c syscalls/util.S
)
endif()
endif()
if (GKFS_BUILD_LIBC_INTERCEPTION)
target_sources(
gkfs_libc_intercept
PRIVATE gkfs_functions.cpp
gkfs_libc.cpp
intercept.cpp
hooks.cpp
logging.cpp
open_file_map.cpp
open_dir.cpp
path.cpp
preload.cpp
preload_context.cpp
preload_util.cpp
malleability.cpp
cache.cpp
rpc/rpc_types.cpp
rpc/forward_data.cpp
rpc/forward_data_proxy.cpp
rpc/forward_management.cpp
rpc/forward_metadata.cpp
rpc/forward_metadata_proxy.cpp
rpc/forward_malleability.cpp
syscalls/detail/syscall_info.c syscalls/util.S
)
endif ()
if (GKFS_BUILD_LIBC_INTERCEPTION_MANUAL)
target_sources(
gkfs_libc_intercept_manual
PRIVATE gkfs_functions.cpp
gkfs_libc.cpp
intercept.cpp
hooks.cpp
logging.cpp
open_file_map.cpp
open_dir.cpp
path.cpp
preload.cpp
preload_context.cpp
preload_util.cpp
malleability.cpp
cache.cpp
rpc/rpc_types.cpp
rpc/forward_data.cpp
rpc/forward_data_proxy.cpp
rpc/forward_management.cpp
rpc/forward_metadata.cpp
rpc/forward_metadata_proxy.cpp
rpc/forward_malleability.cpp
syscalls/detail/syscall_info.c syscalls/util.S
)
endif ()
if (GKFS_BUILD_USER_LIB)
target_compile_definitions(gkfs_user_lib PUBLIC BYPASS_SYSCALL)
target_link_options(gkfs_user_lib PRIVATE -z noexecstack)
if (GKFS_BUILD_USER_LIB_AUTOINIT)
target_compile_definitions(gkfs_user_lib_autoinit PUBLIC BYPASS_SYSCALL ENABLE_INIT)
target_link_options(gkfs_user_lib_autoinit PRIVATE -z noexecstack)
endif()
endif()
if (GKFS_BUILD_LIBC_INTERCEPTION)
target_compile_definitions(gkfs_libc_intercept PUBLIC BYPASS_SYSCALL ENABLE_INIT)
target_link_options(gkfs_libc_intercept PRIVATE -z noexecstack)
endif ()
if (GKFS_BUILD_LIBC_INTERCEPTION_MANUAL)
target_compile_definitions(gkfs_libc_intercept_manual PUBLIC BYPASS_SYSCALL)
target_link_options(gkfs_libc_intercept_manual PRIVATE -z noexecstack)
endif ()
if (GKFS_ENABLE_AGIOS)
......@@ -121,27 +234,130 @@ target_link_libraries(
Threads::Threads
Microsoft.GSL::GSL
)
if (GKFS_BUILD_USER_LIB_AUTOINIT)
target_link_libraries(
gkfs_user_lib_autoinit
PRIVATE metadata distributor env_util arithmetic path_util rpc_utils
PUBLIC dl
Mercury::Mercury
hermes
fmt::fmt
Threads::Threads
Microsoft.GSL::GSL
)
endif()
endif()
if (GKFS_BUILD_LIBC_INTERCEPTION)
target_link_libraries(
gkfs_libc_intercept
PRIVATE metadata distributor env_util arithmetic path_util rpc_utils
PUBLIC dl
Mercury::Mercury
hermes
fmt::fmt
Threads::Threads
Microsoft.GSL::GSL
)
# Enable MSGPack metrics for intercept only
if (GKFS_ENABLE_CLIENT_METRICS)
target_link_libraries(
gkfs_libc_intercept
PUBLIC
msgpack_util
)
target_compile_definitions(gkfs_libc_intercept PUBLIC GKFS_ENABLE_CLIENT_METRICS)
endif ()
endif ()
if (GKFS_BUILD_LIBC_INTERCEPTION_MANUAL)
target_link_libraries(
gkfs_libc_intercept_manual
PRIVATE metadata distributor env_util arithmetic path_util rpc_utils
PUBLIC dl
Mercury::Mercury
hermes
fmt::fmt
Threads::Threads
Microsoft.GSL::GSL
)
# Enable MSGPack metrics for intercept only
if (GKFS_ENABLE_CLIENT_METRICS)
target_link_libraries(
gkfs_libc_intercept_manual
PUBLIC
msgpack_util
)
target_compile_definitions(gkfs_libc_intercept_manual PUBLIC GKFS_ENABLE_CLIENT_METRICS)
endif ()
endif ()
install(
TARGETS gkfs_intercept
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gkfs
)
if (GKFS_BUILD_USER_LIB)
set_target_properties(gkfs_user_lib
PROPERTIES
PUBLIC_HEADER "../../include/client/void_syscall_intercept.hpp"
PUBLIC_HEADER "../../include/client/user_functions.hpp"
)
install(
TARGETS gkfs_user_lib
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gkfs
)
if (GKFS_BUILD_USER_LIB_AUTOINIT)
set_target_properties(gkfs_user_lib_autoinit
PROPERTIES
PUBLIC_HEADER "../../include/client/void_syscall_intercept.hpp"
PUBLIC_HEADER "../../include/client/user_functions.hpp"
)
install(
TARGETS gkfs_user_lib
TARGETS gkfs_user_lib_autoinit
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gkfs
)
endif()
endif()
if (GKFS_BUILD_LIBC_INTERCEPTION)
set_target_properties(gkfs_libc_intercept
PROPERTIES
PUBLIC_HEADER "../../include/client/void_syscall_intercept.hpp"
PUBLIC_HEADER "../../include/client/user_functions.hpp"
)
install(
TARGETS gkfs_intercept
TARGETS gkfs_libc_intercept
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gkfs
)
endif ()
if (GKFS_BUILD_LIBC_INTERCEPTION_MANUAL)
set_target_properties(gkfs_libc_intercept_manual
PROPERTIES
PUBLIC_HEADER "../../include/client/void_syscall_intercept.hpp"
PUBLIC_HEADER "../../include/client/user_functions.hpp"
)
install(
TARGETS gkfs_libc_intercept_manual
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gkfs
)
endif ()
......@@ -48,6 +48,8 @@
#include <client/rpc/forward_data_proxy.hpp>
#include <client/open_dir.hpp>
#include <client/cache.hpp>
#include <string>
#include <string_view>
#include <common/path_util.hpp>
#ifdef GKFS_ENABLE_CLIENT_METRICS
......@@ -60,6 +62,7 @@ extern "C" {
#include <linux/kernel.h> // used for definition of alignment macros
#include <sys/statfs.h>
#include <sys/statvfs.h>
#include <linux/stat.h>
}
using namespace std;
......@@ -70,36 +73,6 @@ using namespace std;
*/
#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
/*
* linux_dirent is used in getdents() but is privately defined in the linux
* kernel: fs/readdir.c.
*/
struct linux_dirent {
#ifndef __USE_FILE_OFFSET64
unsigned long d_ino;
unsigned long d_off;
#else
uint64_t d_ino;
int64_t d_off;
#endif
unsigned short d_reclen;
unsigned char d_type; // Does it break dirents?
char d_name[1];
};
/*
* linux_dirent64 is used in getdents64() and defined in the linux kernel:
* include/linux/dirent.h. However, it is not part of the kernel-headers and
* cannot be imported.
*/
struct linux_dirent64 {
uint64_t d_ino;
int64_t d_off;
unsigned short d_reclen;
unsigned char d_type;
char d_name[1]; // originally `char d_name[0]` in kernel, but ISO C++
// forbids zero-size array 'd_name'
};
struct dirent_extended {
size_t size;
time_t ctime;
......@@ -145,6 +118,65 @@ check_parent_dir(const std::string& path) {
namespace gkfs::syscall {
/**
* @brief generate_lock_file
* @param path
* @param increase
*
* Creates, if it does not exist, a lock file, path+".lockgekko", empty
* If increase is true, increase the size of the file +1
* if increase is false, decrease the size of the file -1
* If size == 0, delete the file
* Using calls : forward_create, forward_stat, forward_remove, forward_decr_size
* and forward_update_metadentry_size Proxy not supported
*/
void
generate_lock_file(const std::string& path, bool increase) {
auto lock_path = path + ".lockgekko";
if(increase) {
auto md = gkfs::utils::get_metadata(lock_path);
if(!md) {
gkfs::rpc::forward_create(lock_path, 0777 | S_IFREG, 0);
}
gkfs::rpc::forward_update_metadentry_size(lock_path, 1, 0, false, 0);
} else {
auto md = gkfs::utils::get_metadata(lock_path);
if(md) {
if(md.value().size() == 1) {
LOG(DEBUG, "Deleting Lock file {}", lock_path);
gkfs::rpc::forward_remove(lock_path, false, 0);
} else {
gkfs::rpc::forward_decr_size(lock_path, md.value().size() - 1,
0);
}
}
}
}
/**
* @brief generate_lock_file
* @param path
*
* Test if the lock file exists, if it exists, wait 0.5 second and check again
* (max 80 times) Using calls : forward_stat
*/
void
test_lock_file(const std::string& path) {
auto lock_path = path + ".lockgekko";
auto md = gkfs::utils::get_metadata(lock_path);
if(md) {
LOG(DEBUG, "Lock file exists {} --> {}", lock_path, md->size());
for(int i = 0; i < 80; i++) {
if(!md) {
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(500));
md = gkfs::utils::get_metadata(lock_path);
}
}
}
/**
* gkfs wrapper for open() system calls
* errno may be set
......@@ -197,9 +229,14 @@ gkfs_open(const std::string& path, mode_t mode, int flags) {
return -1;
}
} else {
// file was successfully created. Add to filemap
return CTX->file_map()->add(
auto fd = CTX->file_map()->add(
std::make_shared<gkfs::filemap::OpenFile>(path, flags));
// CREATE_MODE
if(CTX->protect_files_generator()) {
generate_lock_file(path, true);
}
// file was successfully created. Add to filemap
return fd;
}
} else {
auto md_ = gkfs::utils::get_metadata(path);
......@@ -234,7 +271,8 @@ gkfs_open(const std::string& path, mode_t mode, int flags) {
// get renamed path from target and retrieve metadata from it
auto md_ = gkfs::utils::get_metadata(md.target_path());
new_path = md.target_path();
while(!md_.value().target_path().empty()) {
while(!md_.value().target_path().empty() and
md_.value().blocks() != -1) {
new_path = md_.value().target_path();
md_ = gkfs::utils::get_metadata(md_.value().target_path(),
false);
......@@ -256,7 +294,7 @@ gkfs_open(const std::string& path, mode_t mode, int flags) {
return -1;
}
}
// RENAMED OR SYMLINK NOT PROTECTED
return CTX->file_map()->add(
std::make_shared<gkfs::filemap::OpenFile>(new_path, flags));
}
......@@ -276,9 +314,19 @@ gkfs_open(const std::string& path, mode_t mode, int flags) {
return -1;
}
}
return CTX->file_map()->add(
// NORMAL OPEN
auto fd = CTX->file_map()->add(
std::make_shared<gkfs::filemap::OpenFile>(path, flags));
if(CTX->protect_files_consumer()) {
test_lock_file(path);
}
if(CTX->protect_files_generator()) {
generate_lock_file(path, true);
}
return fd;
}
/**
......@@ -296,6 +344,9 @@ gkfs_create(const std::string& path, mode_t mode) {
case 0:
mode |= S_IFREG;
break;
#ifdef HAS_SYMLINKS
case S_IFLNK:
#endif
case S_IFREG: // intentionally fall-through
case S_IFDIR:
break;
......@@ -342,6 +393,24 @@ gkfs_create(const std::string& path, mode_t mode) {
return 0;
}
/**
* gkfs wrapper for remove() libc call
* removes files with unlink(), see gkfs_remove()
* and directories with rmdir(), see gkfs_rmdir()
*/
int
gkfs_libcremove(const std::string& path) {
auto md = gkfs::utils::get_metadata(path);
if(!md) {
return -1;
}
if(S_ISDIR(md->mode())) {
return gkfs_rmdir(path);
} else {
return gkfs_remove(path);
}
}
/**
* gkfs wrapper for unlink() system calls
* errno may be set
......@@ -350,7 +419,7 @@ gkfs_create(const std::string& path, mode_t mode) {
*/
int
gkfs_remove(const std::string& path) {
#ifdef HAS_SYMLINKS
#ifdef HAS_RENAME
auto md = gkfs::utils::get_metadata(path);
if(!md) {
......@@ -362,16 +431,20 @@ gkfs_remove(const std::string& path) {
errno = EISDIR;
return -1;
}
if(md.value().blocks() == -1) {
errno = ENOENT;
return -1;
} else {
if(!md->is_link()) {
if(!md.value().target_path().empty()) {
auto md_ = gkfs::utils::get_metadata(md.value().target_path());
std::string new_path = md.value().target_path();
while(!md.value().target_path().empty()) {
while(!md.value().target_path().empty() and
md.value().blocks() != -1) {
new_path = md.value().target_path();
md = gkfs::utils::get_metadata(md.value().target_path(), false);
md = gkfs::utils::get_metadata(md.value().target_path(),
false);
if(!md) {
return -1;
}
......@@ -384,8 +457,9 @@ gkfs_remove(const std::string& path) {
}
}
}
}
#endif // HAS_RENAME
#endif // HAS_SYMLINKS
int err = 0;
if(gkfs::config::proxy::fwd_remove && CTX->use_proxy()) {
err = gkfs::rpc::forward_remove_proxy(path, false);
......@@ -423,7 +497,7 @@ gkfs_access(const std::string& path, const int mask, bool follow_links) {
return -1;
} else {
while(!md.value().target_path().empty()) {
while(!md.value().target_path().empty() and md.value().blocks() != -1) {
LOG(DEBUG, "File exist but it is renamed '{} -> {}'", path,
md.value().target_path());
md = gkfs::utils::get_metadata(md.value().target_path(), false);
......@@ -439,7 +513,7 @@ gkfs_access(const std::string& path, const int mask, bool follow_links) {
return 0;
}
#ifdef HAS_SYMLINKS
#ifdef HAS_RENAME
/**
* gkfs wrapper for rename() system calls
......@@ -455,11 +529,13 @@ gkfs_access(const std::string& path, const int mask, bool follow_links) {
int
gkfs_rename(const string& old_path, const string& new_path) {
auto md_old = gkfs::utils::get_metadata(old_path, false);
std::string original_path = old_path;
// if the file is not found, or it is a renamed one cancel.
if(!md_old || md_old.value().blocks() == -1) {
return -1;
}
auto md_new = gkfs::utils::get_metadata(new_path, false);
if(md_new) {
// the new file exists... check for circular...
......@@ -469,22 +545,11 @@ gkfs_rename(const string& old_path, const string& new_path) {
// the original file.
LOG(DEBUG, "Destroying Circular Rename '{}' --> '{}'", old_path,
new_path);
gkfs::metadata::MetadentryUpdateFlags flags{};
flags.atime = false;
flags.mtime = false;
flags.ctime = false;
flags.blocks = true;
flags.mode = false;
flags.size = false;
flags.uid = false;
flags.gid = false;
flags.link_count = false;
md_old.value().blocks(0);
md_old.value().target_path("");
auto err = gkfs::rpc::forward_update_metadentry(
new_path, md_old.value(), flags, 0);
// We update the target_path
auto err = gkfs::rpc::forward_rename(new_path, "", md_old.value());
if(err) {
errno = err;
return -1;
......@@ -502,18 +567,47 @@ gkfs_rename(const string& old_path, const string& new_path) {
return 0;
}
return -1;
} else {
if(!md_old.value().target_path().empty()) {
// the file is a renamed one, we need to get the metadata of the
// original file. (There will be only one level)
original_path = md_old.value().target_path();
if(!S_ISLNK(md_old->mode())) {
md_old = gkfs::utils::get_metadata(original_path, false);
if(!md_old) {
return -1;
}
}
auto err = gkfs::rpc::forward_rename(old_path, new_path, md_old.value());
auto is_dir = false;
if(S_ISDIR(md_old->mode()))
is_dir = true;
// Remove intermediate file
gkfs::rpc::forward_remove(old_path, is_dir, CTX->get_replicas());
}
int err = 0;
if(!S_ISLNK(md_old->mode())) {
err = gkfs::rpc::forward_rename(original_path, new_path,
md_old.value());
} else {
// Was a link so do a forward symlink to regenerate it
err = gkfs_mk_symlink(new_path, original_path);
}
if(err) {
errno = err;
return -1;
}
}
return 0;
}
#endif
#endif
/**
* gkfs wrapper for stat() system calls
......@@ -524,27 +618,41 @@ gkfs_rename(const string& old_path, const string& new_path) {
* @return 0 on success, -1 on failure
*/
int
gkfs_stat(const string& path, struct stat* buf, bool follow_links) {
gkfs_stat(const string& path, struct stat* buf, bool follow_links,
bool bypass_rename) {
auto md = gkfs::utils::get_metadata(path, follow_links);
if(!md) {
return -1;
}
#ifdef HAS_SYMLINKS
std::string new_path = path;
#ifdef HAS_RENAME
if(md->is_link() == false) {
if(md.value().blocks() == -1) {
// This may not be correct in the case of fstat,
// then we will check bypass_rename
if(!bypass_rename) {
errno = ENOENT;
return -1;
}
} else {
while(!md.value().target_path().empty()) {
while(!md.value().target_path().empty() and
md.value().blocks() != -1) {
new_path = md.value().target_path();
md = gkfs::utils::get_metadata(md.value().target_path(), false);
if(!md) {
return -1;
}
}
if(md.value().blocks() == -1)
md.value().blocks(md->size() / 4096);
}
}
#endif
#endif
gkfs::utils::metadata_to_stat(path, *md, *buf);
// Stat should use new_path in order that the inode of a renamed file is
// equal to the original
gkfs::utils::metadata_to_stat(new_path, *md, *buf);
return 0;
}
......@@ -565,25 +673,30 @@ int
gkfs_statx(int dirfs, const std::string& path, int flags, unsigned int mask,
struct statx* buf, bool follow_links) {
auto md = gkfs::utils::get_metadata(path, follow_links);
if(!md) {
return -1;
}
#ifdef HAS_SYMLINKS
#ifdef HAS_RENAME
if(md->is_link() == false) {
if(md.value().blocks() == -1) {
errno = ENOENT;
return -1;
} else {
while(!md.value().target_path().empty()) {
while(!md.value().target_path().empty() and
md.value().blocks() != -1) {
md = gkfs::utils::get_metadata(md.value().target_path(), false);
if(!md) {
return -1;
}
}
if(md.value().blocks() == -1)
md.value().blocks(md->size() / 4096);
}
}
#endif
#endif
struct stat tmp{};
gkfs::utils::metadata_to_stat(path, *md, tmp);
......@@ -843,14 +956,14 @@ gkfs_truncate(const std::string& path, off_t length) {
}
// If rename is enabled we need to check if the file is renamed
#ifdef HAS_SYMLINKS
#ifdef HAS_RENAME
if(md.value().blocks() == -1) {
errno = ENOENT;
return -1;
} else if(!md.value().target_path().empty()) {
std::string new_path;
while(!md.value().target_path().empty()) {
while(!md.value().target_path().empty() and md.value().blocks() != -1) {
new_path = md.value().target_path();
md = gkfs::utils::get_metadata(md.value().target_path());
}
......@@ -864,7 +977,6 @@ gkfs_truncate(const std::string& path, off_t length) {
}
return gkfs_truncate(new_path, size, length);
}
#endif
#endif
auto size = md->size();
......@@ -876,7 +988,7 @@ gkfs_truncate(const std::string& path, off_t length) {
errno = EINVAL;
return -1;
}
gkfs_lseek(output_fd, 0, SEEK_END);
gkfs_lseek(output_fd, (off64_t) 0, SEEK_END);
ssize_t n = static_cast<unsigned long>(length) - size;
// Zeroes the buffer. All make_* are value initialized
auto buf = std::make_unique<char[]>(n);
......@@ -1480,6 +1592,14 @@ gkfs_getdents(unsigned int fd, struct linux_dirent* dirp, unsigned int count) {
while(pos < open_dir->size()) {
// get dentry fir current position
auto de = open_dir->getdent(pos);
if(CTX->protect_files_consumer() or CTX->protect_files_generator()) {
// if de.name ends with lockgekko jump to the next file
if(de.name().size() >= 10 &&
de.name().substr(de.name().size() - 10) == ".lockgekko") {
pos++;
continue;
}
}
/*
* Calculate the total dentry size within the kernel struct
* `linux_dirent` depending on the file name size. The size is then
......@@ -1549,6 +1669,14 @@ gkfs_getdents64(unsigned int fd, struct linux_dirent64* dirp,
struct linux_dirent64* current_dirp = nullptr;
while(pos < open_dir->size()) {
auto de = open_dir->getdent(pos);
if(CTX->protect_files_consumer() or CTX->protect_files_generator()) {
// if de.name ends with lockgekko jump to the next file
if(de.name().size() >= 10 &&
de.name().substr(de.name().size() - 10) == ".lockgekko") {
pos++;
continue;
}
}
/*
* Calculate the total dentry size within the kernel struct
* `linux_dirent` depending on the file name size. The size is then
......@@ -1644,6 +1772,11 @@ gkfs_close(unsigned int fd) {
CTX->file_map()->get(fd)->path());
}
}
if(CTX->protect_files_generator()) {
auto path = CTX->file_map()->get(fd)->path();
generate_lock_file(path, false);
}
// No call to the daemon is required
CTX->file_map()->remove(fd);
return 0;
......@@ -1661,7 +1794,7 @@ gkfs_close(unsigned int fd) {
}
#ifdef HAS_SYMLINKS
#ifdef GKFS_ENABLE_UNUSED_FUNCTIONS
// #ifdef GKFS_ENABLE_UNUSED_FUNCTIONS
/**
* gkfs wrapper for make symlink() system calls
* errno may be set
......@@ -1680,6 +1813,7 @@ gkfs_mk_symlink(const std::string& path, const std::string& target_path) {
* So that application know we don't support link to directory.
*/
auto target_md = gkfs::utils::get_metadata(target_path, false);
std::string new_path = target_path;
if(target_md) {
auto trg_mode = target_md->mode();
if(!(S_ISREG(trg_mode) || S_ISLNK(trg_mode))) {
......@@ -1694,14 +1828,22 @@ gkfs_mk_symlink(const std::string& path, const std::string& target_path) {
return -1;
}
// Path should exists
auto link_md = gkfs::utils::get_metadata(path, false);
if(link_md) {
LOG(DEBUG, "Link exists: '{}'", path);
LOG(DEBUG, "Link does exists: '{}'", path);
errno = EEXIST;
return -1;
}
LOG(DEBUG, "Create file: {}", path);
// create target_path file (we create it regular)
auto create = gkfs_create(path, 0);
if(create) {
errno = create;
return -1;
}
auto err = gkfs::rpc::forward_mk_symlink(path, target_path);
auto err = gkfs::rpc::forward_mk_symlink(path, new_path);
if(err) {
errno = err;
return -1;
......@@ -1744,7 +1886,7 @@ gkfs_readlink(const std::string& path, char* buf, int bufsize) {
std::strcpy(buf + CTX->mountdir().size(), md->target_path().c_str());
return path_size;
}
#endif
// #endif
#endif
......@@ -1764,6 +1906,13 @@ gkfs_get_file_list(const std::string& path) {
while(pos < open_dir->size()) {
auto de = open_dir->getdent(pos++);
if(CTX->protect_files_consumer() or CTX->protect_files_generator()) {
// if de.name ends with lockgekko jump to the next file
if(de.name().size() >= 10 &&
de.name().substr(de.name().size() - 10) == ".lockgekko") {
continue;
}
}
file_list.push_back(de.name());
}
return file_list;
......