Skip to content
GitLab
Explore
Sign in
Show whitespace changes
Inline
Side-by-side
src/client/gkfs_libc.cpp
0 → 100644
View file @
5c0ec2a1
/*
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
<iostream>
#include
<dlfcn.h>
#include
<cstdio>
#include
<cstdarg>
#include
<client/gkfs_libc.hpp>
#include
<client/user_functions.hpp>
#include
<client/hooks.hpp>
#include
<atomic>
#include
<client/preload_context.hpp>
#include
<client/preload.hpp>
#include
<client/open_file_map.hpp>
#include
<common/path_util.hpp>
#include
<client/open_dir.hpp>
#include
<linux/const.h>
#include
<client/logging.hpp>
#include
<sstream>
#include
<aio.h>
#include
<signal.h>
std
::
atomic
<
bool
>
activated
{
false
};
std
::
atomic
<
bool
>
initializing
{
false
};
/**
* @brief Notes: we should return -1, and set up errno correctly on libc
* interception Actually it is done fine on nearly all operations, but we should
* take care
*/
// Define a debug macro, can be easily disabled
#define GKFS_TRACE
#ifdef GKFS_DEBUG_BUILD
#ifdef GKFS_TRACE
#define DEBUG_INFO(...) \
do { \
LOG(DEBUG, __VA_ARGS__); \
} while(0)
#else
#define DEBUG_INFO(...)
/* No debug output */
#endif
#else
#define DEBUG_INFO(...)
#endif
// set to store void * addr, fd, length and offset
std
::
set
<
std
::
tuple
<
void
*
,
int
,
size_t
,
off_t
>>
mmap_set
;
void
initializeGekko
()
{
// if the activated is false call init
// Create a global mutex
if
(
!
activated
and
!
initializing
)
{
/* DEBUG_INFO("%d [BYPASS] >> DEACTIVATED GEKKOFS.... \n",
getpid()); initializing = true; init_libc(); activated = true;
initializing = false;
DEBUG_INFO("%d [BYPASS] >> ACTIVATED GEKKOFS.... \n", getpid());
*/
}
}
#define STRIP_PARENS(...) __VA_ARGS__
void
log_arguments
(
const
char
*
symbol
)
{
DEBUG_INFO
(
"[BYPASS] {}"
,
symbol
);
}
// Variadic case: 1+ arguments
template
<
typename
...
Args
>
void
log_arguments
(
const
char
*
symbol
,
Args
&&
...
args
)
{
std
::
stringstream
ss
;
ss
<<
"[BYPASS] Calling "
<<
symbol
<<
" with arguments: "
;
((
ss
<<
"["
<<
typeid
(
Args
).
name
()
<<
"] "
<<
args
<<
" "
),
...);
DEBUG_INFO
(
"{}"
,
ss
.
str
());
}
// Variadic case: 1+ arguments
template
<
typename
...
Args
>
void
log_argumentsx
(
const
char
*
symbol
,
Args
&&
...
args
)
{
std
::
stringstream
ss
;
ss
<<
"[BYPASS-ERROR] Calling "
<<
symbol
<<
" with arguments: "
;
((
ss
<<
"["
<<
typeid
(
Args
).
name
()
<<
"] "
<<
args
<<
" "
),
...);
DEBUG_INFO
(
"{}"
,
ss
.
str
());
}
/**
* @brief Macro to declare and implement a wrapper for a standard library
* function using dlsym.
*
* This macro simplifies the process of creating wrappers for functions we want
* to intercept. It declares a static function pointer to the real function and
* defines a wrapper function that:
* 1. Lazily loads the real function address using dlsym on the first call.
* 2. Handles dlsym errors by printing to stderr and returning an error value.
* 3. Calls the real function if loaded successfully.
*
* @param return_type The return type of the function.
* @param func_name The name of the function (used for wrapper and real function
* pointer naming).
* @param params The parameter list of the function (e.g., `(int fd, const char*
* path)`).
* @param args The argument list to pass to the real function (e.g., `(fd,
* path)`).
* @param symbol_name The symbol name to look up with dlsym (usually the same as
* `func_name`).
*/
#define DLSYM_WRAPPER(return_type, func_name, params, args, symbol_name) \
static return_type(*real_##func_name) params = nullptr; \
return_type dlsym_##func_name params { \
if(!real_##func_name) { \
real_##func_name = reinterpret_cast<return_type(*) params>( \
dlsym(RTLD_NEXT, symbol_name)); \
if(!real_##func_name) { \
fprintf(stderr, "dlsym failed for %s: %s\n", symbol_name, \
dlerror()); \
errno = ENOSYS;
/* Set errno to ENOSYS to indicate function \
not supported */
\
return (return_type) - 1;
/* Return error value */
\
} \
} \
log_arguments(symbol_name, STRIP_PARENS args); \
return real_##func_name args; \
}
#define NOLOGDLSYM_WRAPPER(return_type, func_name, params, args, symbol_name) \
static return_type(*real_##func_name) params = nullptr; \
return_type dlsym_##func_name params { \
if(!real_##func_name) { \
real_##func_name = reinterpret_cast<return_type(*) params>( \
dlsym(RTLD_NEXT, symbol_name)); \
if(!real_##func_name) { \
fprintf(stderr, "dlsym failed for %s: %s\n", symbol_name, \
dlerror()); \
errno = ENOSYS;
/* Set errno to ENOSYS to indicate function \
not supported */
\
return (return_type) - 1;
/* Return error value */
\
} \
} \
return real_##func_name args; \
}
#define CDLSYM_WRAPPER(return_type, func_name, params, args, symbol_name) \
static return_type(*real_##func_name) params = nullptr; \
return_type dlsym_##func_name params { \
if(!real_##func_name) { \
real_##func_name = reinterpret_cast<return_type(*) params>( \
dlsym(RTLD_NEXT, symbol_name)); \
if(!real_##func_name) { \
fprintf(stderr, "dlsym failed for %s: %s\n", symbol_name, \
dlerror()); \
errno = ENOSYS;
/* Set errno to ENOSYS to indicate function \
not supported */
\
return (return_type) - 1;
/* Return error value */
\
} \
} \
log_argumentsx(symbol_name, STRIP_PARENS args); \
return real_##func_name args; \
} \
return_type func_name params { \
return dlsym_##func_name args; \
}
// DLSYM_WRAPPER(int, open, (char* path, int flags, mode_t mode), (path, flags,
// mode), "open")
DLSYM_WRAPPER
(
int
,
open2
,
(
const
char
*
path
,
int
flags
,
mode_t
mode
),
(
path
,
flags
,
mode
),
"open"
)
DLSYM_WRAPPER
(
int
,
openat
,
(
int
fd
,
const
char
*
path
,
int
flags
,
mode_t
mode
),
(
fd
,
path
,
flags
,
mode
),
"openat"
)
DLSYM_WRAPPER
(
int
,
open64
,
(
const
char
*
path
,
int
flags
,
mode_t
mode
),
(
path
,
flags
,
mode
),
"open64"
)
DLSYM_WRAPPER
(
int
,
close
,
(
int
fd
),
(
fd
),
"close"
)
DLSYM_WRAPPER
(
int
,
close_range
,
(
unsigned
int
low
,
unsigned
int
high
,
int
flags
),
(
low
,
high
,
flags
),
"close_range"
)
DLSYM_WRAPPER
(
int
,
creat
,
(
const
char
*
path
,
mode_t
mode
),
(
path
,
mode
),
"creat"
)
DLSYM_WRAPPER
(
int
,
ftruncate
,
(
int
fd
,
off_t
length
),
(
fd
,
length
),
"ftruncate"
)
DLSYM_WRAPPER
(
ssize_t
,
read
,
(
int
fd
,
void
*
buf
,
size_t
nbyte
),
(
fd
,
buf
,
nbyte
),
"read"
)
DLSYM_WRAPPER
(
ssize_t
,
write
,
(
int
fd
,
const
void
*
buf
,
size_t
nbyte
),
(
fd
,
buf
,
nbyte
),
"write"
)
DLSYM_WRAPPER
(
ssize_t
,
readv
,
(
int
fd
,
const
struct
iovec
*
iov
,
int
iovcnt
),
(
fd
,
iov
,
iovcnt
),
"readv"
)
DLSYM_WRAPPER
(
ssize_t
,
writev
,
(
int
fd
,
const
struct
iovec
*
iov
,
int
iovcnt
),
(
fd
,
iov
,
iovcnt
),
"writev"
)
DLSYM_WRAPPER
(
ssize_t
,
preadv
,
(
int
fd
,
const
struct
iovec
*
iov
,
int
iovcnt
,
off_t
offset
),
(
fd
,
iov
,
iovcnt
,
offset
),
"preadv"
)
DLSYM_WRAPPER
(
ssize_t
,
pwritev
,
(
int
fd
,
const
struct
iovec
*
iov
,
int
iovcnt
,
off_t
offset
),
(
fd
,
iov
,
iovcnt
,
offset
),
"pwritev"
)
DLSYM_WRAPPER
(
ssize_t
,
preadv2
,
(
int
fd
,
const
struct
iovec
*
iov
,
int
iovcnt
,
off_t
offset
,
int
flags
),
(
fd
,
iov
,
iovcnt
,
offset
,
flags
),
"preadv2"
)
DLSYM_WRAPPER
(
ssize_t
,
pwritev2
,
(
int
fd
,
const
struct
iovec
*
iov
,
int
iovcnt
,
off_t
offset
,
int
flags
),
(
fd
,
iov
,
iovcnt
,
offset
,
flags
),
"pwritev2"
)
DLSYM_WRAPPER
(
int
,
mkdir
,
(
const
char
*
path
,
mode_t
mode
),
(
path
,
mode
),
"mkdir"
)
DLSYM_WRAPPER
(
int
,
mkdirat
,
(
int
dfd
,
const
char
*
path
,
mode_t
mode
),
(
dfd
,
path
,
mode
),
"mkdirat"
)
DLSYM_WRAPPER
(
int
,
rmdir
,
(
const
char
*
path
),
(
path
),
"rmdir"
)
DLSYM_WRAPPER
(
ssize_t
,
pread
,
(
int
fd
,
void
*
buf
,
size_t
count
,
off_t
offset
),
(
fd
,
buf
,
count
,
offset
),
"pread"
)
DLSYM_WRAPPER
(
ssize_t
,
pwrite
,
(
int
fd
,
const
void
*
buf
,
size_t
count
,
off_t
offset
),
(
fd
,
buf
,
count
,
offset
),
"pwrite"
)
DLSYM_WRAPPER
(
ssize_t
,
pread64
,
(
int
fd
,
void
*
buf
,
size_t
count
,
off_t
offset
),
(
fd
,
buf
,
count
,
offset
),
"pread64"
)
DLSYM_WRAPPER
(
ssize_t
,
pwrite64
,
(
int
fd
,
const
void
*
buf
,
size_t
count
,
off_t
offset
),
(
fd
,
buf
,
count
,
offset
),
"pwrite64"
)
DLSYM_WRAPPER
(
int
,
mkstemp
,
(
char
*
templates
),
(
templates
),
"mkstemp"
)
DLSYM_WRAPPER
(
off_t
,
lseek
,
(
int
fd
,
off_t
offset
,
int
whence
),
(
fd
,
offset
,
whence
),
"lseek"
)
DLSYM_WRAPPER
(
off64_t
,
lseek64
,
(
int
fd
,
off64_t
offset
,
int
whence
),
(
fd
,
offset
,
whence
),
"lseek64"
)
DLSYM_WRAPPER
(
int
,
lxstat64
,
(
int
ver
,
const
char
*
path
,
struct
stat64
*
buf
),
(
ver
,
path
,
buf
),
"__lxstat64"
)
DLSYM_WRAPPER
(
int
,
xstat64
,
(
int
ver
,
const
char
*
path
,
struct
stat64
*
buf
),
(
ver
,
path
,
buf
),
"__xstat64"
)
DLSYM_WRAPPER
(
int
,
fxstat64
,
(
int
ver
,
int
fd
,
struct
stat64
*
buf
),
(
ver
,
fd
,
buf
),
"__fxstat64"
)
DLSYM_WRAPPER
(
int
,
__lxstat
,
(
int
ver
,
const
char
*
path
,
struct
stat
*
buf
),
(
ver
,
path
,
buf
),
"__lxstat"
)
DLSYM_WRAPPER
(
int
,
lstat
,
(
const
char
*
path
,
struct
stat
*
buf
),
(
path
,
buf
),
"lstat"
)
DLSYM_WRAPPER
(
int
,
lstat64
,
(
const
char
*
path
,
struct
stat64
*
buf
),
(
path
,
buf
),
"lstat64"
)
DLSYM_WRAPPER
(
int
,
stat
,
(
int
ver
,
const
char
*
path
,
struct
stat
*
buf
),
(
ver
,
path
,
buf
),
"__xstat"
)
DLSYM_WRAPPER
(
int
,
__fxstat
,
(
int
ver
,
int
fd
,
struct
stat
*
buf
),
(
ver
,
fd
,
buf
),
"__fxstat"
)
DLSYM_WRAPPER
(
int
,
fstat
,
(
int
fd
,
struct
stat
*
buf
),
(
fd
,
buf
),
"fstat"
)
DLSYM_WRAPPER
(
int
,
fstat64
,
(
int
fd
,
struct
stat64
*
buf
),
(
fd
,
buf
),
"fstat64"
)
DLSYM_WRAPPER
(
int
,
fxstatat
,
(
int
ver
,
int
dfd
,
const
char
*
path
,
struct
stat
*
buf
,
int
flags
),
(
ver
,
dfd
,
path
,
buf
,
flags
),
"__fxstatat"
)
DLSYM_WRAPPER
(
int
,
fstatat
,
(
int
dfd
,
const
char
*
path
,
struct
stat
*
buf
,
int
flags
),
(
dfd
,
path
,
buf
,
flags
),
"fstatat"
)
DLSYM_WRAPPER
(
int
,
fstatat64
,
(
int
dfd
,
const
char
*
path
,
struct
stat64
*
buf
,
int
flags
),
(
dfd
,
path
,
buf
,
flags
),
"fstatat64"
)
DLSYM_WRAPPER
(
int
,
statx
,
(
int
dirfd
,
const
char
*
path
,
int
flags
,
unsigned
int
mask
,
struct
statx
*
buf
),
(
dirfd
,
path
,
flags
,
mask
,
buf
),
"statx"
)
DLSYM_WRAPPER
(
int
,
rename
,
(
const
char
*
oldpath
,
const
char
*
newpath
),
(
oldpath
,
newpath
),
"rename"
)
DLSYM_WRAPPER
(
int
,
renameat
,
(
int
olddirfd
,
const
char
*
oldpath
,
int
newdirfd
,
const
char
*
newpath
),
(
olddirfd
,
oldpath
,
newdirfd
,
newpath
),
"renameat"
)
DLSYM_WRAPPER
(
int
,
renameat2
,
(
int
olddirfd
,
const
char
*
oldpath
,
int
newdirfd
,
const
char
*
newpath
,
unsigned
int
flags
),
(
olddirfd
,
oldpath
,
newdirfd
,
newpath
,
flags
),
"renameat2"
)
DLSYM_WRAPPER
(
int
,
remove
,
(
const
char
*
path
),
(
path
),
"remove"
)
DLSYM_WRAPPER
(
int
,
unlink
,
(
const
char
*
path
),
(
path
),
"unlink"
)
DLSYM_WRAPPER
(
DIR
*
,
opendir
,
(
const
char
*
dirname
),
(
dirname
),
"opendir"
)
DLSYM_WRAPPER
(
DIR
*
,
opendir64
,
(
const
char
*
dirname
),
(
dirname
),
"opendir64"
)
DLSYM_WRAPPER
(
struct
dirent
*
,
readdir
,
(
DIR
*
dirp
),
(
dirp
),
"readdir"
)
DLSYM_WRAPPER
(
struct
dirent64
*
,
readdir64
,
(
DIR
*
dirp
),
(
dirp
),
"readdir64"
)
DLSYM_WRAPPER
(
int
,
closedir
,
(
DIR
*
dirp
),
(
dirp
),
"closedir"
)
DLSYM_WRAPPER
(
void
,
seekdir
,
(
DIR
*
dirp
,
long
loc
),
(
dirp
,
loc
),
"seekdir"
)
DLSYM_WRAPPER
(
long
,
telldir
,
(
DIR
*
dirp
),
(
dirp
),
"telldir"
)
/*
static pid_t (*real_fork)(void) = NULL;
static pid_t (*real_vfork)(void) = NULL;
static pid_t (*real_clone)(int (*fn)(void*), void*, int, void*, ...) = NULL;
pid_t
vfork(void) {
if(!real_vfork)
real_vfork =
reinterpret_cast<int (*)(void)>(dlsym(((void*) -1l), "vfork"));
destroy_libc();
pid_t pid = real_vfork();
// vfork child shares memory until execve, so:
if(pid == 0) {
init_libc(); // Dangerous! Must be async-signal-safe
} else {
init_libc();
}
return pid;
}
pid_t
clone(int (*fn)(void*), void* stack, int flags, void* arg, ...) {
if(!real_clone)
real_clone =
reinterpret_cast<decltype(&::clone)>(dlsym(RTLD_NEXT, "clone"));
// Only handle process-creation clones
if(flags & (SIGCHLD | CLONE_VFORK)) {
destroy_libc();
}
pid_t pid = real_clone(fn, stack, flags, arg);
if(flags & (SIGCHLD | CLONE_VFORK)) {
(pid == 0) ? init_libc() : init_libc();
}
return pid;
}
*/
DLSYM_WRAPPER
(
int
,
pipe
,
(
int
pipefd
[
2
]),
(
pipefd
),
"pipe"
)
DLSYM_WRAPPER
(
int
,
dup
,
(
int
fd
),
(
fd
),
"dup"
)
DLSYM_WRAPPER
(
int
,
dup2
,
(
int
fd
,
int
fd2
),
(
fd
,
fd2
),
"dup2"
)
DLSYM_WRAPPER
(
int
,
dup3
,
(
int
fd
,
int
fd2
,
int
flags
),
(
fd
,
fd2
,
flags
),
"dup3"
)
DLSYM_WRAPPER
(
void
,
exit
,
(
int
status
),
(
status
),
"exit"
)
DLSYM_WRAPPER
(
int
,
chdir
,
(
char
*
path
),
(
path
),
"chdir"
)
DLSYM_WRAPPER
(
int
,
fchdir
,
(
int
fd
),
(
fd
),
"fchdir"
)
DLSYM_WRAPPER
(
int
,
chmod
,
(
char
*
path
,
mode_t
mode
),
(
path
,
mode
),
"chmod"
)
DLSYM_WRAPPER
(
int
,
fchmod
,
(
int
fd
,
mode_t
mode
),
(
fd
,
mode
),
"fchmod"
)
DLSYM_WRAPPER
(
int
,
chown
,
(
char
*
path
,
uid_t
owner
,
gid_t
group
),
(
path
,
owner
,
group
),
"chown"
)
static
int
(
*
real_fcntl
)(
int
fd
,
int
cmd
,
...)
=
nullptr
;
DLSYM_WRAPPER
(
int
,
access
,
(
const
char
*
path
,
int
mode
),
(
path
,
mode
),
"access"
)
DLSYM_WRAPPER
(
int
,
faccessat
,
(
int
dfd
,
const
char
*
path
,
int
mode
,
int
flags
),
(
dfd
,
path
,
mode
,
flags
),
"faccessat"
)
DLSYM_WRAPPER
(
char
*
,
realpath
,
(
const
char
*
path
,
char
*
resolved_path
),
(
path
,
resolved_path
),
"realpath"
)
DLSYM_WRAPPER
(
int
,
fsync
,
(
int
fd
),
(
fd
),
"fsync"
)
DLSYM_WRAPPER
(
int
,
flock
,
(
int
fd
,
int
operation
),
(
fd
,
operation
),
"flock"
)
DLSYM_WRAPPER
(
void
*
,
mmap
,
(
void
*
addr
,
size_t
length
,
int
prot
,
int
flags
,
int
fd
,
off_t
offset
),
(
addr
,
length
,
prot
,
flags
,
fd
,
offset
),
"mmap"
)
DLSYM_WRAPPER
(
int
,
msync
,
(
void
*
addr
,
size_t
length
,
int
flags
),
(
addr
,
length
,
flags
),
"msync"
)
DLSYM_WRAPPER
(
int
,
munmap
,
(
void
*
addr
,
size_t
length
),
(
addr
,
length
),
"munmap"
)
DLSYM_WRAPPER
(
FILE
*
,
fopen
,
(
const
char
*
filename
,
const
char
*
mode
),
(
filename
,
mode
),
"fopen"
)
DLSYM_WRAPPER
(
FILE
*
,
fdopen
,
(
int
fd
,
const
char
*
mode
),
(
fd
,
mode
),
"fdopen"
)
DLSYM_WRAPPER
(
FILE
*
,
freopen64
,
(
const
char
*
filename
,
const
char
*
mode
,
FILE
*
stream
),
(
filename
,
mode
,
stream
),
"freopen"
)
DLSYM_WRAPPER
(
int
,
fclose
,
(
FILE
*
stream
),
(
stream
),
"fclose"
)
DLSYM_WRAPPER
(
size_t
,
fread
,
(
void
*
ptr
,
size_t
size
,
size_t
nmemb
,
FILE
*
stream
),
(
ptr
,
size
,
nmemb
,
stream
),
"fread"
)
DLSYM_WRAPPER
(
size_t
,
fwrite
,
(
const
void
*
ptr
,
size_t
size
,
size_t
nmemb
,
FILE
*
stream
),
(
ptr
,
size
,
nmemb
,
stream
),
"fwrite"
)
DLSYM_WRAPPER
(
int
,
fseek
,
(
FILE
*
stream
,
long
int
offset
,
int
whence
),
(
stream
,
offset
,
whence
),
"fseek"
)
DLSYM_WRAPPER
(
long
,
ftell
,
(
FILE
*
stream
),
(
stream
),
"ftell"
)
DLSYM_WRAPPER
(
void
,
rewind
,
(
FILE
*
stream
),
(
stream
),
"rewind"
)
DLSYM_WRAPPER
(
int
,
feof
,
(
FILE
*
stream
),
(
stream
),
"feof"
)
DLSYM_WRAPPER
(
void
,
clearerr
,
(
FILE
*
stream
),
(
stream
),
"clearerr"
)
DLSYM_WRAPPER
(
int
,
fputs
,
(
const
char
*
str
,
FILE
*
stream
),
(
str
,
stream
),
"fputs"
)
NOLOGDLSYM_WRAPPER
(
char
*
,
fgets
,
(
char
*
str
,
int
n
,
FILE
*
stream
),
(
str
,
n
,
stream
),
"fgets"
)
NOLOGDLSYM_WRAPPER
(
int
,
fflush
,
(
FILE
*
stream
),
(
stream
),
"fflush"
)
DLSYM_WRAPPER
(
int
,
fchown
,
(
int
fd
,
uid_t
owner
,
gid_t
group
),
(
fd
,
owner
,
group
),
"fchown"
)
DLSYM_WRAPPER
(
int
,
futimes
,
(
int
fd
,
const
struct
timeval
times
[
2
]),
(
fd
,
times
),
"futimes"
)
DLSYM_WRAPPER
(
int
,
utimes
,
(
const
char
*
path
,
const
struct
timeval
times
[
2
]),
(
path
,
times
),
"utimes"
)
// listxattr
DLSYM_WRAPPER
(
ssize_t
,
listxattr
,
(
const
char
*
path
,
char
*
list
,
size_t
size
),
(
path
,
list
,
size
),
"listxattr"
)
DLSYM_WRAPPER
(
ssize_t
,
llistxattr
,
(
const
char
*
path
,
char
*
list
,
size_t
size
),
(
path
,
list
,
size
),
"llistxattr"
)
DLSYM_WRAPPER
(
ssize_t
,
flistxattr
,
(
int
fd
,
char
*
list
,
size_t
size
),
(
fd
,
list
,
size
),
"flistxattr"
)
// DLSYM_WRAPPER(long, syscall, (long number, char* cmd, void* arg, void* env),
// (number, cmd, arg, env), "syscall")
DLSYM_WRAPPER
(
DIR
*
,
fdopendir
,
(
int
fd
),
(
fd
),
"fdopendir"
)
DLSYM_WRAPPER
(
int
,
scandir
,
(
const
char
*
dirp
,
struct
dirent
***
namelist
,
typeof
(
int
(
const
struct
dirent
*
))
*
filter
,
typeof
(
int
(
const
struct
dirent
**
,
const
struct
dirent
**
))
*
compar
),
(
dirp
,
namelist
,
filter
,
compar
),
"scandir"
)
DLSYM_WRAPPER
(
int
,
symlink
,
(
const
char
*
path1
,
const
char
*
path2
),
(
path1
,
path2
),
"symlink"
)
// NEED HAS_SYMLINKS
DLSYM_WRAPPER
(
ssize_t
,
readlink
,
(
const
char
*
path
,
char
*
buf
,
size_t
bufsize
),
(
path
,
buf
,
bufsize
),
"readlink"
)
DLSYM_WRAPPER
(
ssize_t
,
readlinkat
,
(
int
dfd
,
const
char
*
path
,
char
*
buf
,
size_t
bufsize
),
(
dfd
,
path
,
buf
,
bufsize
),
"readlinkat"
)
/*
CDLSYM_WRAPPER(int, openat2,
(int dfd, const char* path, int flags, mode_t mode),
(dfd, path, flags, mode), "openat2")
CDLSYM_WRAPPER(int, execve,
(const char* filename, char* const argv, char* const envp),
(filename, argv, envp), "execve")
CDLSYM_WRAPPER(int, execveat,
(int dfd, const char* filename, char* const argv[],
char* const envp[], int flags),
(dfd, filename, argv, envp, flags), "execveat")
CDLSYM_WRAPPER(int, execv, (const char* filename, char* const argv[]),
(filename, argv), "execv")
CDLSYM_WRAPPER(int, fexecve, (int fd, const char* filename, char* const argv[]),
(fd, filename, argv), "fexecve")
CDLSYM_WRAPPER(int, execvpe, (const char* filename, char* const argv[], char*
const envp[]), (filename, argv, envp), "execvpe")
CDLSYM_WRAPPER(int, execvp, (const char* filename, char* const
argv[]), (filename, argv), "execvp")
*/
/* not used */
/*static void
convert(struct stat64* src, struct stat* dest) {
dest->st_dev = static_cast<__dev_t>(src->st_dev);
dest->st_ino = static_cast<__ino_t>(src->st_ino);
dest->st_mode = static_cast<__mode_t>(src->st_mode);
dest->st_nlink = static_cast<__nlink_t>(src->st_nlink);
dest->st_uid = static_cast<__uid_t>(src->st_uid);
dest->st_gid = static_cast<__gid_t>(src->st_gid);
dest->st_rdev = static_cast<__dev_t>(src->st_rdev);
dest->st_size = static_cast<__off_t>(src->st_size);
dest->st_blksize = static_cast<__blksize_t>(src->st_blksize);
dest->st_blocks = static_cast<__blkcnt_t>(src->st_blocks);
dest->st_atime = static_cast<__time_t>(src->st_atime);
dest->st_mtime = static_cast<__time_t>(src->st_mtime);
dest->st_ctime = static_cast<__time_t>(src->st_ctime);
}*/
static
void
convert
(
struct
stat
*
src
,
struct
stat64
*
dest
)
{
dest
->
st_dev
=
static_cast
<
__dev_t
>
(
src
->
st_dev
);
dest
->
st_ino
=
static_cast
<
__ino64_t
>
(
src
->
st_ino
);
dest
->
st_mode
=
static_cast
<
__mode_t
>
(
src
->
st_mode
);
dest
->
st_nlink
=
static_cast
<
__nlink_t
>
(
src
->
st_nlink
);
dest
->
st_uid
=
static_cast
<
__uid_t
>
(
src
->
st_uid
);
dest
->
st_gid
=
static_cast
<
__gid_t
>
(
src
->
st_gid
);
dest
->
st_rdev
=
static_cast
<
__dev_t
>
(
src
->
st_rdev
);
dest
->
st_size
=
static_cast
<
__off64_t
>
(
src
->
st_size
);
dest
->
st_blksize
=
static_cast
<
__blksize_t
>
(
src
->
st_blksize
);
dest
->
st_blocks
=
static_cast
<
__blkcnt64_t
>
(
src
->
st_blocks
);
dest
->
st_atime
=
static_cast
<
__time_t
>
(
src
->
st_atime
);
dest
->
st_mtime
=
static_cast
<
__time_t
>
(
src
->
st_mtime
);
dest
->
st_ctime
=
static_cast
<
__time_t
>
(
src
->
st_ctime
);
}
//========================= Path Handling ========================//
/**
* @enum PathStatus
* @brief Enum representing the status of a resolved path.
*
* - External: Path is outside of GekkoFS scope.
* - Internal: Path is within GekkoFS scope.
* - Error: An error occurred during path resolution (errno is set).
*/
enum
class
PathStatus
{
External
,
Internal
,
Error
};
/**
* @brief Resolves a path in the context of GekkoFS.
*
* Determines if a given path (relative to a directory file descriptor `dirfd`)
* is managed by GekkoFS or external. If it's a GekkoFS path, it resolves it
* to an internal representation.
*
* @param dirfd Directory file descriptor for relative path resolution (AT_FDCWD
* for current directory).
* @param path The path to resolve.
* @param resolved Output parameter to store the resolved path string.
* @param flags Optional flags (currently unused in the original code, might be
* for future extensions).
* @return PathStatus indicating whether the path is internal, external, or if
* an error occurred. If an error occurs, errno will be set to indicate the
* specific error (ENOTDIR, EBADF).
*/
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:
return
PathStatus
::
External
;
}
}
bool
is_gkfs_fd
(
int
fd
)
{
return
CTX
->
file_map
()
->
exist
(
fd
);
}
#define GKFS_OPERATION(name, ...) \
if(CTX->interception_enabled() && is_gkfs_fd(fd)) { \
\
auto res = gkfs::syscall::gkfs_##name(__VA_ARGS__); \
DEBUG_INFO("[GKFS] {} -> res {}", fd, res); \
return res; \
}
#define GKFS_PATH_OPERATION(name, dirfd, path, ...) \
if(CTX->interception_enabled()) { \
std::string resolved; \
switch(resolve_gkfs_path(dirfd, path, resolved)) { \
case PathStatus::Internal: \
DEBUG_INFO("[GKFS] {}", resolved); \
return gkfs::syscall::gkfs_##name(resolved, __VA_ARGS__); \
case PathStatus::Error: \
return -1; \
default: \
break; \
} \
}
#define GKFS_PATH_OPERATION_DIR(name, dirfd, path, ...) \
if(CTX->interception_enabled()) { \
std::string resolved; \
switch(resolve_gkfs_path(dirfd, path, resolved)) { \
case PathStatus::Internal: \
DEBUG_INFO("[GKFS] {}", resolved); \
return gkfs::syscall::gkfs_##name(dirfd, resolved, \
__VA_ARGS__); \
case PathStatus::Error: \
return -1; \
default: \
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: \
DEBUG_INFO("[GKFS] {}", resolved); \
return gkfs::syscall::gkfs_##name(resolved); \
case PathStatus::Error: \
return -1; \
default: \
break; \
} \
}
#define GKFS_FALLBACK(func_name, ...) return dlsym_##func_name(__VA_ARGS__);
// File API
int
open
(
const
char
*
path
,
int
flags
,
...)
{
va_list
ap
;
va_start
(
ap
,
flags
);
mode_t
mode
=
va_arg
(
ap
,
mode_t
);
va_end
(
ap
);
initializeGekko
();
GKFS_PATH_OPERATION
(
open
,
AT_FDCWD
,
path
,
mode
,
flags
)
GKFS_FALLBACK
(
open2
,
const_cast
<
char
*>
(
path
),
flags
,
mode
)
}
int
open64
(
const
char
*
path
,
int
flags
,
...)
{
va_list
ap
;
va_start
(
ap
,
flags
);
mode_t
mode
=
va_arg
(
ap
,
mode_t
);
va_end
(
ap
);
initializeGekko
();
GKFS_PATH_OPERATION
(
open
,
AT_FDCWD
,
path
,
mode
,
flags
)
GKFS_FALLBACK
(
open64
,
const_cast
<
char
*>
(
path
),
flags
,
mode
)
}
int
open64
(
const
char
*
path
,
int
flags
,
mode_t
mode
)
{
initializeGekko
();
GKFS_PATH_OPERATION
(
open
,
AT_FDCWD
,
path
,
mode
,
flags
)
GKFS_FALLBACK
(
open64
,
const_cast
<
char
*>
(
path
),
flags
,
mode
)
}
int
openat
(
int
dirfd
,
const
char
*
path
,
int
flags
,
...)
{
va_list
ap
;
mode_t
mode
=
0
;
va_start
(
ap
,
flags
);
mode
=
va_arg
(
ap
,
mode_t
);
va_end
(
ap
);
initializeGekko
();
GKFS_PATH_OPERATION
(
open
,
dirfd
,
path
,
mode
,
flags
)
GKFS_FALLBACK
(
openat
,
dirfd
,
path
,
flags
,
mode
)
}
int
openat64
(
int
dirfd
,
const
char
*
path
,
int
flags
,
...)
{
va_list
ap
;
mode_t
mode
=
0
;
va_start
(
ap
,
flags
);
mode
=
va_arg
(
ap
,
mode_t
);
va_end
(
ap
);
return
openat
(
dirfd
,
path
,
flags
,
mode
);
}
// Generate the rest of the functions to build a libc io interception
int
close
(
int
fd
)
{
initializeGekko
();
GKFS_OPERATION
(
close
,
fd
)
GKFS_FALLBACK
(
close
,
fd
);
}
std
::
vector
<
int
>
get_open_fds
()
{
std
::
vector
<
int
>
fds
;
const
std
::
string
fd_path
=
"/proc/self/fd"
;
DIR
*
dir
=
dlsym_opendir
(
fd_path
.
c_str
());
if
(
!
dir
)
{
perror
(
"opendir() failed"
);
return
fds
;
}
struct
dirent
*
entry
;
while
((
entry
=
dlsym_readdir
(
dir
)))
{
// Skip "." and ".." entries
if
(
entry
->
d_type
==
DT_DIR
)
continue
;
try
{
int
fd
=
std
::
stoi
(
entry
->
d_name
);
// Skip the directory FD itself
if
(
fd
!=
dirfd
(
dir
))
{
fds
.
push_back
(
fd
);
}
}
catch
(
const
std
::
exception
&
)
{
// Ignore non-integer entries
}
}
dlsym_closedir
(
dir
);
/* fds.erase(std::remove_if(fds.begin(), fds.end(),
[](int fd) { return fcntl(fd, F_GETFD) < 0; }),
fds.end());
*/
return
fds
;
}
int
close_range
(
unsigned
low
,
unsigned
high
,
int
flags
)
{
DEBUG_INFO
(
"close_range({}, {}, {})"
,
low
,
high
,
flags
);
return
0
;
auto
fds
=
get_open_fds
();
#ifdef CLOSE_RANGE_UNSHARE
if
(
flags
&
CLOSE_RANGE_UNSHARE
)
{
// Unshare the file descriptor table
DEBUG_INFO
(
"close_range with CLOSE_RANGE_UNSHARE"
);
if
(
unshare
(
CLONE_FILES
)
==
-
1
)
{
perror
(
"unshare() failed"
);
return
-
1
;
}
}
#endif
#ifdef CLOSE_RANGE_CLOEXEC
if
(
flags
&
CLOSE_RANGE_CLOEXEC
)
{
// Close all file descriptors in the range
DEBUG_INFO
(
"close_range with CLOSE_RANGE_CLOEXEC"
);
}
#endif
// Is fd from gekkofs ?
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
for
(
unsigned
int
i
:
fds
)
{
if
(
i
<
low
||
i
>
high
)
continue
;
if
(
is_gkfs_fd
(
i
))
{
DEBUG_INFO
(
"[GKFS] Closing fd {}"
,
i
);
gkfs
::
syscall
::
gkfs_close
(
i
);
}
else
{
DEBUG_INFO
(
"[NONGKFS] Closing fd {}"
,
i
);
close
(
i
);
}
}
return
0
;
}
else
{
std
::
cout
<<
"Not in gekko: close_range"
<<
std
::
endl
;
for
(
unsigned
int
i
:
fds
)
{
if
(
i
<
low
||
i
>
high
)
continue
;
std
::
cout
<<
"Not in gekko: close_range "
<<
i
<<
std
::
endl
;
close
(
i
);
}
}
// We are not in gekko just do it...
// GKFS_FALLBACK(close_range, low, high, flags);
return
0
;
}
int
creat
(
const
char
*
path
,
mode_t
mode
)
{
initializeGekko
();
GKFS_PATH_OPERATION
(
create
,
AT_FDCWD
,
path
,
mode
)
GKFS_FALLBACK
(
creat
,
path
,
mode
);
}
int
ftruncate
(
int
fd
,
off_t
length
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
is_gkfs_fd
(
fd
))
{
std
::
string
path_str
=
CTX
->
file_map
()
->
get
(
fd
)
->
path
();
DEBUG_INFO
(
"[GKFS] {}"
,
path_str
);
return
gkfs
::
syscall
::
gkfs_truncate
(
path_str
,
length
);
}
GKFS_FALLBACK
(
ftruncate
,
fd
,
length
);
}
ssize_t
read
(
int
fd
,
void
*
buf
,
size_t
nbyte
)
{
initializeGekko
();
GKFS_OPERATION
(
read
,
fd
,
buf
,
nbyte
);
GKFS_FALLBACK
(
read
,
fd
,
buf
,
nbyte
);
}
ssize_t
write
(
int
fd
,
const
void
*
buf
,
size_t
nbyte
)
{
initializeGekko
();
GKFS_OPERATION
(
write
,
fd
,
buf
,
nbyte
);
GKFS_FALLBACK
(
write
,
fd
,
buf
,
nbyte
);
}
int
mkdir
(
const
char
*
path
,
mode_t
mode
)
{
initializeGekko
();
DEBUG_INFO
(
"[GKFS] at MKDIR path {} {}"
,
path
,
CTX
->
interception_enabled
());
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
DEBUG_INFO
(
"[GKFS] mkdir path {}"
,
path
);
switch
(
resolve_gkfs_path
(
AT_FDCWD
,
path
,
resolved
))
{
case
PathStatus
::
Internal
:
DEBUG_INFO
(
"[GKFS] mkdir res {}"
,
resolved
);
return
gkfs
::
syscall
::
gkfs_create
(
resolved
,
mode
|
S_IFDIR
);
case
PathStatus
::
Error
:
return
-
1
;
default:
break
;
}
}
GKFS_FALLBACK
(
mkdir
,
path
,
mode
);
}
int
mkdirat
(
int
dirfd
,
const
char
*
path
,
mode_t
mode
)
{
initializeGekko
();
GKFS_PATH_OPERATION
(
create
,
dirfd
,
path
,
mode
|
S_IFDIR
)
GKFS_FALLBACK
(
mkdirat
,
dirfd
,
path
,
mode
);
}
int
rmdir
(
const
char
*
path
)
{
initializeGekko
();
GKFS_PATH_OPERATION1
(
rmdir
,
AT_FDCWD
,
path
)
GKFS_FALLBACK
(
rmdir
,
path
);
}
ssize_t
pread
(
int
fd
,
void
*
buf
,
size_t
count
,
off_t
offset
)
{
initializeGekko
();
GKFS_OPERATION
(
pread
,
fd
,
buf
,
count
,
offset
);
GKFS_FALLBACK
(
pread
,
fd
,
buf
,
count
,
offset
);
}
ssize_t
pwrite
(
int
fd
,
const
void
*
buf
,
size_t
count
,
off_t
offset
)
{
initializeGekko
();
GKFS_OPERATION
(
pwrite
,
fd
,
buf
,
count
,
offset
);
GKFS_FALLBACK
(
pwrite
,
fd
,
buf
,
count
,
offset
);
}
int
mkstemp
(
char
*
templates
)
{
GKFS_FALLBACK
(
mkstemp
,
templates
);
}
off_t
lseek
(
int
fd
,
off_t
offset
,
int
whence
)
{
initializeGekko
();
GKFS_OPERATION
(
lseek
,
fd
,
offset
,
whence
);
GKFS_FALLBACK
(
lseek
,
fd
,
offset
,
whence
);
}
off64_t
lseek64
(
int
fd
,
off64_t
offset
,
int
whence
)
{
initializeGekko
();
GKFS_OPERATION
(
lseek
,
fd
,
offset
,
whence
);
GKFS_FALLBACK
(
lseek64
,
fd
,
offset
,
whence
);
}
ssize_t
pread64
(
int
fd
,
void
*
buf
,
size_t
count
,
off64_t
offset
)
{
initializeGekko
();
GKFS_OPERATION
(
pread
,
fd
,
buf
,
count
,
offset
);
GKFS_FALLBACK
(
pread64
,
fd
,
buf
,
count
,
offset
);
}
ssize_t
pwrite64
(
int
fd
,
const
void
*
buf
,
size_t
count
,
off64_t
offset
)
{
initializeGekko
();
GKFS_OPERATION
(
pwrite
,
fd
,
buf
,
count
,
offset
);
GKFS_FALLBACK
(
pwrite64
,
fd
,
buf
,
count
,
offset
);
}
int
remove
(
const
char
*
path
)
{
initializeGekko
();
GKFS_PATH_OPERATION1
(
libcremove
,
AT_FDCWD
,
path
)
GKFS_FALLBACK
(
remove
,
path
);
}
int
unlink
(
const
char
*
path
)
{
initializeGekko
();
GKFS_PATH_OPERATION1
(
remove
,
AT_FDCWD
,
path
)
GKFS_FALLBACK
(
unlink
,
path
);
}
int
__xstat
(
int
ver
,
const
char
*
path
,
struct
stat
*
buf
)
{
GKFS_PATH_OPERATION
(
stat
,
AT_FDCWD
,
path
,
buf
)
auto
res
=
dlsym_stat
(
ver
,
path
,
buf
);
return
res
;
}
int
__xstat64
(
int
ver
,
const
char
*
path
,
struct
stat64
*
buf
)
{
struct
stat
st
;
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
switch
(
resolve_gkfs_path
(
AT_FDCWD
,
path
,
resolved
))
{
case
PathStatus
::
Internal
:
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved
);
auto
res
=
gkfs
::
syscall
::
gkfs_stat
(
resolved
,
&
st
);
convert
(
&
st
,
buf
);
return
res
;
}
case
PathStatus
::
Error
:
{
return
-
(
*
__errno_location
());
}
default
:
{
}
}
}
auto
res
=
dlsym_stat
(
ver
,
path
,
&
st
);
convert
(
&
st
,
buf
);
return
res
;
}
int
statx
(
int
dirfd
,
const
char
*
path
,
int
flags
,
unsigned
int
mask
,
struct
statx
*
statxbuf
)
{
initializeGekko
();
bool
follow
=
(
flags
&
AT_SYMLINK_NOFOLLOW
)
==
0
;
DEBUG_INFO
(
"[GKFS] {} -> follow {}"
,
path
,
follow
);
GKFS_PATH_OPERATION_DIR
(
statx
,
dirfd
,
path
,
flags
,
mask
,
statxbuf
,
follow
)
GKFS_FALLBACK
(
statx
,
dirfd
,
path
,
flags
,
mask
,
statxbuf
);
}
/**
* @brief lxstat wrapper function. Should be called with resolve_last_link =
* false on the CI.
*
* @param ver
* @param path
* @param buf
* @return int
*/
int
__lxstat
(
int
ver
,
const
char
*
path
,
struct
stat
*
buf
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
switch
(
resolve_gkfs_path
(
AT_FDCWD
,
path
,
resolved
,
0
,
false
))
{
case
PathStatus
::
Internal
:
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved
);
auto
res
=
gkfs
::
syscall
::
gkfs_stat
(
resolved
,
buf
,
false
);
return
res
;
}
case
PathStatus
::
Error
:
{
return
-
(
*
__errno_location
());
}
default
:
{
}
}
}
GKFS_FALLBACK
(
__lxstat
,
ver
,
path
,
buf
);
}
int
__lxstat64
(
int
ver
,
const
char
*
path
,
struct
stat64
*
buf
)
{
initializeGekko
();
struct
stat
st
;
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
switch
(
resolve_gkfs_path
(
AT_FDCWD
,
path
,
resolved
))
{
case
PathStatus
::
Internal
:
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved
);
auto
res
=
gkfs
::
syscall
::
gkfs_stat
(
resolved
,
&
st
,
false
);
convert
(
&
st
,
buf
);
return
res
;
}
case
PathStatus
::
Error
:
{
return
-
(
*
__errno_location
());
}
default
:
{
}
}
}
GKFS_FALLBACK
(
lxstat64
,
ver
,
path
,
buf
);
}
int
__fxstat
(
int
ver
,
int
fd
,
struct
stat
*
buf
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
is_gkfs_fd
(
fd
))
{
DEBUG_INFO
(
"[GKFS] {}"
,
CTX
->
file_map
()
->
get
(
fd
)
->
path
());
return
gkfs
::
syscall
::
gkfs_stat
(
CTX
->
file_map
()
->
get
(
fd
)
->
path
(),
buf
,
true
,
true
);
}
GKFS_FALLBACK
(
__fxstat
,
ver
,
fd
,
buf
);
}
int
__fxstatat
(
int
ver
,
int
dfd
,
const
char
*
path
,
struct
stat
*
buf
,
int
flags
)
{
initializeGekko
();
bool
follow
=
(
flags
&
AT_SYMLINK_NOFOLLOW
)
==
0
;
GKFS_PATH_OPERATION
(
stat
,
AT_FDCWD
,
path
,
buf
,
follow
)
GKFS_FALLBACK
(
fxstatat
,
ver
,
dfd
,
path
,
buf
,
flags
);
}
int
__fxstat64
(
int
ver
,
int
fd
,
struct
stat64
*
buf
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
is_gkfs_fd
(
fd
))
{
struct
stat
st
;
DEBUG_INFO
(
"[GKFS] {}"
,
CTX
->
file_map
()
->
get
(
fd
)
->
path
());
int
res
=
gkfs
::
syscall
::
gkfs_stat
(
CTX
->
file_map
()
->
get
(
fd
)
->
path
(),
&
st
,
true
,
true
);
if
(
res
==
0
)
convert
(
&
st
,
buf
);
return
res
;
}
GKFS_FALLBACK
(
fxstat64
,
ver
,
fd
,
buf
);
}
int
fstat64
(
int
fd
,
struct
stat64
*
buf
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
is_gkfs_fd
(
fd
))
{
struct
stat
st
;
DEBUG_INFO
(
"[GKFS] {}"
,
CTX
->
file_map
()
->
get
(
fd
)
->
path
());
int
res
=
gkfs
::
syscall
::
gkfs_stat
(
CTX
->
file_map
()
->
get
(
fd
)
->
path
(),
&
st
,
true
,
true
);
if
(
res
==
0
)
convert
(
&
st
,
buf
);
return
res
;
}
GKFS_FALLBACK
(
fstat64
,
fd
,
buf
);
}
int
fstat
(
int
fd
,
struct
stat
*
buf
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
is_gkfs_fd
(
fd
))
{
DEBUG_INFO
(
"[GKFS] {}"
,
CTX
->
file_map
()
->
get
(
fd
)
->
path
());
// The fd could be a renamed file, thus when doing gkfs_stat we will get
// a is not found.
int
res
=
gkfs
::
syscall
::
gkfs_stat
(
CTX
->
file_map
()
->
get
(
fd
)
->
path
(),
buf
,
true
,
true
);
return
res
;
}
GKFS_FALLBACK
(
fstat
,
fd
,
buf
);
}
int
__fxstatat64
(
int
ver
,
int
dfd
,
const
char
*
path
,
struct
stat64
*
buf
,
int
flags
)
{
initializeGekko
();
struct
stat
st
;
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
switch
(
resolve_gkfs_path
(
dfd
,
path
,
resolved
))
{
case
PathStatus
::
Internal
:
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved
);
bool
follow
=
(
flags
&
AT_SYMLINK_NOFOLLOW
)
==
0
;
auto
res
=
gkfs
::
syscall
::
gkfs_stat
(
resolved
,
&
st
,
follow
);
convert
(
&
st
,
buf
);
return
res
;
}
case
PathStatus
::
Error
:
{
return
-
(
*
__errno_location
());
}
default
:
{
}
}
}
GKFS_FALLBACK
(
fstatat64
,
dfd
,
path
,
buf
,
flags
);
}
int
stat
(
const
char
*
path
,
struct
stat
*
buf
)
{
initializeGekko
();
GKFS_PATH_OPERATION
(
stat
,
AT_FDCWD
,
path
,
buf
)
GKFS_FALLBACK
(
stat
,
_STAT_VER
,
path
,
buf
);
}
int
stat64
(
const
char
*
path
,
struct
stat64
*
buf
)
{
initializeGekko
();
struct
stat
st
;
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
switch
(
resolve_gkfs_path
(
AT_FDCWD
,
path
,
resolved
))
{
case
PathStatus
::
Internal
:
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved
);
auto
res
=
gkfs
::
syscall
::
gkfs_stat
(
resolved
,
&
st
);
convert
(
&
st
,
buf
);
return
res
;
}
case
PathStatus
::
Error
:
{
return
-
(
*
__errno_location
());
}
default
:
{
}
}
}
int
ret
=
dlsym_stat
(
_STAT_VER
,
path
,
&
st
);
if
(
ret
==
0
)
convert
(
&
st
,
buf
);
return
ret
;
}
int
fstatat
(
int
dfd
,
const
char
*
path
,
struct
stat
*
buf
,
int
flags
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
switch
(
resolve_gkfs_path
(
dfd
,
path
,
resolved
))
{
case
PathStatus
::
Internal
:
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved
);
bool
follow
=
(
flags
&
AT_SYMLINK_NOFOLLOW
)
==
0
;
auto
res
=
gkfs
::
syscall
::
gkfs_stat
(
resolved
,
buf
,
follow
);
return
res
;
}
case
PathStatus
::
Error
:
{
return
-
(
*
__errno_location
());
}
default
:
{
}
}
}
GKFS_FALLBACK
(
fstatat
,
dfd
,
path
,
buf
,
flags
);
}
int
rename
(
const
char
*
oldpath
,
const
char
*
newpath
)
{
initializeGekko
();
// Is path from GekkoFS?
DEBUG_INFO
(
"rename {} --> {}"
,
oldpath
,
newpath
);
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved_old
;
std
::
string
resolved_new
;
auto
rstatus_old
=
resolve_gkfs_path
(
AT_FDCWD
,
oldpath
,
resolved_old
);
auto
rstatus_new
=
resolve_gkfs_path
(
AT_FDCWD
,
newpath
,
resolved_new
);
switch
(
rstatus_old
)
{
case
PathStatus
::
Internal
:
switch
(
rstatus_new
)
{
case
PathStatus
::
Internal
:
DEBUG_INFO
(
"[GKFS] {} {}"
,
resolved_old
,
resolved_new
);
#ifdef HAS_RENAME
return
gkfs
::
syscall
::
gkfs_rename
(
resolved_old
,
resolved_new
);
#else
errno
=
ENOTSUP
;
return
-
1
;
#endif
default:
// Try normal open.
break
;
}
default
:
// Try normal open.
break
;
}
}
GKFS_FALLBACK
(
rename
,
oldpath
,
newpath
);
}
/*
pid_t
fork(void) {
printf("[BYPASS] >> Begin fork() %p\n", (void*) &activated);
pid_t ret = dlsym_fork();
if(0 == ret) {
// We want the children to be initialized
printf("%d -> %d I am the child %p\n", ret, getpid(),
(void*) &activated);
// DEBUG_INFO("[BYPASS] >> ACTIVATED GEKKOFS (child).... \n");
// initializing = false;
// init_libc();
// DEBUG_INFO("%d -> %d [BYPASS] << After fork(child)\n", ret,
// getpid());
return ret;
}
// initializing = false;
// init_libc();
printf("%d -> %d [BYPASS] << After fork() %p\n", ret, getpid(),
(void*) &activated);
return ret;
}
*/
/*
pid_t
vfork(void) {
printf("[BYPASS] >> Begin vfork() %p\n", (void*) &activated);
pid_t ret = dlsym_vfork();
if(0 == ret) {
// We want the children to be initialized
// DEBUG_INFO("[BYPASS] >> ACTIVATED GEKKOFS (child).... \n");
// initializing = false;
//at_child();
printf("%d -> %d I am the child vfork\n", ret, getpid());
return ret;
}
// initializing = false;
return ret;
}
*/
int
pipe
(
int
pipefd
[
2
])
{
initializeGekko
();
auto
res
=
dlsym_pipe
(
pipefd
);
DEBUG_INFO
(
"pipe {} <---> {}"
,
pipefd
[
0
],
pipefd
[
1
]);
// GKFS_FALLBACK(pipe, pipefd);
return
res
;
}
int
dup
(
int
fd
)
{
initializeGekko
();
GKFS_OPERATION
(
dup
,
fd
);
GKFS_FALLBACK
(
dup
,
fd
);
}
int
dup2
(
int
fd
,
int
fd2
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
if
(
is_gkfs_fd
(
fd
)
&&
is_gkfs_fd
(
fd2
))
{
DEBUG_INFO
(
"[GKFS] DUP2 G{} --> G{}"
,
fd
,
fd2
);
return
gkfs
::
syscall
::
gkfs_dup2
(
fd
,
fd2
);
}
else
if
(
is_gkfs_fd
(
fd2
))
{
DEBUG_INFO
(
"[GKFS-NON] DUP2 {} --> G{}"
,
fd
,
fd2
);
return
gkfs
::
syscall
::
gkfs_dup
(
fd
);
}
else
if
(
is_gkfs_fd
(
fd
))
{
DEBUG_INFO
(
"[GKFS-NON] DUP2 G{} --> {}"
,
fd
,
fd2
);
return
gkfs
::
syscall
::
gkfs_dup2
(
fd
,
fd2
);
}
}
// GKFS_OPERATION(dup2, fd, fd2);
GKFS_FALLBACK
(
dup2
,
fd
,
fd2
);
}
// TODO: Implement CLOEXEC
int
dup3
(
int
fd
,
int
fd2
,
int
flags
)
{
initializeGekko
();
GKFS_OPERATION
(
dup2
,
fd
,
fd2
);
GKFS_FALLBACK
(
dup3
,
fd
,
fd2
,
flags
);
}
void
exit
(
int
status
)
{
dlsym_exit
(
status
);
__builtin_unreachable
();
}
// Manager API
int
chdir
(
const
char
*
path
)
{
initializeGekko
();
// is the path from gekkofs
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
auto
rstatus
=
CTX
->
relativize_fd_path
(
AT_FDCWD
,
path
,
resolved
);
DEBUG_INFO
(
"[GKFS] chdir status {} {} --> {}"
,
(
int
)
rstatus
,
resolved
,
path
);
switch
(
rstatus
)
{
case
gkfs
::
preload
::
RelativizeStatus
::
fd_not_a_dir
:
return
-
ENOTDIR
;
case
gkfs
::
preload
::
RelativizeStatus
::
internal
:
{
auto
res
=
gkfs
::
hook
::
hook_chdir
(
path
);
if
(
res
<
0
)
{
errno
=
-
res
;
return
-
1
;
}
return
0
;
}
break
;
default
:
// Try normal open.
break
;
}
}
CTX
->
cwd
(
path
);
GKFS_FALLBACK
(
chdir
,
const_cast
<
char
*>
(
path
));
}
int
fchdir
(
int
fd
)
{
initializeGekko
();
// is the path from gekkofs
auto
res
=
gkfs
::
hook
::
hook_fchdir
(
fd
);
if
(
res
<
0
)
{
errno
=
-
res
;
return
-
1
;
}
return
0
;
}
int
fcntl
(
int
fd
,
int
cmd
,
...)
// TODO
{
initializeGekko
();
if
(
!
real_fcntl
)
{
real_fcntl
=
reinterpret_cast
<
int
(
*
)(
int
fd
,
int
cmd
,
...)
>
(
dlsym
(((
void
*
)
-
1l
),
"fcntl"
));
if
(
!
real_fcntl
)
{
fprintf
(
stderr
,
"dlsym failed for %s: %s
\n
"
,
"fcntl"
,
dlerror
());
(
*
__errno_location
())
=
38
;
return
(
int
)
-
1
;
}
}
va_list
myargs
;
va_start
(
myargs
,
cmd
);
auto
arg
=
va_arg
(
myargs
,
long
);
va_end
(
myargs
);
// is from gekkofs?
if
(
CTX
->
interception_enabled
()
&&
is_gkfs_fd
(
fd
))
{
int
ret
=
-
1
;
switch
(
cmd
)
{
case
F_DUPFD
:
DEBUG_INFO
(
"[GKFS] DUPFD {}"
,
fd
);
return
gkfs
::
syscall
::
gkfs_dup
(
fd
);
case
F_DUPFD_CLOEXEC
:
DEBUG_INFO
(
"[GKFS] F_DUPFD_CLOEXEC {}"
,
fd
);
ret
=
gkfs
::
syscall
::
gkfs_dup
(
fd
);
if
(
ret
==
-
1
)
{
return
-
1
;
}
CTX
->
file_map
()
->
get
(
fd
)
->
set_flag
(
gkfs
::
filemap
::
OpenFile_flags
::
cloexec
,
true
);
return
ret
;
case
F_GETFD
:
DEBUG_INFO
(
"[GKFS] F_GETFD {}"
,
fd
);
if
(
CTX
->
file_map
()
->
get
(
fd
)
->
get_flag
(
gkfs
::
filemap
::
OpenFile_flags
::
cloexec
))
{
return
FD_CLOEXEC
;
}
return
0
;
case
F_GETFL
:
DEBUG_INFO
(
"[GKFS] F_GETFL {}"
,
fd
);
ret
=
0
;
if
(
CTX
->
file_map
()
->
get
(
fd
)
->
get_flag
(
gkfs
::
filemap
::
OpenFile_flags
::
rdonly
))
{
ret
|=
O_RDONLY
;
}
if
(
CTX
->
file_map
()
->
get
(
fd
)
->
get_flag
(
gkfs
::
filemap
::
OpenFile_flags
::
wronly
))
{
ret
|=
O_WRONLY
;
}
if
(
CTX
->
file_map
()
->
get
(
fd
)
->
get_flag
(
gkfs
::
filemap
::
OpenFile_flags
::
rdwr
))
{
ret
|=
O_RDWR
;
}
return
ret
;
case
F_SETFL
:
DEBUG_INFO
(
"[GKFS] F_SETFL {}"
,
fd
);
ret
=
0
;
// get flags from arg and setup
if
(
arg
&
O_RDONLY
)
{
CTX
->
file_map
()
->
get
(
fd
)
->
set_flag
(
gkfs
::
filemap
::
OpenFile_flags
::
rdonly
,
true
);
}
if
(
arg
&
O_WRONLY
)
{
CTX
->
file_map
()
->
get
(
fd
)
->
set_flag
(
gkfs
::
filemap
::
OpenFile_flags
::
wronly
,
true
);
}
if
(
arg
&
O_RDWR
)
{
CTX
->
file_map
()
->
get
(
fd
)
->
set_flag
(
gkfs
::
filemap
::
OpenFile_flags
::
rdwr
,
true
);
}
if
(
arg
&
O_APPEND
)
{
CTX
->
file_map
()
->
get
(
fd
)
->
set_flag
(
gkfs
::
filemap
::
OpenFile_flags
::
append
,
true
);
}
if
(
arg
&
O_NONBLOCK
)
{
DEBUG_INFO
(
"[GKFS] F_SETFL {} NONBLOCK"
,
fd
);
}
return
ret
;
case
F_SETFD
:
DEBUG_INFO
(
"[GKFS] F_SETFD {}"
,
fd
);
CTX
->
file_map
()
->
get
(
fd
)
->
set_flag
(
gkfs
::
filemap
::
OpenFile_flags
::
cloexec
,
(
arg
&
FD_CLOEXEC
));
return
0
;
#ifdef ENABLE_LOCKING
case
F_GETLK
:
{
DEBUG_INFO
(
"[GKFS] F_GETLK {}"
,
fd
);
auto
res
=
real_fcntl
(
fd
,
cmd
,
arg
);
return
res
;
}
case
F_SETLK
:
{
DEBUG_INFO
(
"[GKFS] F_SETLK {}"
,
fd
);
auto
res
=
real_fcntl
(
fd
,
cmd
,
arg
);
return
res
;
}
case
F_SETLKW
:
// Used on S3D-IO
{
DEBUG_INFO
(
"[GKFS] F_SETLKW {}"
,
fd
);
auto
res
=
real_fcntl
(
fd
,
cmd
,
arg
);
return
res
;
}
case
F_GETOWN
:
{
DEBUG_INFO
(
"[GKFS] F_GETOWN {}"
,
fd
);
auto
res
=
real_fcntl
(
fd
,
cmd
,
arg
);
return
res
;
}
case
F_SETOWN
:
// Used on S3D-IO
{
DEBUG_INFO
(
"[GKFS] F_SETOWN {}"
,
fd
);
auto
res
=
real_fcntl
(
fd
,
cmd
,
arg
);
return
res
;
}
#endif
default
:
DEBUG_INFO
(
"[GKFS] NOTSUPPORTED {}"
,
fd
);
errno
=
ENOTSUP
;
return
-
1
;
}
}
// log_arguments("fcntl", fd, cmd);
auto
res
=
real_fcntl
(
fd
,
cmd
,
arg
);
return
res
;
// GKFS_FALLBACK(fcntl, fd, cmd, arg);
}
int
access
(
const
char
*
path
,
int
mode
)
{
initializeGekko
();
GKFS_PATH_OPERATION
(
access
,
AT_FDCWD
,
path
,
mode
,
true
)
GKFS_FALLBACK
(
access
,
path
,
mode
);
}
char
*
realpath
(
const
char
*
path
,
char
*
resolved_path
)
{
// is gekkofs
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
auto
rstatus
=
CTX
->
relativize_fd_path
(
AT_FDCWD
,
path
,
resolved
);
switch
(
rstatus
)
{
case
gkfs
::
preload
::
RelativizeStatus
::
fd_not_a_dir
:
return
nullptr
;
case
gkfs
::
preload
::
RelativizeStatus
::
internal
:
DEBUG_INFO
(
"[GKFS] {} --> {}"
,
resolved
,
path
);
if
(
resolved_path
==
nullptr
)
{
resolved_path
=
static_cast
<
char
*>
(
malloc
(
resolved
.
size
()
+
1
));
if
(
resolved_path
==
nullptr
)
{
errno
=
ENOMEM
;
return
nullptr
;
}
}
strcpy
(
resolved_path
,
path
);
return
resolved_path
;
default
:
break
;
}
}
GKFS_FALLBACK
(
realpath
,
path
,
resolved_path
);
}
char
*
__realpath_chk
(
const
char
*
path
,
char
*
resolved_path
,
__attribute__
((
__unused__
))
size_t
resolved_len
)
{
return
realpath
(
path
,
resolved_path
);
}
int
fsync
(
int
fd
)
// TODO
{
GKFS_FALLBACK
(
fsync
,
fd
);
}
int
flock
(
int
fd
,
int
operation
)
{
GKFS_FALLBACK
(
flock
,
fd
,
operation
);
}
DIR
*
gekko_opendir
(
const
std
::
string
&
path
)
{
struct
stat
st
;
if
(
gkfs
::
syscall
::
gkfs_stat
(
path
,
&
st
)
!=
0
||
S_ISREG
(
st
.
st_mode
))
{
errno
=
ENOTDIR
;
return
nullptr
;
}
const
int
fd
=
gkfs
::
syscall
::
gkfs_opendir
(
path
);
if
(
fd
<
0
)
return
nullptr
;
DIR
*
dirp
=
static_cast
<
DIR
*>
(
malloc
(
sizeof
(
DIR
)));
if
(
dirp
==
NULL
)
{
errno
=
ENOMEM
;
return
NULL
;
}
dirp
->
fd
=
fd
;
dirp
->
path
=
strdup
(
path
.
c_str
());
return
dirp
;
}
DIR
*
opendir
(
const
char
*
dirname
)
{
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
if
(
resolve_gkfs_path
(
AT_FDCWD
,
dirname
,
resolved
)
==
PathStatus
::
Internal
)
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved
);
return
gekko_opendir
(
resolved
);
}
}
return
dlsym_opendir
(
const_cast
<
char
*>
(
dirname
));
}
// opendir64
DIR
*
opendir64
(
const
char
*
dirname
)
{
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
if
(
resolve_gkfs_path
(
AT_FDCWD
,
dirname
,
resolved
)
==
PathStatus
::
Internal
)
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved
);
return
gekko_opendir
(
resolved
);
}
}
return
dlsym_opendir64
(
const_cast
<
char
*>
(
dirname
));
}
DIR
*
fdopendir
(
int
fd
)
{
if
(
CTX
->
interception_enabled
())
{
if
(
is_gkfs_fd
(
fd
))
{
DEBUG_INFO
(
"[GKFS] {}"
,
CTX
->
file_map
()
->
get
(
fd
)
->
path
());
return
gekko_opendir
(
CTX
->
file_map
()
->
get
(
fd
)
->
path
());
}
}
GKFS_FALLBACK
(
fdopendir
,
fd
);
}
// clang-format off
#ifndef __ALIGN_KERNEL_MASK
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
#endif
#ifndef __ALIGN_KERNEL
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x)) (a) -1)
#endif
// clang-format on
#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
// readdir
struct
dirent
*
readdir
(
DIR
*
dirp
)
{
struct
dirent
*
ret
;
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
if
(
CTX
->
file_map
()
->
exist
(
dirp
->
fd
))
{
// call gekko getdents
// DEBUG_INFO("[BYPASS] >> readdir GKFS.... %p\n", (void*)
// dirp);
ret
=
static_cast
<
struct
dirent
*>
(
malloc
(
sizeof
(
dirent
)
*
1
));
auto
open_dir
=
CTX
->
file_map
()
->
get_dir
(
dirp
->
fd
);
if
(
open_dir
==
nullptr
)
{
// Cast did not succeeded: open_file is a regular file
errno
=
EBADF
;
free
(
static_cast
<
struct
dirent
*>
(
ret
));
return
NULL
;
}
// get directory position of which entries to return
auto
pos
=
open_dir
->
pos
();
if
(
pos
>=
open_dir
->
size
())
{
free
(
static_cast
<
struct
dirent
*>
(
ret
));
return
NULL
;
}
auto
de
=
open_dir
->
getdent
(
pos
);
auto
total_size
=
ALIGN
(
offsetof
(
struct
dirent
,
d_name
)
+
de
.
name
().
size
()
+
3
,
sizeof
(
long
));
// fill ret with data
ret
->
d_ino
=
std
::
hash
<
std
::
string
>
()(
open_dir
->
path
()
+
"/"
+
de
.
name
());
ret
->
d_reclen
=
total_size
;
ret
->
d_type
=
((
de
.
type
()
==
gkfs
::
filemap
::
FileType
::
regular
)
?
DT_REG
:
DT_DIR
);
std
::
strcpy
(
&
(
ret
->
d_name
[
0
]),
de
.
name
().
c_str
());
open_dir
->
pos
(
pos
+
1
);
return
ret
;
}
}
ret
=
dlsym_readdir
(
dirp
);
return
ret
;
}
// readdir64
struct
dirent64
*
readdir64
(
DIR
*
dirp
)
{
struct
dirent64
*
ret
;
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
if
(
CTX
->
file_map
()
->
exist
(
dirp
->
fd
))
{
// call gekko getdents
// DEBUG_INFO("[BYPASS] >> readdir64 GKFS.... %p\n", (void*)
// dirp);
ret
=
static_cast
<
struct
dirent64
*>
(
malloc
(
sizeof
(
dirent64
)
*
1
));
auto
open_dir
=
CTX
->
file_map
()
->
get_dir
(
dirp
->
fd
);
if
(
open_dir
==
nullptr
)
{
// Cast did not succeeded: open_file is a regular file
errno
=
EBADF
;
free
(
static_cast
<
struct
dirent64
*>
(
ret
));
return
NULL
;
}
// get directory position of which entries to return
auto
pos
=
open_dir
->
pos
();
if
(
pos
>=
open_dir
->
size
())
{
free
(
static_cast
<
struct
dirent64
*>
(
ret
));
return
NULL
;
}
auto
de
=
open_dir
->
getdent
(
pos
);
auto
total_size
=
ALIGN
(
offsetof
(
struct
dirent64
,
d_name
)
+
de
.
name
().
size
()
+
3
,
sizeof
(
long
));
// fill ret with data
ret
->
d_ino
=
std
::
hash
<
std
::
string
>
()(
open_dir
->
path
()
+
"/"
+
de
.
name
());
ret
->
d_reclen
=
total_size
;
ret
->
d_type
=
((
de
.
type
()
==
gkfs
::
filemap
::
FileType
::
regular
)
?
DT_REG
:
DT_DIR
);
std
::
strcpy
(
&
(
ret
->
d_name
[
0
]),
de
.
name
().
c_str
());
open_dir
->
pos
(
pos
+
1
);
return
ret
;
}
}
ret
=
dlsym_readdir64
(
dirp
);
return
ret
;
}
// closedir
int
closedir
(
DIR
*
dirp
)
{
int
ret
;
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
if
(
CTX
->
file_map
()
->
exist
(
dirp
->
fd
))
{
DEBUG_INFO
(
"[GKFS] {}"
,
dirp
->
fd
);
ret
=
gkfs
::
syscall
::
gkfs_close
(
dirp
->
fd
);
free
(
dirp
->
path
);
free
(
dirp
);
return
ret
;
}
}
ret
=
dlsym_closedir
(
dirp
);
return
ret
;
}
// telldir
long
telldir
(
DIR
*
dirp
)
{
long
ret
;
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
if
(
CTX
->
file_map
()
->
exist
(
dirp
->
fd
))
{
DEBUG_INFO
(
"[GKFS] NOT IMPLEMENTED"
);
ret
=
0
;
// ret = gkfs::syscall::gkfs_telldir(dirp->fd);
return
ret
;
}
}
ret
=
dlsym_telldir
(
dirp
);
return
ret
;
}
// seekdir
void
seekdir
(
DIR
*
dirp
,
long
loc
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
if
(
CTX
->
file_map
()
->
exist
(
dirp
->
fd
))
{
DEBUG_INFO
(
"[GKFS] NOT IMPLEMENTED"
);
return
;
}
}
dlsym_seekdir
(
dirp
,
loc
);
}
// File ops
FILE
*
fdopen
(
int
fd
,
const
char
*
mode
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
if
(
is_gkfs_fd
(
fd
))
{
FILE
*
ret
=
static_cast
<
FILE
*>
(
malloc
(
sizeof
(
FILE
)));
if
(
ret
==
NULL
)
{
errno
=
ENOMEM
;
return
NULL
;
}
ret
->
_mode
=
0666
;
ret
->
_flags
=
0x2000
;
if
(
strchr
(
mode
,
'r'
)
!=
NULL
)
ret
->
_flags
|=
O_RDONLY
;
if
(
strchr
(
mode
,
'w'
))
ret
->
_flags
=
O_WRONLY
|
O_CREAT
|
O_TRUNC
|
0x2000
;
if
(
strchr
(
mode
,
'a'
))
ret
->
_flags
=
O_WRONLY
|
O_CREAT
|
O_APPEND
|
0x2000
;
if
(
strchr
(
mode
,
'+'
))
ret
->
_flags
=
O_RDWR
|
O_CREAT
|
0x2000
;
ret
->
_fileno
=
fd
;
return
ret
;
}
}
GKFS_FALLBACK
(
fdopen
,
fd
,
mode
);
}
FILE
*
fopen
(
const
char
*
path
,
const
char
*
mode
)
{
// DEBUG_INFO("[BYPASS] >> fopen.... %s \n", path);
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
if
(
resolve_gkfs_path
(
AT_FDCWD
,
path
,
resolved
)
==
PathStatus
::
Internal
)
{
int
flags
=
0
;
if
(
strchr
(
mode
,
'r'
)
!=
NULL
)
flags
|=
O_RDONLY
;
if
(
strchr
(
mode
,
'w'
))
flags
=
O_WRONLY
|
O_CREAT
|
O_TRUNC
;
if
(
strchr
(
mode
,
'a'
))
flags
=
O_WRONLY
|
O_CREAT
|
O_APPEND
;
if
(
strchr
(
mode
,
'+'
))
flags
=
O_RDWR
|
O_CREAT
;
DEBUG_INFO
(
"[GEKKO] {}"
,
resolved
);
const
int
fd
=
gkfs
::
syscall
::
gkfs_open
(
resolved
,
0666
,
flags
);
if
(
fd
<
0
)
return
nullptr
;
FILE
*
fp
=
fdopen
(
fd
,
mode
);
return
fp
;
}
}
GKFS_FALLBACK
(
fopen
,
path
,
mode
);
}
FILE
*
freopen64
(
const
char
*
path
,
const
char
*
mode
,
FILE
*
stream
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
if
(
resolve_gkfs_path
(
AT_FDCWD
,
path
,
resolved
)
==
PathStatus
::
Internal
)
{
int
flags
=
0
;
if
(
strchr
(
mode
,
'r'
)
!=
NULL
)
flags
|=
O_RDONLY
;
if
(
strchr
(
mode
,
'w'
))
flags
=
O_WRONLY
|
O_CREAT
|
O_TRUNC
;
if
(
strchr
(
mode
,
'a'
))
flags
=
O_WRONLY
|
O_CREAT
|
O_APPEND
;
if
(
strchr
(
mode
,
'+'
))
flags
=
O_RDWR
|
O_CREAT
;
DEBUG_INFO
(
"[GEKKO] {}"
,
resolved
);
const
int
fd
=
gkfs
::
syscall
::
gkfs_open
(
resolved
,
0666
,
flags
);
if
(
fd
<
0
)
return
nullptr
;
stream
->
_mode
=
0666
;
stream
->
_flags
=
flags
|
0x2000
;
stream
->
_fileno
=
fd
;
return
stream
;
}
}
GKFS_FALLBACK
(
freopen64
,
path
,
mode
,
stream
);
}
/* We return nmems, not size*/
size_t
fread
(
void
*
ptr
,
size_t
size
,
size_t
nmemb
,
FILE
*
stream
)
{
if
(
CTX
->
interception_enabled
()
&&
CTX
->
file_map
()
->
exist
(
stream
->
_fileno
))
{
DEBUG_INFO
(
"[GEKKO] {}"
,
stream
->
_fileno
);
return
gkfs
::
syscall
::
gkfs_read
(
stream
->
_fileno
,
ptr
,
size
*
nmemb
)
/
size
;
}
GKFS_FALLBACK
(
fread
,
ptr
,
size
,
nmemb
,
stream
)
}
size_t
fwrite
(
const
void
*
ptr
,
size_t
size
,
size_t
nmemb
,
FILE
*
stream
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
CTX
->
file_map
()
->
exist
(
stream
->
_fileno
))
{
DEBUG_INFO
(
"[GEKKO] {}"
,
stream
->
_fileno
);
return
gkfs
::
syscall
::
gkfs_write
(
stream
->
_fileno
,
ptr
,
size
*
nmemb
)
/
size
;
}
GKFS_FALLBACK
(
fwrite
,
ptr
,
size
,
nmemb
,
stream
)
}
int
fseek
(
FILE
*
stream
,
long
int
offset
,
int
whence
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
CTX
->
file_map
()
->
exist
(
stream
->
_fileno
))
{
DEBUG_INFO
(
"[GKFS] {}"
,
stream
->
_fileno
);
return
gkfs
::
syscall
::
gkfs_lseek
(
stream
->
_fileno
,
offset
,
whence
);
}
GKFS_FALLBACK
(
fseek
,
stream
,
offset
,
whence
)
}
long
ftell
(
FILE
*
stream
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
CTX
->
file_map
()
->
exist
(
stream
->
_fileno
))
{
DEBUG_INFO
(
"[GKFS] {}"
,
stream
->
_fileno
);
return
gkfs
::
syscall
::
gkfs_lseek
(
stream
->
_fileno
,
0
,
SEEK_CUR
);
}
GKFS_FALLBACK
(
ftell
,
stream
)
}
void
rewind
(
FILE
*
stream
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
CTX
->
file_map
()
->
exist
(
stream
->
_fileno
))
{
DEBUG_INFO
(
"[GKFS] {}"
,
stream
->
_fileno
);
gkfs
::
syscall
::
gkfs_lseek
(
stream
->
_fileno
,
0
,
SEEK_SET
);
return
;
}
GKFS_FALLBACK
(
rewind
,
stream
)
}
// clearerr
void
clearerr
(
FILE
*
stream
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
CTX
->
file_map
()
->
exist
(
stream
->
_fileno
))
{
DEBUG_INFO
(
"[GKFS] {}"
,
stream
->
_fileno
);
return
;
}
GKFS_FALLBACK
(
clearerr
,
stream
)
}
int
fclose
(
FILE
*
stream
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
CTX
->
file_map
()
->
exist
(
stream
->
_fileno
))
{
DEBUG_INFO
(
"[GKFS] {}"
,
stream
->
_fileno
);
auto
ret
=
gkfs
::
syscall
::
gkfs_close
(
stream
->
_fileno
);
// should I do a fclose (stream)?
free
(
stream
);
return
ret
;
}
GKFS_FALLBACK
(
fclose
,
stream
)
}
// feof implementation with lseek
int
feof
(
FILE
*
stream
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
CTX
->
file_map
()
->
exist
(
stream
->
_fileno
))
{
DEBUG_INFO
(
"[GKFS] {}"
,
stream
->
_fileno
);
off_t
cur
=
gkfs
::
syscall
::
gkfs_lseek
(
stream
->
_fileno
,
0
,
SEEK_CUR
);
off_t
end
=
gkfs
::
syscall
::
gkfs_lseek
(
stream
->
_fileno
,
0
,
SEEK_END
);
gkfs
::
syscall
::
gkfs_lseek
(
stream
->
_fileno
,
cur
,
SEEK_SET
);
return
(
cur
==
end
);
}
GKFS_FALLBACK
(
feof
,
stream
)
}
int
fputs
(
const
char
*
str
,
FILE
*
stream
)
{
initializeGekko
();
auto
fd
=
stream
->
_fileno
;
GKFS_OPERATION
(
write
,
fd
,
str
,
strlen
(
str
))
GKFS_FALLBACK
(
fputs
,
str
,
stream
)
}
char
*
fgets
(
char
*
str
,
int
n
,
FILE
*
stream
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
CTX
->
file_map
()
->
exist
(
stream
->
_fileno
))
{
// read str n chars
DEBUG_INFO
(
"[GKFS] {}"
,
stream
->
_fileno
);
auto
l
=
gkfs
::
syscall
::
gkfs_read
(
stream
->
_fileno
,
str
,
n
);
if
(
l
==
0
)
{
return
NULL
;
}
return
str
;
}
GKFS_FALLBACK
(
fgets
,
str
,
n
,
stream
)
}
// implement fflush and bypass if gekko (no need to log)
int
fflush
(
FILE
*
stream
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
CTX
->
file_map
()
->
exist
(
stream
->
_fileno
))
{
// flush stream with gkfs_fsync
return
0
;
}
return
dlsym_fflush
(
stream
);
}
void
*
mmap
(
void
*
addr
,
size_t
length
,
int
prot
,
int
flags
,
int
fd
,
off_t
offset
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
is_gkfs_fd
(
fd
))
{
DEBUG_INFO
(
"[GKFS] {}"
,
CTX
->
file_map
()
->
get
(
fd
)
->
path
());
// create a malloc of length
void
*
ptr
=
malloc
(
length
);
if
(
ptr
==
nullptr
)
{
return
MAP_FAILED
;
}
// store info on mmap_set
mmap_set
.
insert
(
std
::
make_tuple
(
ptr
,
fd
,
length
,
offset
));
gkfs
::
syscall
::
gkfs_pread
(
fd
,
ptr
,
length
,
offset
);
return
ptr
;
}
GKFS_FALLBACK
(
mmap
,
addr
,
length
,
prot
,
flags
,
fd
,
offset
);
}
// implement msync, if addr is from gekkofs (we should have a set with them)
// We pwrite length to the original offset (also from the set), and return
// if not just go to the normal msync
int
msync
(
void
*
addr
,
size_t
length
,
int
flags
)
{
initializeGekko
();
// check if addr is from gekkofs (mmap_set)
// if so, get the fd and offset
// pwrite length to the original offset
// return
// if not just go to the normal msync
for
(
const
auto
&
tuple
:
mmap_set
)
{
if
(
std
::
get
<
0
>
(
tuple
)
==
addr
)
{
int
fd
=
std
::
get
<
1
>
(
tuple
);
off_t
offset
=
std
::
get
<
3
>
(
tuple
);
gkfs
::
syscall
::
gkfs_pwrite
(
fd
,
addr
,
length
,
offset
);
return
0
;
}
}
GKFS_FALLBACK
(
msync
,
addr
,
length
,
flags
);
}
// implement munmap, if it's on the set, call msync, free memory
int
munmap
(
void
*
addr
,
size_t
length
)
{
initializeGekko
();
// check if addr is from gekkofs (mmap_set)
// if so, call msync
// free memory
// return
// if not just go to the normal munmap
if
(
mmap_set
.
size
()
!=
0
)
{
for
(
auto
it
=
mmap_set
.
begin
();
it
!=
mmap_set
.
end
();
++
it
)
{
if
(
std
::
get
<
0
>
(
*
it
)
==
addr
)
{
msync
(
addr
,
length
,
0
);
free
(
addr
);
mmap_set
.
erase
(
it
);
return
0
;
}
}
}
GKFS_FALLBACK
(
munmap
,
addr
,
length
);
}
// fchown
int
fchown
(
int
fd
,
uid_t
owner
,
gid_t
group
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
is_gkfs_fd
(
fd
))
{
DEBUG_INFO
(
"[GKFS] {}"
,
CTX
->
file_map
()
->
get
(
fd
)
->
path
());
return
0
;
}
GKFS_FALLBACK
(
fchown
,
fd
,
owner
,
group
);
}
// fchmod
int
fchmod
(
int
fd
,
mode_t
mode
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
is_gkfs_fd
(
fd
))
{
DEBUG_INFO
(
"[GKFS] {}"
,
CTX
->
file_map
()
->
get
(
fd
)
->
path
());
return
0
;
}
GKFS_FALLBACK
(
fchmod
,
fd
,
mode
);
}
// futimes
int
futimes
(
int
fd
,
const
struct
timeval
tv
[
2
])
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
is_gkfs_fd
(
fd
))
{
DEBUG_INFO
(
"[GKFS] {}"
,
CTX
->
file_map
()
->
get
(
fd
)
->
path
());
return
0
;
}
GKFS_FALLBACK
(
futimes
,
fd
,
tv
);
}
int
utimes
(
const
char
*
path
,
const
struct
timeval
tv
[
2
])
{
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
if
(
resolve_gkfs_path
(
AT_FDCWD
,
path
,
resolved
)
==
PathStatus
::
Internal
)
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved
);
return
0
;
}
}
GKFS_FALLBACK
(
utimes
,
path
,
tv
);
}
// Without use... goes directly to the syscall (ls -l)
ssize_t
listxattr
(
const
char
*
path
,
char
*
list
,
size_t
size
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
if
(
resolve_gkfs_path
(
AT_FDCWD
,
path
,
resolved
)
==
PathStatus
::
Internal
)
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved
);
return
0
;
}
}
GKFS_FALLBACK
(
listxattr
,
path
,
list
,
size
);
}
ssize_t
flistxattr
(
int
fd
,
char
*
list
,
size_t
size
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
is_gkfs_fd
(
fd
))
{
DEBUG_INFO
(
"[GKFS] {}"
,
CTX
->
file_map
()
->
get
(
fd
)
->
path
());
return
0
;
}
GKFS_FALLBACK
(
flistxattr
,
fd
,
list
,
size
);
}
ssize_t
llistxattr
(
const
char
*
path
,
char
*
list
,
size_t
size
)
{
initializeGekko
();
return
0
;
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
if
(
resolve_gkfs_path
(
AT_FDCWD
,
path
,
resolved
)
==
PathStatus
::
Internal
)
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved
);
return
0
;
}
}
GKFS_FALLBACK
(
llistxattr
,
path
,
list
,
size
);
}
int
lstat
(
const
char
*
path
,
struct
stat
*
st
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
if
(
resolve_gkfs_path
(
AT_FDCWD
,
path
,
resolved
,
0
,
false
)
==
PathStatus
::
Internal
)
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved
);
auto
res
=
gkfs
::
syscall
::
gkfs_stat
(
resolved
,
st
,
false
);
return
res
;
}
}
GKFS_FALLBACK
(
lstat
,
path
,
st
);
}
int
lstat64
(
const
char
*
path
,
struct
stat64
*
buf
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
struct
stat
st
;
if
(
resolve_gkfs_path
(
AT_FDCWD
,
path
,
resolved
,
0
,
false
)
==
PathStatus
::
Internal
)
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved
);
auto
res
=
gkfs
::
syscall
::
gkfs_stat
(
resolved
,
&
st
,
false
);
convert
(
&
st
,
buf
);
return
res
;
}
}
GKFS_FALLBACK
(
lstat64
,
path
,
buf
);
}
int
scandir
(
const
char
*
dirname
,
struct
dirent
***
namelist
,
int
(
*
filter
)(
const
struct
dirent
*
),
int
(
*
compar
)(
const
struct
dirent
**
,
const
struct
dirent
**
))
{
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
if
(
resolve_gkfs_path
(
AT_FDCWD
,
dirname
,
resolved
)
==
PathStatus
::
Internal
)
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved
);
// implement scandir : The scandir() function scans the
// directory dirp, calling filter() on each directory entry.
// Entries for which filter() returns nonzero are stored in
// strings allocated via malloc(3), sorted using qsort(3) with
// the comparison function compar(), and collected in
// array namelist which is allocated via malloc(3). If
// filter is NULL, all entries are selected.
DIR
*
dirp
=
gekko_opendir
(
resolved
);
if
(
dirp
==
nullptr
)
{
return
-
1
;
}
std
::
vector
<
struct
dirent
*>
entries
;
struct
dirent
*
entry
;
while
((
entry
=
readdir
(
dirp
))
!=
nullptr
)
{
if
(
filter
==
nullptr
||
filter
(
entry
)
!=
0
)
{
entries
.
push_back
(
entry
);
}
}
closedir
(
dirp
);
if
(
entries
.
empty
())
{
*
namelist
=
nullptr
;
return
0
;
}
if
(
compar
!=
nullptr
)
{
qsort
(
entries
.
data
(),
entries
.
size
(),
sizeof
(
struct
dirent
*
),
reinterpret_cast
<
int
(
*
)(
const
void
*
,
const
void
*
)
>
(
compar
));
}
*
namelist
=
static_cast
<
struct
dirent
**>
(
malloc
(
sizeof
(
struct
dirent
*
)
*
(
entries
.
size
()
+
1
)));
if
(
*
namelist
==
nullptr
)
{
for
(
auto
e
:
entries
)
{
free
(
e
);
}
return
-
1
;
}
for
(
size_t
i
=
0
;
i
<
entries
.
size
();
++
i
)
{
(
*
namelist
)[
i
]
=
entries
[
i
];
}
(
*
namelist
)[
entries
.
size
()]
=
nullptr
;
return
entries
.
size
();
}
}
GKFS_FALLBACK
(
scandir
,
dirname
,
namelist
,
filter
,
compar
);
}
// path2 should not exists (is the symbolic link)
int
symlink
(
const
char
*
path1
,
const
char
*
path2
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
if
(
resolve_gkfs_path
(
AT_FDCWD
,
path1
,
resolved
)
==
PathStatus
::
Internal
)
{
DEBUG_INFO
(
"[GKFS] path 1 internal {}"
,
resolved
);
std
::
string
resolved2
;
if
(
resolve_gkfs_path
(
AT_FDCWD
,
path2
,
resolved2
)
==
PathStatus
::
Internal
)
{
DEBUG_INFO
(
"[GKFS] path 2 internal {}"
,
resolved2
);
#ifdef HAS_SYMLINKS
// In Gekko we invert the parameters.
return
gkfs
::
syscall
::
gkfs_mk_symlink
(
resolved2
,
resolved
);
#else
DEBUG_INFO
(
"[GKFS] symlinks not supported/compiled"
);
errno
=
ENOTSUP
;
return
-
1
;
#endif
}
}
}
GKFS_FALLBACK
(
symlink
,
path1
,
path2
);
}
#ifdef HAS_SYMLINKS
ssize_t
readlink
(
const
char
*
path
,
char
*
buf
,
size_t
bufsize
)
{
initializeGekko
();
GKFS_PATH_OPERATION
(
readlink
,
AT_FDCWD
,
path
,
buf
,
bufsize
);
GKFS_FALLBACK
(
readlink
,
path
,
buf
,
bufsize
);
}
ssize_t
readlinkat
(
int
dfd
,
const
char
*
path
,
char
*
buf
,
size_t
bufsize
)
{
initializeGekko
();
GKFS_PATH_OPERATION
(
readlink
,
dfd
,
path
,
buf
,
bufsize
);
GKFS_FALLBACK
(
readlinkat
,
dfd
,
path
,
buf
,
bufsize
);
}
#endif
ssize_t
readv
(
int
fd
,
const
struct
iovec
*
iov
,
int
iovcnt
)
{
initializeGekko
();
GKFS_OPERATION
(
readv
,
fd
,
iov
,
iovcnt
)
GKFS_FALLBACK
(
readv
,
fd
,
iov
,
iovcnt
)
}
ssize_t
writev
(
int
fd
,
const
struct
iovec
*
iov
,
int
iovcnt
)
{
initializeGekko
();
GKFS_OPERATION
(
writev
,
fd
,
iov
,
iovcnt
)
GKFS_FALLBACK
(
writev
,
fd
,
iov
,
iovcnt
)
}
ssize_t
preadv
(
int
fd
,
const
struct
iovec
*
iov
,
int
iovcnt
,
off_t
offset
)
{
initializeGekko
();
GKFS_OPERATION
(
preadv
,
fd
,
iov
,
iovcnt
,
offset
)
GKFS_FALLBACK
(
preadv
,
fd
,
iov
,
iovcnt
,
offset
)
}
ssize_t
pwritev
(
int
fd
,
const
struct
iovec
*
iov
,
int
iovcnt
,
off_t
offset
)
{
initializeGekko
();
GKFS_OPERATION
(
pwritev
,
fd
,
iov
,
iovcnt
,
offset
)
GKFS_FALLBACK
(
pwritev
,
fd
,
iov
,
iovcnt
,
offset
)
}
ssize_t
preadv2
(
int
fd
,
const
struct
iovec
*
iov
,
int
iovcnt
,
off_t
offset
,
int
flags
)
{
initializeGekko
();
GKFS_OPERATION
(
preadv
,
fd
,
iov
,
iovcnt
,
offset
)
GKFS_FALLBACK
(
preadv2
,
fd
,
iov
,
iovcnt
,
offset
,
flags
)
}
ssize_t
pwritev2
(
int
fd
,
const
struct
iovec
*
iov
,
int
iovcnt
,
off_t
offset
,
int
flags
)
{
initializeGekko
();
GKFS_OPERATION
(
pwritev
,
fd
,
iov
,
iovcnt
,
offset
)
GKFS_FALLBACK
(
pwritev2
,
fd
,
iov
,
iovcnt
,
offset
,
flags
)
}
// aio Operations (write)
// /*
// #include <aiocb.h>
// struct aiocb {
// int aio_fildes; /* File descriptor */
// off_t aio_offset; /* File offset */
// volatile void *aio_buf; /* Location of buffer */
// size_t aio_nbytes; /* Length of transfer */
// int aio_reqprio; /* Request priority */
// struct sigevent aio_sigevent; /* Notification method */
// int aio_lio_opcode; /* Operation to be
// performed;
// lio_listio() only */
// /* Various implementation-internal fields not shown */
// };
// /* Operation codes for 'aio_lio_opcode': */
// enum { LIO_READ, LIO_WRITE, LIO_NOP };
// Possible values for aio_sigevent.sigev_notify are SIGEV_NONE,
// SIGEV_SIGNAL, and SIGEV_THREAD. See sigevent(3type) for further de‐
// tails.
// */
/* Add result to tracking list */
typedef
struct
ResultEntry
{
struct
aiocb
*
aiocbp
;
ssize_t
result
;
struct
ResultEntry
*
next
;
}
ResultEntry
;
static
pthread_mutex_t
result_mutex
=
PTHREAD_MUTEX_INITIALIZER
;
static
ResultEntry
*
results
=
NULL
;
static
void
add_result
(
struct
aiocb
*
aiocbp
,
ssize_t
res
)
{
ResultEntry
*
entry
=
(
ResultEntry
*
)
malloc
(
sizeof
(
ResultEntry
));
entry
->
aiocbp
=
aiocbp
;
entry
->
result
=
res
;
entry
->
next
=
NULL
;
pthread_mutex_lock
(
&
result_mutex
);
entry
->
next
=
results
;
results
=
entry
;
pthread_mutex_unlock
(
&
result_mutex
);
}
DLSYM_WRAPPER
(
int
,
aio_write
,
(
struct
aiocb
*
aiocbp
),
(
aiocbp
),
"aio_write"
)
int
aio_write
(
struct
aiocb
*
aiocbp
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
is_gkfs_fd
(
aiocbp
->
aio_fildes
))
{
auto
res
=
gkfs
::
syscall
::
gkfs_write
(
aiocbp
->
aio_fildes
,
(
const
void
*
)
aiocbp
->
aio_buf
,
aiocbp
->
aio_nbytes
);
// TODO : SIGNALING IS WRONG*/
add_result
(
aiocbp
,
res
);
return
0
;
}
GKFS_FALLBACK
(
aio_write
,
aiocbp
)
}
DLSYM_WRAPPER
(
int
,
aio_read
,
(
struct
aiocb
*
aiocbp
),
(
aiocbp
),
"aio_read"
)
int
aio_read
(
struct
aiocb
*
aiocbp
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
is_gkfs_fd
(
aiocbp
->
aio_fildes
))
{
auto
res
=
gkfs
::
syscall
::
gkfs_read
(
aiocbp
->
aio_fildes
,
(
void
*
)
aiocbp
->
aio_buf
,
aiocbp
->
aio_nbytes
);
// TODO : SIGNALING IS WRONG
add_result
(
aiocbp
,
res
);
return
0
;
}
// aiocbp->aio_nbytes = 0; // TODO : NOt overwrite the buffer
GKFS_FALLBACK
(
aio_read
,
aiocbp
)
}
/* Find and remove result from tracking list */
static
ssize_t
get_result
(
struct
aiocb
*
aiocbp
)
{
pthread_mutex_lock
(
&
result_mutex
);
ResultEntry
**
prev
=
&
results
;
ResultEntry
*
current
=
results
;
ssize_t
ret
=
-
1
;
while
(
current
)
{
if
(
current
->
aiocbp
==
aiocbp
)
{
*
prev
=
current
->
next
;
ret
=
current
->
result
;
free
(
current
);
break
;
}
prev
=
&
current
->
next
;
current
=
current
->
next
;
}
pthread_mutex_unlock
(
&
result_mutex
);
return
ret
;
}
DLSYM_WRAPPER
(
ssize_t
,
aio_return
,
(
struct
aiocb
*
aiocbp
),
(
aiocbp
),
"aio_return"
)
ssize_t
aio_return
(
struct
aiocb
*
aiocbp
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
is_gkfs_fd
(
aiocbp
->
aio_fildes
))
{
ssize_t
ret
=
get_result
(
aiocbp
);
DEBUG_INFO
(
"AIO_RETURN {} -> {}"
,
aiocbp
->
aio_fildes
,
ret
);
if
(
ret
==
-
1
)
errno
=
EINVAL
;
return
ret
;
}
GKFS_FALLBACK
(
aio_return
,
aiocbp
)
}
DLSYM_WRAPPER
(
int
,
aio_error
,
(
const
struct
aiocb
*
aiocbp
),
(
aiocbp
),
"aio_error"
)
int
aio_error
(
const
struct
aiocb
*
aiocbp
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
is_gkfs_fd
(
aiocbp
->
aio_fildes
))
{
DEBUG_INFO
(
"AIO_ERROR {}"
,
aiocbp
->
aio_fildes
);
return
0
;
}
GKFS_FALLBACK
(
aio_error
,
aiocbp
)
}
DLSYM_WRAPPER
(
char
*
,
getcwd
,
(
char
*
buffer
,
size_t
size
),
(
buffer
,
size
),
"getcwd"
)
char
*
getcwd
(
char
*
buffer
,
size_t
size
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
if
(
buffer
==
nullptr
)
{
if
(
size
!=
0
)
buffer
=
(
char
*
)
malloc
(
size
);
else
{
// allocate buffer big enough
buffer
=
(
char
*
)
malloc
(
CTX
->
cwd
().
size
()
+
1
);
size
=
CTX
->
cwd
().
size
()
+
1
;
}
}
if
(
CTX
->
cwd
().
size
()
+
1
>
size
)
{
errno
=
ERANGE
;
return
NULL
;
}
DEBUG_INFO
(
"[GKFS] Size: {} / CWD {}"
,
size
,
CTX
->
cwd
().
c_str
());
strcpy
(
buffer
,
CTX
->
cwd
().
c_str
());
return
buffer
;
}
GKFS_FALLBACK
(
getcwd
,
buffer
,
size
);
}
DLSYM_WRAPPER
(
void
,
rewinddir
,
(
DIR
*
dirstream
),
(
dirstream
),
"rewinddir"
)
void
rewinddir
(
DIR
*
dirstream
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
()
&&
is_gkfs_fd
(
dirstream
->
fd
))
{
DEBUG_INFO
(
"[GKFS] {}"
,
dirstream
->
fd
);
auto
open_dir
=
CTX
->
file_map
()
->
get_dir
(
dirstream
->
fd
);
if
(
open_dir
==
nullptr
)
{
// Cast did not succeeded: open_file is a regular file
errno
=
EBADF
;
return
;
}
open_dir
->
pos
(
0
);
return
;
}
GKFS_FALLBACK
(
rewinddir
,
dirstream
)
}
int
renameat
(
int
olddirfd
,
const
char
*
oldpath
,
int
newdirfd
,
const
char
*
newpath
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
if
(
resolve_gkfs_path
(
olddirfd
,
oldpath
,
resolved
)
==
PathStatus
::
Internal
)
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved
);
std
::
string
resolved2
;
if
(
resolve_gkfs_path
(
newdirfd
,
newpath
,
resolved2
)
==
PathStatus
::
Internal
)
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved2
);
#ifdef HAS_RENAME
return
gkfs
::
syscall
::
gkfs_rename
(
resolved
,
resolved2
);
#else
errno
=
ENOTSUP
;
return
-
1
;
#endif
}
}
}
GKFS_FALLBACK
(
renameat
,
olddirfd
,
oldpath
,
newdirfd
,
newpath
);
}
int
renameat2
(
int
olddirfd
,
const
char
*
oldpath
,
int
newdirfd
,
const
char
*
newpath
,
unsigned
int
flags
)
{
initializeGekko
();
if
(
CTX
->
interception_enabled
())
{
std
::
string
resolved
;
if
(
resolve_gkfs_path
(
olddirfd
,
oldpath
,
resolved
)
==
PathStatus
::
Internal
)
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved
);
std
::
string
resolved2
;
if
(
resolve_gkfs_path
(
newdirfd
,
newpath
,
resolved2
)
==
PathStatus
::
Internal
)
{
DEBUG_INFO
(
"[GKFS] {}"
,
resolved2
);
#ifdef HAS_RENAME
return
gkfs
::
syscall
::
gkfs_rename
(
resolved
,
resolved2
);
#else
errno
=
ENOTSUP
;
return
-
1
;
#endif
}
}
}
GKFS_FALLBACK
(
renameat2
,
olddirfd
,
oldpath
,
newdirfd
,
newpath
,
flags
);
}
src/client/hooks.cpp
View file @
5c0ec2a1
...
@@ -134,11 +134,12 @@ hook_stat(const char* path, struct stat* buf) {
...
@@ -134,11 +134,12 @@ hook_stat(const char* path, struct stat* buf) {
int
int
hook_statx
(
int
dirfd
,
const
char
*
path
,
int
flags
,
unsigned
int
mask
,
hook_statx
(
int
dirfd
,
const
char
*
path
,
int
flags
,
unsigned
int
mask
,
struct
::
statx
*
buf
)
{
struct
::
statx
*
buf
)
{
bool
follow
=
(
flags
&
AT_SYMLINK_NOFOLLOW
)
==
0
;
LOG
(
DEBUG
,
LOG
(
DEBUG
,
"{}() called with dirfd: '{}', path:
\"
{}
\"
, flags: '{}', mask: '{}', buf: '{}'"
,
"{}() called with dirfd: '{}', path:
\"
{}
\"
, flags: '{}', mask: '{}', buf: '{}'"
,
__func__
,
dirfd
,
path
,
flags
,
mask
,
fmt
::
ptr
(
buf
));
__func__
,
dirfd
,
path
,
flags
,
mask
,
fmt
::
ptr
(
buf
));
std
::
string
resolved
;
std
::
string
resolved
;
auto
rstatus
=
CTX
->
relativize_fd_path
(
dirfd
,
path
,
resolved
);
auto
rstatus
=
CTX
->
relativize_fd_path
(
dirfd
,
path
,
resolved
);
switch
(
rstatus
)
{
switch
(
rstatus
)
{
...
@@ -154,8 +155,8 @@ hook_statx(int dirfd, const char* path, int flags, unsigned int mask,
...
@@ -154,8 +155,8 @@ hook_statx(int dirfd, const char* path, int flags, unsigned int mask,
return
-
ENOTDIR
;
return
-
ENOTDIR
;
case
gkfs
::
preload
::
RelativizeStatus
::
internal
:
case
gkfs
::
preload
::
RelativizeStatus
::
internal
:
return
with_errno
(
gkfs
::
syscall
::
gkfs_statx
(
dirfd
,
resolved
.
c_str
(),
return
with_errno
(
gkfs
::
syscall
::
gkfs_statx
(
flags
,
mask
,
buf
));
dirfd
,
resolved
.
c_str
(),
flags
,
mask
,
buf
,
follow
));
default:
default:
LOG
(
ERROR
,
"{}() relativize status unknown: {}"
,
__func__
);
LOG
(
ERROR
,
"{}() relativize status unknown: {}"
,
__func__
);
...
@@ -174,7 +175,7 @@ hook_lstat(const char* path, struct stat* buf) {
...
@@ -174,7 +175,7 @@ hook_lstat(const char* path, struct stat* buf) {
std
::
string
rel_path
;
std
::
string
rel_path
;
if
(
CTX
->
relativize_path
(
path
,
rel_path
))
{
if
(
CTX
->
relativize_path
(
path
,
rel_path
))
{
return
with_errno
(
gkfs
::
syscall
::
gkfs_stat
(
rel_path
,
buf
));
return
with_errno
(
gkfs
::
syscall
::
gkfs_stat
(
rel_path
,
buf
,
false
));
}
}
return
gsl
::
narrow_cast
<
int
>
(
return
gsl
::
narrow_cast
<
int
>
(
syscall_no_intercept_wrapper
(
SYS_lstat
,
rel_path
.
c_str
(),
buf
));
syscall_no_intercept_wrapper
(
SYS_lstat
,
rel_path
.
c_str
(),
buf
));
...
@@ -193,7 +194,7 @@ hook_fstat(unsigned int fd, struct stat* buf) {
...
@@ -193,7 +194,7 @@ hook_fstat(unsigned int fd, struct stat* buf) {
// We can change file_map and recall
// We can change file_map and recall
auto
md
=
gkfs
::
utils
::
get_metadata
(
path
,
false
);
auto
md
=
gkfs
::
utils
::
get_metadata
(
path
,
false
);
if
(
md
.
has_value
()
&&
md
.
value
().
blocks
()
==
-
1
)
{
if
(
md
.
has_value
()
&&
md
.
value
().
blocks
()
==
-
1
)
{
path
=
md
.
value
().
rename
_path
();
path
=
md
.
value
().
target
_path
();
}
}
#endif
#endif
return
with_errno
(
gkfs
::
syscall
::
gkfs_stat
(
path
,
buf
));
return
with_errno
(
gkfs
::
syscall
::
gkfs_stat
(
path
,
buf
));
...
@@ -204,10 +205,11 @@ hook_fstat(unsigned int fd, struct stat* buf) {
...
@@ -204,10 +205,11 @@ hook_fstat(unsigned int fd, struct stat* buf) {
int
int
hook_fstatat
(
int
dirfd
,
const
char
*
cpath
,
struct
stat
*
buf
,
int
flags
)
{
hook_fstatat
(
int
dirfd
,
const
char
*
cpath
,
struct
stat
*
buf
,
int
flags
)
{
bool
follow
=
(
flags
&
AT_SYMLINK_NOFOLLOW
)
==
0
;
LOG
(
DEBUG
,
"{}() called with path:
\"
{}
\"
, fd: {}, buf: {}, flags: {}"
,
LOG
(
DEBUG
,
"{}() called with path:
\"
{}
\"
, fd: {}, buf: {}, flags: {}"
,
__func__
,
cpath
,
dirfd
,
fmt
::
ptr
(
buf
),
flags
);
__func__
,
cpath
,
dirfd
,
fmt
::
ptr
(
buf
),
flags
);
std
::
string
resolved
;
std
::
string
resolved
;
auto
rstatus
=
CTX
->
relativize_fd_path
(
dirfd
,
cpath
,
resolved
,
flags
);
auto
rstatus
=
CTX
->
relativize_fd_path
(
dirfd
,
cpath
,
resolved
,
flags
);
switch
(
rstatus
)
{
switch
(
rstatus
)
{
...
@@ -223,7 +225,7 @@ hook_fstatat(int dirfd, const char* cpath, struct stat* buf, int flags) {
...
@@ -223,7 +225,7 @@ hook_fstatat(int dirfd, const char* cpath, struct stat* buf, int flags) {
return
-
ENOTDIR
;
return
-
ENOTDIR
;
case
gkfs
::
preload
::
RelativizeStatus
::
internal
:
case
gkfs
::
preload
::
RelativizeStatus
::
internal
:
return
with_errno
(
gkfs
::
syscall
::
gkfs_stat
(
resolved
,
buf
));
return
with_errno
(
gkfs
::
syscall
::
gkfs_stat
(
resolved
,
buf
,
follow
));
default:
default:
LOG
(
ERROR
,
"{}() relativize status unknown: {}"
,
__func__
);
LOG
(
ERROR
,
"{}() relativize status unknown: {}"
,
__func__
);
...
@@ -383,10 +385,17 @@ hook_symlinkat(const char* oldname, int newdfd, const char* newname) {
...
@@ -383,10 +385,17 @@ hook_symlinkat(const char* oldname, int newdfd, const char* newname) {
LOG
(
DEBUG
,
"{}() called with oldname:
\"
{}
\"
, newfd: {}, newname:
\"
{}
\"
"
,
LOG
(
DEBUG
,
"{}() called with oldname:
\"
{}
\"
, newfd: {}, newname:
\"
{}
\"
"
,
__func__
,
oldname
,
newdfd
,
newname
);
__func__
,
oldname
,
newdfd
,
newname
);
#ifdef HAS_SYMLINKS
bool
internal1
=
false
;
#endif
std
::
string
oldname_resolved
;
std
::
string
oldname_resolved
;
if
(
CTX
->
relativize_path
(
oldname
,
oldname_resolved
))
{
if
(
CTX
->
relativize_path
(
oldname
,
oldname_resolved
))
{
#ifdef HAS_SYMLINKS
internal1
=
true
;
#else
LOG
(
WARNING
,
"{}() operation not supported"
,
__func__
);
LOG
(
WARNING
,
"{}() operation not supported"
,
__func__
);
return
-
ENOTSUP
;
return
-
ENOTSUP
;
#endif
}
}
std
::
string
newname_resolved
;
std
::
string
newname_resolved
;
...
@@ -405,8 +414,17 @@ hook_symlinkat(const char* oldname, int newdfd, const char* newname) {
...
@@ -405,8 +414,17 @@ hook_symlinkat(const char* oldname, int newdfd, const char* newname) {
return
-
ENOTDIR
;
return
-
ENOTDIR
;
case
gkfs
::
preload
::
RelativizeStatus
::
internal
:
case
gkfs
::
preload
::
RelativizeStatus
::
internal
:
#ifdef HAS_SYMLINKS
if
(
internal1
)
{
// Parameters are inverted
return
with_errno
(
gkfs
::
syscall
::
gkfs_mk_symlink
(
newname_resolved
,
oldname_resolved
));
}
LOG
(
WARNING
,
"{}() operation not supported"
,
__func__
);
return
-
ENOTSUP
;
#else
LOG
(
WARNING
,
"{}() operation not supported"
,
__func__
);
LOG
(
WARNING
,
"{}() operation not supported"
,
__func__
);
return
-
ENOTSUP
;
return
-
ENOTSUP
;
#endif
default
:
default
:
LOG
(
ERROR
,
"{}() relativize status unknown"
,
__func__
);
LOG
(
ERROR
,
"{}() relativize status unknown"
,
__func__
);
...
@@ -704,7 +722,8 @@ hook_chdir(const char* path) {
...
@@ -704,7 +722,8 @@ hook_chdir(const char* path) {
// path falls in our namespace
// path falls in our namespace
auto
md
=
gkfs
::
utils
::
get_metadata
(
rel_path
);
auto
md
=
gkfs
::
utils
::
get_metadata
(
rel_path
);
if
(
!
md
)
{
if
(
!
md
)
{
LOG
(
ERROR
,
"{}() path {} errno {}"
,
__func__
,
path
,
errno
);
LOG
(
ERROR
,
"{}() path {} / {} errno {}"
,
__func__
,
path
,
rel_path
,
errno
);
return
-
errno
;
return
-
errno
;
}
}
...
@@ -802,6 +821,11 @@ hook_readlinkat(int dirfd, const char* cpath, char* buf, int bufsiz) {
...
@@ -802,6 +821,11 @@ hook_readlinkat(int dirfd, const char* cpath, char* buf, int bufsiz) {
return
-
ENOTDIR
;
return
-
ENOTDIR
;
case
gkfs
::
preload
::
RelativizeStatus
::
internal
:
case
gkfs
::
preload
::
RelativizeStatus
::
internal
:
#ifdef HAS_SYMLINKS
return
with_errno
(
gkfs
::
syscall
::
gkfs_readlink
(
resolved
,
buf
,
bufsiz
));
#endif
return
-
EINVAL
;
return
-
EINVAL
;
default:
default:
...
@@ -861,22 +885,78 @@ hook_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) {
...
@@ -861,22 +885,78 @@ hook_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) {
ret
|=
O_RDWR
;
ret
|=
O_RDWR
;
}
}
return
ret
;
return
ret
;
case
F_SETFL
:
LOG
(
DEBUG
,
"{}() F_SETFL on fd {}"
,
__func__
,
fd
);
// get flags from arg and setup
if
(
arg
&
O_RDONLY
)
{
CTX
->
file_map
()
->
get
(
fd
)
->
set_flag
(
gkfs
::
filemap
::
OpenFile_flags
::
rdonly
,
true
);
}
if
(
arg
&
O_WRONLY
)
{
CTX
->
file_map
()
->
get
(
fd
)
->
set_flag
(
gkfs
::
filemap
::
OpenFile_flags
::
wronly
,
true
);
}
if
(
arg
&
O_RDWR
)
{
CTX
->
file_map
()
->
get
(
fd
)
->
set_flag
(
gkfs
::
filemap
::
OpenFile_flags
::
rdwr
,
true
);
}
if
(
arg
&
O_APPEND
)
{
CTX
->
file_map
()
->
get
(
fd
)
->
set_flag
(
gkfs
::
filemap
::
OpenFile_flags
::
append
,
true
);
}
if
(
arg
&
O_NONBLOCK
)
{
LOG
(
DEBUG
,
"[GKFS] F_SETFL {} NONBLOCK"
,
fd
);
}
if
(
arg
&
O_ASYNC
)
{
LOG
(
DEBUG
,
"[GKFS] F_SETFL {} ASYNC"
,
fd
);
}
if
(
arg
&
O_CLOEXEC
)
{
LOG
(
DEBUG
,
"[GKFS] F_SETFL {} CLOEXEC"
,
fd
);
CTX
->
file_map
()
->
get
(
fd
)
->
set_flag
(
gkfs
::
filemap
::
OpenFile_flags
::
cloexec
,
true
);
}
return
0
;
case
F_SETFD
:
case
F_SETFD
:
LOG
(
DEBUG
,
"{}() [fd: {}, cmd: F_SETFD, FD_CLOEXEC: {}]"
,
__func__
,
LOG
(
DEBUG
,
"{}() [fd: {}, cmd: F_SETFD, FD_CLOEXEC: {}]"
,
__func__
,
fd
,
(
arg
&
FD_CLOEXEC
));
fd
,
(
arg
&
FD_CLOEXEC
));
CTX
->
file_map
()
->
get
(
fd
)
->
set_flag
(
CTX
->
file_map
()
->
get
(
fd
)
->
set_flag
(
gkfs
::
filemap
::
OpenFile_flags
::
cloexec
,
(
arg
&
FD_CLOEXEC
));
gkfs
::
filemap
::
OpenFile_flags
::
cloexec
,
(
arg
&
FD_CLOEXEC
));
return
0
;
return
0
;
#ifdef ENABLE_LOCKING
case
F_GETLK
:
case
F_GETLK
:
LOG
(
ERROR
,
"{}() F_GETLK on fd (Not Supported) {}"
,
__func__
,
fd
);
LOG
(
ERROR
,
"{}() F_GETLK on fd (on underlying fd) {}"
,
__func__
,
return
0
;
fd
);
return
gsl
::
narrow_cast
<
int
>
(
syscall_no_intercept_wrapper
(
SYS_fcntl
,
fd
,
cmd
,
arg
));
case
F_SETLK
:
case
F_SETLK
:
LOG
(
ERROR
,
"{}() F_SETLK on fd (Not Supported) {}"
,
__func__
,
fd
);
LOG
(
ERROR
,
"{}() F_SETLK on fd (on underlying fd) {}"
,
__func__
,
return
0
;
fd
);
return
gsl
::
narrow_cast
<
int
>
(
syscall_no_intercept_wrapper
(
SYS_fcntl
,
fd
,
cmd
,
arg
));
case
F_SETLKW
:
LOG
(
ERROR
,
"{}() F_SETLKW on fd (on underlying fd) {}"
,
__func__
,
fd
);
return
gsl
::
narrow_cast
<
int
>
(
syscall_no_intercept_wrapper
(
SYS_fcntl
,
fd
,
cmd
,
arg
));
case
F_GETOWN
:
case
__F_GETOWN_EX
:
LOG
(
ERROR
,
"{}() F_GETOWN on fd (on underlying fd) {}"
,
__func__
,
fd
);
return
gsl
::
narrow_cast
<
int
>
(
syscall_no_intercept_wrapper
(
SYS_fcntl
,
fd
,
cmd
,
arg
));
case
F_SETOWN
:
case
__F_SETOWN_EX
:
LOG
(
ERROR
,
"{}() F_SETOWN on fd (on underlying fd) {}"
,
__func__
,
fd
);
return
gsl
::
narrow_cast
<
int
>
(
syscall_no_intercept_wrapper
(
SYS_fcntl
,
fd
,
cmd
,
arg
));
#endif
default
:
default
:
LOG
(
ERROR
,
"{}() unrecognized command {} on fd {}"
,
__func__
,
cmd
,
LOG
(
ERROR
,
"{}() unrecognized command {} on fd {}"
,
__func__
,
cmd
,
fd
);
fd
);
...
...
src/client/intercept.cpp
View file @
5c0ec2a1
...
@@ -92,6 +92,62 @@ get_current_syscall_info() {
...
@@ -92,6 +92,62 @@ get_current_syscall_info() {
return
saved_syscall_info
;
return
saved_syscall_info
;
}
}
std
::
vector
<
int
>
get_open_fds
()
{
std
::
vector
<
int
>
fds
;
const
int
buffer_size
=
4096
;
char
buffer
[
buffer_size
];
// Open /proc/self/fd directory using raw syscall
int
dir_fd
=
syscall_no_intercept_wrapper
(
SYS_open
,
"/proc/self/fd"
,
O_RDONLY
|
O_DIRECTORY
,
0
);
if
(
dir_fd
<
0
)
{
return
fds
;
}
while
(
true
)
{
// Read directory entries using getdents64 syscall
int
nread
=
syscall_no_intercept_wrapper
(
SYS_getdents64
,
dir_fd
,
buffer
,
buffer_size
);
if
(
nread
<=
0
)
break
;
for
(
int
bpos
=
0
;
bpos
<
nread
;)
{
auto
*
dent
=
reinterpret_cast
<
linux_dirent64
*>
(
buffer
+
bpos
);
// Skip . and .. entries
const
std
::
string
d_name
(
dent
->
d_name
);
if
(
d_name
==
"."
||
d_name
==
".."
)
{
bpos
+=
dent
->
d_reclen
;
continue
;
}
try
{
int
fd
=
std
::
stoi
(
d_name
);
// Skip the directory FD itself
if
(
fd
!=
dir_fd
)
{
fds
.
push_back
(
fd
);
}
}
catch
(
const
std
::
exception
&
)
{
// Ignore non-integer entries
}
bpos
+=
dent
->
d_reclen
;
}
}
// Close directory using raw syscall
syscall_no_intercept_wrapper
(
SYS_close
,
dir_fd
);
// Filter out non-open FDs (optional safety check)
fds
.
erase
(
std
::
remove_if
(
fds
.
begin
(),
fds
.
end
(),
[](
int
fd
)
{
return
syscall
(
SYS_fcntl
,
fd
,
F_GETFD
)
<
0
;
}),
fds
.
end
());
return
fds
;
}
/*
/*
* hook_internal -- interception hook for internal syscalls
* hook_internal -- interception hook for internal syscalls
...
@@ -512,15 +568,18 @@ hook(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4,
...
@@ -512,15 +568,18 @@ hook(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4,
*
result
=
gkfs
::
hook
::
hook_close
(
static_cast
<
int
>
(
arg0
));
*
result
=
gkfs
::
hook
::
hook_close
(
static_cast
<
int
>
(
arg0
));
break
;
break
;
#ifdef SYS_close_range
#ifdef SYS_close_range
case
SYS_close_range
:
case
SYS_close_range
:
{
for
(
auto
i
=
arg0
;
i
<=
arg1
;
i
++
)
{
auto
fds
=
get_open_fds
();
if
(
i
>=
GKFS_MAX_OPEN_FDS
)
for
(
auto
fd
:
fds
)
{
break
;
if
(
fd
<
static_cast
<
int
>
(
arg0
)
||
fd
>
static_cast
<
int
>
(
arg1
))
if
(
CTX
->
file_map
()
->
exist
(
i
))
{
continue
;
gkfs
::
syscall
::
gkfs_close
(
i
);
if
(
CTX
->
file_map
()
->
exist
(
fd
))
{
}
gkfs
::
syscall
::
gkfs_close
(
fd
);
}
else
close
(
fd
);
*
result
=
0
;
*
result
=
0
;
}
}
}
*
result
=
0
;
*
result
=
0
;
break
;
break
;
#endif // SYS_close_range
#endif // SYS_close_range
...
...
src/client/path.cpp
View file @
5c0ec2a1
...
@@ -58,6 +58,7 @@
...
@@ -58,6 +58,7 @@
extern
"C"
{
extern
"C"
{
#include
<sys/stat.h>
#include
<sys/stat.h>
#include
<dlfcn.h>
}
}
using
namespace
std
;
using
namespace
std
;
...
@@ -111,16 +112,25 @@ match_components(const string& path, unsigned int& path_components,
...
@@ -111,16 +112,25 @@ match_components(const string& path, unsigned int& path_components,
return
matched
;
return
matched
;
}
}
static
char
*
(
*
real_realpath
)(
const
char
*
path
,
char
*
resolved_path
)
=
nullptr
;
string
string
follow_symlinks
(
const
string
&
path
)
{
follow_symlinks
(
const
string
&
path
)
{
struct
stat
st
{};
struct
stat
st
{};
if
(
lstat
(
path
.
c_str
(),
&
st
)
<
0
)
{
auto
res
=
syscall_no_intercept
(
SYS_lstat
,
path
.
c_str
(),
&
st
);
if
(
res
<
0
)
{
LOG
(
DEBUG
,
"path
\"
{}
\"
does not exist"
,
path
);
LOG
(
DEBUG
,
"path
\"
{}
\"
does not exist"
,
path
);
return
path
;
return
path
;
}
}
if
(
S_ISLNK
(
st
.
st_mode
))
{
if
(
S_ISLNK
(
st
.
st_mode
))
{
auto
link_resolved
=
::
unique_ptr
<
char
[]
>
(
new
char
[
PATH_MAX
]);
auto
link_resolved
=
::
unique_ptr
<
char
[]
>
(
new
char
[
PATH_MAX
]);
if
(
realpath
(
path
.
c_str
(),
link_resolved
.
get
())
==
nullptr
)
{
if
(
real_realpath
==
nullptr
)
{
real_realpath
=
reinterpret_cast
<
char
*
(
*
)
(
const
char
*
path
,
char
*
resolved_path
)
>
(
dlsym
(((
void
*
)
-
1l
),
"realpath"
));
}
if
(
real_realpath
(
path
.
c_str
(),
link_resolved
.
get
())
==
nullptr
)
{
LOG
(
ERROR
,
LOG
(
ERROR
,
"Failed to get realpath for link
\"
{}
\"
. "
"Failed to get realpath for link
\"
{}
\"
. "
...
@@ -198,7 +208,6 @@ resolve_new(const string& path, bool resolve_last_link) {
...
@@ -198,7 +208,6 @@ resolve_new(const string& path, bool resolve_last_link) {
}
}
#endif
#endif
}
}
if
(
resolved
.
substr
(
0
,
CTX
->
mountdir
().
size
())
==
CTX
->
mountdir
())
{
if
(
resolved
.
substr
(
0
,
CTX
->
mountdir
().
size
())
==
CTX
->
mountdir
())
{
resolved
.
erase
(
1
,
CTX
->
mountdir
().
size
());
resolved
.
erase
(
1
,
CTX
->
mountdir
().
size
());
LOG
(
DEBUG
,
"internal:
\"
{}
\"
"
,
resolved
);
LOG
(
DEBUG
,
"internal:
\"
{}
\"
"
,
resolved
);
...
...
src/client/preload.cpp
View file @
5c0ec2a1
...
@@ -239,6 +239,16 @@ init_environment() {
...
@@ -239,6 +239,16 @@ init_environment() {
"Failed to connect to hosts: "
s
+
e
.
what
());
"Failed to connect to hosts: "
s
+
e
.
what
());
}
}
LOG
(
INFO
,
"Lock-Files : Generator = {} / Consumer = {}"
,
(
bool
)
gkfs
::
env
::
get_var
(
gkfs
::
env
::
PROTECT_FILES_GENERATOR
,
0
),
(
bool
)
gkfs
::
env
::
get_var
(
gkfs
::
env
::
PROTECT_FILES_CONSUMER
,
0
));
CTX
->
protect_files_generator
(
gkfs
::
env
::
get_var
(
gkfs
::
env
::
PROTECT_FILES_GENERATOR
,
0
));
CTX
->
protect_files_consumer
(
gkfs
::
env
::
get_var
(
gkfs
::
env
::
PROTECT_FILES_CONSUMER
,
0
));
/* Setup distributor */
/* Setup distributor */
auto
forwarding_map_file
=
gkfs
::
env
::
get_var
(
auto
forwarding_map_file
=
gkfs
::
env
::
get_var
(
gkfs
::
env
::
FORWARDING_MAP_FILE
,
gkfs
::
config
::
forwarding_file_path
);
gkfs
::
env
::
FORWARDING_MAP_FILE
,
gkfs
::
config
::
forwarding_file_path
);
...
@@ -329,13 +339,14 @@ init_environment() {
...
@@ -329,13 +339,14 @@ init_environment() {
}
}
}
}
LOG
(
INFO
,
"Retrieving file system configuration..."
);
//
LOG(INFO, "Retrieving file system configuration...");
if
(
!
gkfs
::
rpc
::
forward_get_fs_config
())
{
// if(!gkfs::rpc::forward_get_fs_config()) {
exit_error_msg
(
// exit_error_msg(
EXIT_FAILURE
,
// EXIT_FAILURE,
"Unable to fetch file system configurations from daemon process through RPC."
);
// "Unable to fetch file system configurations from daemon
}
// process through RPC.");
// }
// Initialize random number generator and seed for replica selection
// Initialize random number generator and seed for replica selection
// in case of failure, a new replica will be selected
// in case of failure, a new replica will be selected
if
(
CTX
->
get_replicas
()
>
0
)
{
if
(
CTX
->
get_replicas
()
>
0
)
{
...
@@ -359,14 +370,12 @@ init_preload() {
...
@@ -359,14 +370,12 @@ init_preload() {
// The original errno value will be restored after initialization to not
// The original errno value will be restored after initialization to not
// leak internal error codes
// leak internal error codes
auto
oerrno
=
errno
;
auto
oerrno
=
errno
;
if
(
atomic_exchange
(
&
init
,
1
)
==
0
)
{
pthread_atfork
(
&
at_fork_syscall
,
&
at_parent_syscall
,
&
at_child_syscall
);
}
CTX
->
enable_interception
();
CTX
->
enable_interception
();
gkfs
::
preload
::
start_self_interception
();
gkfs
::
preload
::
start_self_interception
();
if
(
!
init
)
{
init
=
true
;
pthread_atfork
(
&
at_fork_syscall
,
&
at_parent_syscall
,
&
at_child_syscall
);
}
CTX
->
init_logging
();
CTX
->
init_logging
();
// from here ownwards it is safe to print messages
// from here ownwards it is safe to print messages
LOG
(
DEBUG
,
"Logging subsystem initialized"
);
LOG
(
DEBUG
,
"Logging subsystem initialized"
);
...
@@ -384,9 +393,6 @@ init_preload() {
...
@@ -384,9 +393,6 @@ init_preload() {
if
(
gkfs
::
env
::
var_is_set
(
gkfs
::
env
::
PROTECT_FD
))
{
if
(
gkfs
::
env
::
var_is_set
(
gkfs
::
env
::
PROTECT_FD
))
{
CTX
->
protect_fds
(
true
);
CTX
->
protect_fds
(
true
);
LOG
(
INFO
,
"Protecting user fds"
);
LOG
(
INFO
,
"Protecting user fds"
);
}
else
{
CTX
->
protect_fds
(
false
);
LOG
(
INFO
,
"Not protecting user fds"
);
}
}
if
(
CTX
->
protect_fds
())
{
if
(
CTX
->
protect_fds
())
{
...
@@ -466,12 +472,22 @@ destroy_preload() {
...
@@ -466,12 +472,22 @@ destroy_preload() {
extern
"C"
int
extern
"C"
int
gkfs_init
()
{
gkfs_init
()
{
CTX
->
init_logging
();
CTX
->
init_logging
();
// from here ownwards it is safe to print messages
// from here ownwards it is safe to print messages
LOG
(
DEBUG
,
"Logging subsystem initialized"
);
LOG
(
DEBUG
,
"Logging subsystem initialized"
);
if
(
gkfs
::
env
::
var_is_set
(
gkfs
::
env
::
PROTECT_FD
))
{
CTX
->
protect_fds
(
true
);
LOG
(
INFO
,
"Protecting user fds"
);
}
if
(
CTX
->
protect_fds
())
{
CTX
->
protect_user_fds
();
}
gkfs
::
preload
::
init_environment
();
gkfs
::
preload
::
init_environment
();
if
(
CTX
->
protect_fds
())
CTX
->
unprotect_user_fds
();
return
0
;
return
0
;
}
}
...
@@ -489,6 +505,76 @@ gkfs_end() {
...
@@ -489,6 +505,76 @@ gkfs_end() {
return
0
;
return
0
;
}
}
/**
* @brief Automatically launch init/destroy
* NOTES: this is not called, in the child of a fork
*/
void
init_libc
()
{
CTX
->
init_logging
();
// from here ownwards it is safe to print messages
LOG
(
DEBUG
,
"Logging subsystem initialized"
);
if
(
gkfs
::
env
::
var_is_set
(
gkfs
::
env
::
PROTECT_FD
))
{
CTX
->
protect_fds
(
true
);
LOG
(
INFO
,
"Protecting user fds"
);
}
if
(
CTX
->
protect_fds
())
{
CTX
->
protect_user_fds
();
}
if
(
atomic_exchange
(
&
init
,
1
)
==
0
)
{
pthread_atfork
(
&
at_fork
,
&
at_parent
,
&
at_child
);
}
gkfs
::
preload
::
init_environment
();
if
(
CTX
->
protect_fds
())
{
CTX
->
unprotect_user_fds
();
}
CTX
->
enable_interception
();
if
(
!
CTX
->
init_metrics
())
{
exit_error_msg
(
EXIT_FAILURE
,
"Unable to initialize client metrics. Exiting..."
);
}
}
void
destroy_libc
()
{
CTX
->
disable_interception
();
#ifdef GKFS_ENABLE_CLIENT_METRICS
LOG
(
INFO
,
"Flushing final metrics..."
);
CTX
->
write_metrics
()
->
flush_msgpack
();
CTX
->
read_metrics
()
->
flush_msgpack
();
LOG
(
INFO
,
"Metrics flushed. Total flush operations: {}"
,
CTX
->
write_metrics
()
->
flush_count
());
CTX
->
destroy_metrics
();
#endif
CTX
->
clear_hosts
();
LOG
(
DEBUG
,
"Peer information deleted"
);
ld_network_service
.
reset
();
LOG
(
DEBUG
,
"RPC subsystem shut down"
);
LOG
(
INFO
,
"All subsystems shut down. Client shutdown complete."
);
}
void
at_fork
()
{
destroy_libc
();
}
void
at_parent
()
{
init_libc
();
}
void
at_child
()
{
init_libc
();
}
void
void
at_fork_syscall
()
{
at_fork_syscall
()
{
destroy_preload
();
destroy_preload
();
...
...
src/client/preload_context.cpp
View file @
5c0ec2a1
...
@@ -85,6 +85,7 @@ PreloadContext::PreloadContext()
...
@@ -85,6 +85,7 @@ PreloadContext::PreloadContext()
char
host
[
255
];
char
host
[
255
];
gethostname
(
host
,
255
);
gethostname
(
host
,
255
);
hostname
=
host
;
hostname
=
host
;
cwd_
=
gkfs
::
path
::
get_sys_cwd
();
PreloadContext
::
set_replicas
(
PreloadContext
::
set_replicas
(
std
::
stoi
(
gkfs
::
env
::
get_var
(
gkfs
::
env
::
NUM_REPL
,
"0"
)));
std
::
stoi
(
gkfs
::
env
::
get_var
(
gkfs
::
env
::
NUM_REPL
,
"0"
)));
}
}
...
@@ -659,6 +660,28 @@ PreloadContext::get_replicas() {
...
@@ -659,6 +660,28 @@ PreloadContext::get_replicas() {
return
replicas_
;
return
replicas_
;
}
}
bool
PreloadContext
::
protect_files_generator
()
const
{
return
protect_files_generator_
;
}
void
PreloadContext
::
protect_files_generator
(
bool
protect
)
{
protect_files_generator_
=
protect
;
}
bool
PreloadContext
::
protect_files_consumer
()
const
{
return
protect_files_consumer_
;
}
void
PreloadContext
::
protect_files_consumer
(
bool
protect
)
{
protect_files_consumer_
=
protect
;
}
const
std
::
shared_ptr
<
messagepack
::
ClientMetrics
>
const
std
::
shared_ptr
<
messagepack
::
ClientMetrics
>
PreloadContext
::
write_metrics
()
{
PreloadContext
::
write_metrics
()
{
return
write_metrics_
;
return
write_metrics_
;
...
...
src/client/preload_util.cpp
View file @
5c0ec2a1
...
@@ -166,8 +166,10 @@ load_hostfile(const std::string& path) {
...
@@ -166,8 +166,10 @@ load_hostfile(const std::string& path) {
path
,
strerror
(
errno
)));
path
,
strerror
(
errno
)));
}
}
vector
<
pair
<
string
,
string
>>
hosts
;
vector
<
pair
<
string
,
string
>>
hosts
;
const
regex
line_re
(
"^(
\\
S+)
\\
s+(
\\
S+)
\\
s*(
\\
S*)$"
,
const
regex
line_re
(
"^(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)$"
,
regex
::
ECMAScript
|
regex
::
optimize
);
regex
::
ECMAScript
|
regex
::
optimize
);
string
line
;
string
line
;
string
host
;
string
host
;
string
uri
;
string
uri
;
...
@@ -185,9 +187,25 @@ load_hostfile(const std::string& path) {
...
@@ -185,9 +187,25 @@ load_hostfile(const std::string& path) {
throw
runtime_error
(
throw
runtime_error
(
fmt
::
format
(
"unrecognized line format: '{}'"
,
line
));
fmt
::
format
(
"unrecognized line format: '{}'"
,
line
));
}
}
host
=
match
[
1
];
host
=
match
[
1
];
uri
=
match
[
2
];
uri
=
match
[
2
];
// match[3] that is the proxy (not used here)
hosts
.
emplace_back
(
host
,
uri
);
hosts
.
emplace_back
(
host
,
uri
);
// info will be repeated line per line:
CTX
->
mountdir
(
match
[
4
]);
LOG
(
INFO
,
"Mountdir: '{}'"
,
CTX
->
mountdir
());
CTX
->
fs_conf
()
->
rootdir
=
match
[
5
];
CTX
->
fs_conf
()
->
atime_state
=
match
[
6
]
==
'1'
;
CTX
->
fs_conf
()
->
mtime_state
=
match
[
7
]
==
'1'
;
CTX
->
fs_conf
()
->
ctime_state
=
match
[
8
]
==
'1'
;
CTX
->
fs_conf
()
->
link_cnt_state
=
match
[
9
]
==
'1'
;
CTX
->
fs_conf
()
->
blocks_state
=
match
[
10
]
==
'1'
;
// convert match[11] and match[12] to unsigned integers.
CTX
->
fs_conf
()
->
uid
=
std
::
stoi
(
match
[
11
]);
CTX
->
fs_conf
()
->
gid
=
std
::
stoi
(
match
[
12
]);
}
}
if
(
hosts
.
empty
())
{
if
(
hosts
.
empty
())
{
throw
runtime_error
(
throw
runtime_error
(
...
@@ -264,6 +282,7 @@ get_metadata(const string& path, bool follow_links) {
...
@@ -264,6 +282,7 @@ get_metadata(const string& path, bool follow_links) {
#ifdef HAS_SYMLINKS
#ifdef HAS_SYMLINKS
if
(
follow_links
)
{
if
(
follow_links
)
{
gkfs
::
metadata
::
Metadata
md
{
attr
};
gkfs
::
metadata
::
Metadata
md
{
attr
};
auto
original
=
md
;
while
(
md
.
is_link
())
{
while
(
md
.
is_link
())
{
if
(
gkfs
::
config
::
proxy
::
fwd_stat
&&
CTX
->
use_proxy
())
{
if
(
gkfs
::
config
::
proxy
::
fwd_stat
&&
CTX
->
use_proxy
())
{
err
=
gkfs
::
rpc
::
forward_stat_proxy
(
md
.
target_path
(),
attr
);
err
=
gkfs
::
rpc
::
forward_stat_proxy
(
md
.
target_path
(),
attr
);
...
...
src/client/rpc/forward_metadata.cpp
View file @
5c0ec2a1
...
@@ -406,9 +406,34 @@ int
...
@@ -406,9 +406,34 @@ int
forward_rename
(
const
string
&
oldpath
,
const
string
&
newpath
,
forward_rename
(
const
string
&
oldpath
,
const
string
&
newpath
,
const
gkfs
::
metadata
::
Metadata
&
md
)
{
const
gkfs
::
metadata
::
Metadata
&
md
)
{
auto
endp
=
CTX
->
hosts
().
at
(
auto
endp
=
CTX
->
hosts
().
at
(
CTX
->
distributor
()
->
locate_file_metadata
(
oldpath
,
0
));
CTX
->
distributor
()
->
locate_file_metadata
(
oldpath
,
0
));
if
(
newpath
==
""
)
{
// Just cleanup rename status
try
{
LOG
(
DEBUG
,
"Sending RPC ..."
);
// TODO(amiranda): add a post() with RPC_TIMEOUT to hermes so that
// we can retry for RPC_TRIES (see old commits with margo)
// TODO(amiranda): hermes will eventually provide a post(endpoint)
// returning one result and a broadcast(endpoint_set) returning a
// result_set. When that happens we can remove the .at(0) :/
auto
out
=
ld_network_service
->
post
<
gkfs
::
rpc
::
rename
>
(
endp
,
oldpath
,
newpath
)
.
get
()
.
at
(
0
);
LOG
(
DEBUG
,
"Got response success: {}"
,
out
.
err
());
// return out.err() ? out.err() : 0;
return
0
;
}
catch
(
const
std
::
exception
&
ex
)
{
LOG
(
ERROR
,
"while getting rpc output"
);
return
EBUSY
;
}
}
try
{
try
{
LOG
(
DEBUG
,
"Sending RPC ..."
);
LOG
(
DEBUG
,
"Sending RPC ..."
);
// TODO(amiranda): add a post() with RPC_TIMEOUT to hermes so that we
// TODO(amiranda): add a post() with RPC_TIMEOUT to hermes so that we
...
@@ -484,9 +509,8 @@ forward_rename(const string& oldpath, const string& newpath,
...
@@ -484,9 +509,8 @@ forward_rename(const string& oldpath, const string& newpath,
// returning one result and a broadcast(endpoint_set) returning a
// returning one result and a broadcast(endpoint_set) returning a
// result_set. When that happens we can remove the .at(0) :/
// result_set. When that happens we can remove the .at(0) :/
// Update new file with target link = oldpath
// Update new file with target link = oldpath
auto
out
=
auto
out
=
ld_network_service
ld_network_service
->
post
<
gkfs
::
rpc
::
rename
>
(
endp2
,
newpath
,
oldpath
)
->
post
<
gkfs
::
rpc
::
mk_symlink
>
(
endp2
,
newpath
,
oldpath
)
.
get
()
.
get
()
.
at
(
0
);
.
at
(
0
);
...
@@ -508,7 +532,7 @@ forward_rename(const string& oldpath, const string& newpath,
...
@@ -508,7 +532,7 @@ forward_rename(const string& oldpath, const string& newpath,
// returning one result and a broadcast(endpoint_set) returning a
// returning one result and a broadcast(endpoint_set) returning a
// result_set. When that happens we can remove the .at(0) :/
// result_set. When that happens we can remove the .at(0) :/
auto
out
=
ld_network_service
auto
out
=
ld_network_service
->
post
<
gkfs
::
rpc
::
mk_symlink
>
(
endp
,
oldpath
,
newpath
)
->
post
<
gkfs
::
rpc
::
rename
>
(
endp
,
oldpath
,
newpath
)
.
get
()
.
get
()
.
at
(
0
);
.
at
(
0
);
...
...
src/client/rpc/rpc_types.cpp
View file @
5c0ec2a1
...
@@ -62,6 +62,9 @@ hermes::detail::register_user_request_types(uint32_t provider_id) {
...
@@ -62,6 +62,9 @@ hermes::detail::register_user_request_types(uint32_t provider_id) {
#ifdef HAS_SYMLINKS
#ifdef HAS_SYMLINKS
(
void
)
registered_requests
().
add
<
gkfs
::
rpc
::
mk_symlink
>
(
provider_id
);
(
void
)
registered_requests
().
add
<
gkfs
::
rpc
::
mk_symlink
>
(
provider_id
);
#endif // HAS_SYMLINKS
#endif // HAS_SYMLINKS
#ifdef HAS_RENAME
(
void
)
registered_requests
().
add
<
gkfs
::
rpc
::
rename
>
(
provider_id
);
#endif // HAS_RENAME
(
void
)
registered_requests
().
add
<
gkfs
::
rpc
::
remove_data
>
(
provider_id
);
(
void
)
registered_requests
().
add
<
gkfs
::
rpc
::
remove_data
>
(
provider_id
);
(
void
)
registered_requests
().
add
<
gkfs
::
rpc
::
write_data
>
(
provider_id
);
(
void
)
registered_requests
().
add
<
gkfs
::
rpc
::
write_data
>
(
provider_id
);
(
void
)
registered_requests
().
add
<
gkfs
::
rpc
::
read_data
>
(
provider_id
);
(
void
)
registered_requests
().
add
<
gkfs
::
rpc
::
read_data
>
(
provider_id
);
...
...
src/common/statistics/stats.cpp
View file @
5c0ec2a1
...
@@ -163,9 +163,10 @@ Stats::output_map(std::ofstream& output) {
...
@@ -163,9 +163,10 @@ Stats::output_map(std::ofstream& output) {
}
}
auto
chunkMap
=
auto
chunkMap
=
[](
std
::
string
caption
,
[](
const
std
::
string
&
caption
,
map
<
unsigned
int
,
const
map
<
unsigned
int
,
std
::
set
<
pair
<
std
::
string
,
unsigned
long
long
>>>&
order
,
std
::
set
<
pair
<
std
::
string
,
unsigned
long
long
>>>&
order
,
std
::
ofstream
&
output
)
{
std
::
ofstream
&
output
)
{
output
<<
caption
<<
std
::
endl
;
output
<<
caption
<<
std
::
endl
;
for
(
auto
k
:
order
)
{
for
(
auto
k
:
order
)
{
...
...
src/daemon/daemon.cpp
View file @
5c0ec2a1
...
@@ -178,6 +178,10 @@ register_server_rpcs(margo_instance_id mid) {
...
@@ -178,6 +178,10 @@ register_server_rpcs(margo_instance_id mid) {
#ifdef HAS_SYMLINKS
#ifdef HAS_SYMLINKS
MARGO_REGISTER
(
mid
,
gkfs
::
rpc
::
tag
::
mk_symlink
,
rpc_mk_symlink_in_t
,
MARGO_REGISTER
(
mid
,
gkfs
::
rpc
::
tag
::
mk_symlink
,
rpc_mk_symlink_in_t
,
rpc_err_out_t
,
rpc_srv_mk_symlink
);
rpc_err_out_t
,
rpc_srv_mk_symlink
);
#endif
#ifdef HAS_RENAME
MARGO_REGISTER
(
mid
,
gkfs
::
rpc
::
tag
::
rename
,
rpc_rename_in_t
,
rpc_err_out_t
,
rpc_srv_rename
);
#endif
#endif
MARGO_REGISTER
(
mid
,
gkfs
::
rpc
::
tag
::
write
,
rpc_write_data_in_t
,
MARGO_REGISTER
(
mid
,
gkfs
::
rpc
::
tag
::
write
,
rpc_write_data_in_t
,
rpc_data_out_t
,
rpc_srv_write
);
rpc_data_out_t
,
rpc_srv_write
);
...
@@ -584,7 +588,7 @@ agios_initialize() {
...
@@ -584,7 +588,7 @@ agios_initialize() {
agios_exit
();
agios_exit
();
throw
;
std
::
terminate
()
;
}
}
}
}
#endif
#endif
...
@@ -885,10 +889,9 @@ parse_input(const cli_options& opts, const CLI::App& desc) {
...
@@ -885,10 +889,9 @@ parse_input(const cli_options& opts, const CLI::App& desc) {
metadir_path
.
native
());
metadir_path
.
native
());
}
else
{
}
else
{
// use rootdir as metadata dir
// use rootdir as metadata dir
auto
metadir
=
opts
.
rootdir
;
if
(
GKFS_DATA
->
enable_forwarding
())
{
if
(
GKFS_DATA
->
enable_forwarding
())
{
auto
metadir
=
opts
.
rootdir
;
// As we store normally he metadata to the pfs, we need to put each
// As we store normally he metadata to the pfs, we need to put each
// daemon in a separate directory.
// daemon in a separate directory.
auto
metadir_path
=
auto
metadir_path
=
...
...
src/daemon/handler/srv_metadata.cpp
View file @
5c0ec2a1
...
@@ -847,7 +847,66 @@ rpc_srv_get_dirents_extended(hg_handle_t handle) {
...
@@ -847,7 +847,66 @@ rpc_srv_get_dirents_extended(hg_handle_t handle) {
return
gkfs
::
rpc
::
cleanup_respond
(
&
handle
,
&
in
,
&
out
,
&
bulk_handle
);
return
gkfs
::
rpc
::
cleanup_respond
(
&
handle
,
&
in
,
&
out
,
&
bulk_handle
);
}
}
#if defined(HAS_SYMLINKS) || defined(HAS_RENAME)
#if defined(HAS_SYMLINKS)
/**
* @brief Serves a request create a symbolic link
* @internal
* The state of this function is unclear and requires a complete refactor.
*
* All exceptions must be caught here and dealt with accordingly. Any errors are
* placed in the response.
* @endinternal
* @param handle Mercury RPC handle (path is the symbolic link)
* @return Mercury error code to Mercury
*/
hg_return_t
rpc_srv_mk_symlink
(
hg_handle_t
handle
)
{
rpc_mk_symlink_in_t
in
{};
rpc_err_out_t
out
{};
auto
ret
=
margo_get_input
(
handle
,
&
in
);
if
(
ret
!=
HG_SUCCESS
)
{
GKFS_DATA
->
spdlogger
()
->
error
(
"{}() Failed to retrieve input from handle"
,
__func__
);
}
GKFS_DATA
->
spdlogger
()
->
debug
(
"{}() Got RPC with path '{}' and target path '{}'"
,
__func__
,
in
.
path
,
in
.
target_path
);
// do update
try
{
gkfs
::
metadata
::
Metadata
md
=
gkfs
::
metadata
::
get
(
in
.
path
);
md
.
target_path
(
in
.
target_path
);
md
.
mode
(
S_IFLNK
);
md
.
blocks
(
0
);
GKFS_DATA
->
spdlogger
()
->
debug
(
"{}() Updating path '{}' with metadata '{}'"
,
__func__
,
in
.
path
,
md
.
serialize
());
gkfs
::
metadata
::
update
(
in
.
path
,
md
);
out
.
err
=
0
;
}
catch
(
const
std
::
exception
&
e
)
{
// TODO handle NotFoundException
GKFS_DATA
->
spdlogger
()
->
error
(
"{}() Failed to update entry"
,
__func__
);
out
.
err
=
1
;
}
GKFS_DATA
->
spdlogger
()
->
debug
(
"{}() Sending output err '{}'"
,
__func__
,
out
.
err
);
auto
hret
=
margo_respond
(
handle
,
&
out
);
if
(
hret
!=
HG_SUCCESS
)
{
GKFS_DATA
->
spdlogger
()
->
error
(
"{}() Failed to respond"
,
__func__
);
}
// Destroy handle when finished
margo_free_input
(
handle
,
&
in
);
margo_destroy
(
handle
);
return
HG_SUCCESS
;
}
#endif // HAS_SYMLINKS
#if defined(HAS_RENAME)
/**
/**
* @brief Serves a request create a symbolic link and supports rename
* @brief Serves a request create a symbolic link and supports rename
* @internal
* @internal
...
@@ -856,11 +915,11 @@ rpc_srv_get_dirents_extended(hg_handle_t handle) {
...
@@ -856,11 +915,11 @@ rpc_srv_get_dirents_extended(hg_handle_t handle) {
* All exceptions must be caught here and dealt with accordingly. Any errors are
* All exceptions must be caught here and dealt with accordingly. Any errors are
* placed in the response.
* placed in the response.
* @endinteral
* @endinteral
* @param handle Mercury RPC handle
* @param handle Mercury RPC handle
(target_path is the symbolic link)
* @return Mercury error code to Mercury
* @return Mercury error code to Mercury
*/
*/
hg_return_t
hg_return_t
rpc_srv_
mk_symlink
(
hg_handle_t
handle
)
{
rpc_srv_
rename
(
hg_handle_t
handle
)
{
rpc_mk_symlink_in_t
in
{};
rpc_mk_symlink_in_t
in
{};
rpc_err_out_t
out
{};
rpc_err_out_t
out
{};
...
@@ -875,17 +934,14 @@ rpc_srv_mk_symlink(hg_handle_t handle) {
...
@@ -875,17 +934,14 @@ rpc_srv_mk_symlink(hg_handle_t handle) {
// do update
// do update
try
{
try
{
gkfs
::
metadata
::
Metadata
md
=
gkfs
::
metadata
::
get
(
in
.
path
);
gkfs
::
metadata
::
Metadata
md
=
gkfs
::
metadata
::
get
(
in
.
path
);
#ifdef HAS_RENAME
if
(
md
.
blocks
()
==
-
1
)
{
// We need to fill the rename path as this is an inverse path
// old -> new
md
.
rename_path
(
in
.
target_path
);
}
else
{
#endif // HAS_RENAME
md
.
target_path
(
in
.
target_path
);
md
.
target_path
(
in
.
target_path
);
#ifdef HAS_RENAME
// We are reverting the rename so we clean up the target_path
if
(
strcmp
(
in
.
target_path
,
""
)
==
0
)
{
md
.
blocks
(
0
);
}
}
#endif // HAS_RENAME
GKFS_DATA
->
spdlogger
()
->
debug
(
GKFS_DATA
->
spdlogger
()
->
debug
(
"{}() Updating path '{}' with metadata '{}'"
,
__func__
,
in
.
path
,
"{}() Updating path '{}' with metadata '{}'"
,
__func__
,
in
.
path
,
md
.
serialize
());
md
.
serialize
());
...
@@ -910,7 +966,7 @@ rpc_srv_mk_symlink(hg_handle_t handle) {
...
@@ -910,7 +966,7 @@ rpc_srv_mk_symlink(hg_handle_t handle) {
return
HG_SUCCESS
;
return
HG_SUCCESS
;
}
}
#endif //
HAS_SYMLINKS ||
HAS_RENAME
#endif // HAS_RENAME
}
// namespace
}
// namespace
...
@@ -938,3 +994,6 @@ DEFINE_MARGO_RPC_HANDLER(rpc_srv_get_dirents_extended)
...
@@ -938,3 +994,6 @@ DEFINE_MARGO_RPC_HANDLER(rpc_srv_get_dirents_extended)
DEFINE_MARGO_RPC_HANDLER
(
rpc_srv_mk_symlink
)
DEFINE_MARGO_RPC_HANDLER
(
rpc_srv_mk_symlink
)
#endif
#endif
#ifdef HAS_RENAME
DEFINE_MARGO_RPC_HANDLER
(
rpc_srv_rename
)
#endif
src/daemon/malleability/malleable_manager.cpp
View file @
5c0ec2a1
...
@@ -75,8 +75,10 @@ MalleableManager::load_hostfile(const std::string& path) {
...
@@ -75,8 +75,10 @@ MalleableManager::load_hostfile(const std::string& path) {
path
,
strerror
(
errno
)));
path
,
strerror
(
errno
)));
}
}
vector
<
pair
<
string
,
string
>>
hosts
;
vector
<
pair
<
string
,
string
>>
hosts
;
const
regex
line_re
(
"^(
\\
S+)
\\
s+(
\\
S+)
\\
s*(
\\
S*)$"
,
const
regex
line_re
(
"^(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)$"
,
regex
::
ECMAScript
|
regex
::
optimize
);
regex
::
ECMAScript
|
regex
::
optimize
);
string
line
;
string
line
;
string
host
;
string
host
;
string
uri
;
string
uri
;
...
...
src/daemon/util.cpp
View file @
5c0ec2a1
...
@@ -47,6 +47,7 @@
...
@@ -47,6 +47,7 @@
#include
<iostream>
#include
<iostream>
#include
<limits>
#include
<limits>
#include
<thread>
#include
<thread>
#include
<unistd.h>
using
namespace
std
;
using
namespace
std
;
...
@@ -114,6 +115,16 @@ populate_hosts_file() {
...
@@ -114,6 +115,16 @@ populate_hosts_file() {
auto
line_out
=
fmt
::
format
(
"{} {}"
,
hostname
,
daemon_addr
);
auto
line_out
=
fmt
::
format
(
"{} {}"
,
hostname
,
daemon_addr
);
if
(
!
proxy_addr
.
empty
())
if
(
!
proxy_addr
.
empty
())
line_out
=
fmt
::
format
(
"{} {}"
,
line_out
,
proxy_addr
);
line_out
=
fmt
::
format
(
"{} {}"
,
line_out
,
proxy_addr
);
if
(
proxy_addr
.
empty
())
line_out
=
fmt
::
format
(
"{} {}"
,
line_out
,
"NOPROXY"
);
line_out
=
fmt
::
format
(
"{} {} {} {} {} {} {} {} {} {}"
,
line_out
,
GKFS_DATA
->
mountdir
(),
GKFS_DATA
->
rootdir
(),
(
int
)
GKFS_DATA
->
atime_state
(),
(
int
)
GKFS_DATA
->
mtime_state
(),
(
int
)
GKFS_DATA
->
ctime_state
(),
(
int
)
GKFS_DATA
->
link_cnt_state
(),
(
int
)
GKFS_DATA
->
blocks_state
(),
getuid
(),
getgid
());
// Constants for retry mechanism
// Constants for retry mechanism
const
int
MAX_RETRIES
=
5
;
// Maximum number of retry attempts
const
int
MAX_RETRIES
=
5
;
// Maximum number of retry attempts
const
std
::
chrono
::
milliseconds
RETRY_DELAY
(
const
std
::
chrono
::
milliseconds
RETRY_DELAY
(
...
...
src/proxy/util.cpp
View file @
5c0ec2a1
...
@@ -56,8 +56,10 @@ load_hostfile(const std::string& lfpath) {
...
@@ -56,8 +56,10 @@ load_hostfile(const std::string& lfpath) {
lfpath
,
strerror
(
errno
)));
lfpath
,
strerror
(
errno
)));
}
}
vector
<
pair
<
string
,
string
>>
hosts
;
vector
<
pair
<
string
,
string
>>
hosts
;
const
regex
line_re
(
"^(
\\
S+)
\\
s+(
\\
S+)
\\
s*(
\\
S*)$"
,
const
regex
line_re
(
"^(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)
\\
s+(
\\
S+)$"
,
regex
::
ECMAScript
|
regex
::
optimize
);
regex
::
ECMAScript
|
regex
::
optimize
);
string
line
;
string
line
;
string
host
;
string
host
;
string
uri
;
string
uri
;
...
@@ -213,8 +215,8 @@ connect_to_hosts(const vector<pair<string, string>>& hosts) {
...
@@ -213,8 +215,8 @@ connect_to_hosts(const vector<pair<string, string>>& hosts) {
ret
=
margo_addr_lookup
(
PROXY_DATA
->
client_rpc_mid
(),
uri
.
c_str
(),
ret
=
margo_addr_lookup
(
PROXY_DATA
->
client_rpc_mid
(),
uri
.
c_str
(),
&
svr_addr
);
&
svr_addr
);
if
(
ret
!=
HG_SUCCESS
)
{
if
(
ret
!=
HG_SUCCESS
)
{
// still not working after
5
tries.
// still not working after
4
tries.
if
(
i
==
4
)
{
if
(
i
==
3
)
{
auto
err_msg
=
auto
err_msg
=
fmt
::
format
(
"{}() Unable to lookup address '{}'"
,
fmt
::
format
(
"{}() Unable to lookup address '{}'"
,
__func__
,
uri
);
__func__
,
uri
);
...
...
test/symlink_test.cpp
View file @
5c0ec2a1
...
@@ -57,7 +57,7 @@ main(int argc, char* argv[]) {
...
@@ -57,7 +57,7 @@ main(int argc, char* argv[]) {
const
std
::
string
link_ext
=
dir_ext
+
"/tmp/link"
;
const
std
::
string
link_ext
=
dir_ext
+
"/tmp/link"
;
char
buffIn
[]
=
"oops."
;
char
buffIn
[]
=
"oops."
;
char
*
buffOut
=
new
char
[
strlen
(
buffIn
)
+
1
];
char
buffOut
[
strlen
(
buffIn
)
+
1
];
struct
stat
st
;
struct
stat
st
;
int
ret
;
int
ret
;
...
@@ -170,7 +170,7 @@ main(int argc, char* argv[]) {
...
@@ -170,7 +170,7 @@ main(int argc, char* argv[]) {
// Check readlink
// Check readlink
char
*
target_path
=
new
char
[
target_int
.
size
()
+
1
];
char
target_path
[
target_int
.
size
()
+
1
];
ret
=
readlink
(
link_int
.
c_str
(),
target_path
,
target_int
.
size
()
+
1
);
ret
=
readlink
(
link_int
.
c_str
(),
target_path
,
target_int
.
size
()
+
1
);
if
(
ret
<=
0
)
{
if
(
ret
<=
0
)
{
std
::
cerr
<<
"ERROR: Failed to retrieve link path: "
<<
strerror
(
errno
)
std
::
cerr
<<
"ERROR: Failed to retrieve link path: "
<<
strerror
(
errno
)
...
...
test/wr_test.cpp
View file @
5c0ec2a1
...
@@ -61,7 +61,7 @@ main(int argc, char* argv[]) {
...
@@ -61,7 +61,7 @@ main(int argc, char* argv[]) {
string
mountdir
=
"/tmp/mountdir"
;
string
mountdir
=
"/tmp/mountdir"
;
string
p
=
mountdir
+
"/file"
;
string
p
=
mountdir
+
"/file"
;
char
buffIn
[]
=
"oops."
;
char
buffIn
[]
=
"oops."
;
char
*
buffOut
=
new
char
[
strlen
(
buffIn
)
+
1
+
20
];
char
buffOut
[
strlen
(
buffIn
)
+
1
+
20
];
int
fd
;
int
fd
;
int
ret
;
int
ret
;
struct
stat
st
;
struct
stat
st
;
...
...
tests/integration/conftest.py
View file @
5c0ec2a1
...
@@ -34,7 +34,7 @@ from pathlib import Path
...
@@ -34,7 +34,7 @@ from pathlib import Path
from
harness.logger
import
logger
,
initialize_logging
,
finalize_logging
from
harness.logger
import
logger
,
initialize_logging
,
finalize_logging
from
harness.cli
import
add_cli_options
,
set_default_log_formatter
from
harness.cli
import
add_cli_options
,
set_default_log_formatter
from
harness.workspace
import
Workspace
,
FileCreator
from
harness.workspace
import
Workspace
,
FileCreator
from
harness.gkfs
import
Daemon
,
Client
,
Proxy
,
ShellClient
,
FwdDaemon
,
FwdClient
,
ShellFwdClient
,
FwdDaemonCreator
,
FwdClientCreator
from
harness.gkfs
import
Daemon
,
Client
,
ClientLibc
,
Proxy
,
ShellClient
,
ShellClientLibc
,
FwdDaemon
,
FwdClient
,
ShellFwdClient
,
FwdDaemonCreator
,
FwdClientCreator
from
harness.reporter
import
report_test_status
,
report_test_headline
,
report_assertion_pass
from
harness.reporter
import
report_test_status
,
report_test_headline
,
report_assertion_pass
def
pytest_configure
(
config
):
def
pytest_configure
(
config
):
...
@@ -159,6 +159,16 @@ def gkfs_client_proxy(test_workspace):
...
@@ -159,6 +159,16 @@ def gkfs_client_proxy(test_workspace):
return
Client
(
test_workspace
,
True
)
return
Client
(
test_workspace
,
True
)
@pytest.fixture
def
gkfs_clientLibc
(
test_workspace
):
"""
Sets up a gekkofs client environment so that
operations (system calls, library calls, ...) can
be requested from a co-running daemon.
"""
return
ClientLibc
(
test_workspace
)
@pytest.fixture
@pytest.fixture
def
gkfs_shell
(
test_workspace
):
def
gkfs_shell
(
test_workspace
):
"""
"""
...
@@ -177,6 +187,15 @@ def gkfs_shell_proxy(test_workspace):
...
@@ -177,6 +187,15 @@ def gkfs_shell_proxy(test_workspace):
return
ShellClient
(
test_workspace
,
True
)
return
ShellClient
(
test_workspace
,
True
)
@pytest.fixture
def
gkfs_shellLibc
(
test_workspace
):
"""
Sets up a gekkofs environment so that shell commands
(stat, ls, mkdir, etc.) can be issued to a co-running daemon.
"""
return
ShellClientLibc
(
test_workspace
)
@pytest.fixture
@pytest.fixture
def
file_factory
(
test_workspace
):
def
file_factory
(
test_workspace
):
"""
"""
...
...
tests/integration/conftest.template
View file @
5c0ec2a1
...
@@ -34,7 +34,7 @@ from pathlib import Path
...
@@ -34,7 +34,7 @@ from pathlib import Path
from harness.logger import logger, initialize_logging, finalize_logging
from harness.logger import logger, initialize_logging, finalize_logging
from harness.cli import add_cli_options, set_default_log_formatter
from harness.cli import add_cli_options, set_default_log_formatter
from harness.workspace import Workspace, FileCreator
from harness.workspace import Workspace, FileCreator
from harness.gkfs import Daemon, Client, Proxy, ShellClient, FwdDaemon, FwdClient, ShellFwdClient, FwdDaemonCreator, FwdClientCreator
from harness.gkfs import Daemon, Client,
ClientLibc,
Proxy, ShellClient,
ShellClientLibc,
FwdDaemon, FwdClient, ShellFwdClient, FwdDaemonCreator, FwdClientCreator
from harness.reporter import report_test_status, report_test_headline, report_assertion_pass
from harness.reporter import report_test_status, report_test_headline, report_assertion_pass
def pytest_configure(config):
def pytest_configure(config):
...
@@ -159,6 +159,16 @@ def gkfs_client_proxy(test_workspace):
...
@@ -159,6 +159,16 @@ def gkfs_client_proxy(test_workspace):
return Client(test_workspace, True)
return Client(test_workspace, True)
@pytest.fixture
def gkfs_clientLibc(test_workspace):
"""
Sets up a gekkofs client environment so that
operations (system calls, library calls, ...) can
be requested from a co-running daemon.
"""
return ClientLibc(test_workspace)
@pytest.fixture
@pytest.fixture
def gkfs_shell(test_workspace):
def gkfs_shell(test_workspace):
"""
"""
...
@@ -177,6 +187,15 @@ def gkfs_shell_proxy(test_workspace):
...
@@ -177,6 +187,15 @@ def gkfs_shell_proxy(test_workspace):
return ShellClient(test_workspace,True)
return ShellClient(test_workspace,True)
@pytest.fixture
def gkfs_shellLibc(test_workspace):
"""
Sets up a gekkofs environment so that shell commands
(stat, ls, mkdir, etc.) can be issued to a co-running daemon.
"""
return ShellClientLibc(test_workspace)
@pytest.fixture
@pytest.fixture
def file_factory(test_workspace):
def file_factory(test_workspace):
"""
"""
...
...
tests/integration/harness/gkfs.py
View file @
5c0ec2a1
...
@@ -40,6 +40,7 @@ from harness.cmd import CommandParser
...
@@ -40,6 +40,7 @@ from harness.cmd import CommandParser
gkfs_daemon_cmd
=
'
gkfs_daemon
'
gkfs_daemon_cmd
=
'
gkfs_daemon
'
gkfs_client_cmd
=
'
gkfs.io
'
gkfs_client_cmd
=
'
gkfs.io
'
gkfs_client_lib_file
=
'
libgkfs_intercept.so
'
gkfs_client_lib_file
=
'
libgkfs_intercept.so
'
gkfs_client_lib_libc_file
=
'
libgkfs_libc_intercept.so
'
gkfs_hosts_file
=
'
gkfs_hosts.txt
'
gkfs_hosts_file
=
'
gkfs_hosts.txt
'
gkfs_daemon_log_file
=
'
gkfs_daemon.log
'
gkfs_daemon_log_file
=
'
gkfs_daemon.log
'
gkfs_daemon_log_level
=
'
100
'
gkfs_daemon_log_level
=
'
100
'
...
@@ -255,10 +256,10 @@ class Daemon:
...
@@ -255,10 +256,10 @@ class Daemon:
def
run
(
self
):
def
run
(
self
):
args
=
[
'
--mountdir
'
,
self
.
mountdir
,
args
=
[
'
--mountdir
'
,
self
.
mountdir
.
as_posix
()
,
'
--rootdir
'
,
self
.
rootdir
,
'
--rootdir
'
,
self
.
rootdir
.
as_posix
()
,
'
-l
'
,
self
.
_address
,
'
-l
'
,
self
.
_address
,
'
--metadir
'
,
self
.
_metadir
,
'
--metadir
'
,
self
.
_metadir
.
as_posix
()
,
'
--dbbackend
'
,
self
.
_database
,
'
--dbbackend
'
,
self
.
_database
,
'
--output-stats
'
,
self
.
logdir
/
'
stats.log
'
,
'
--output-stats
'
,
self
.
logdir
/
'
stats.log
'
,
'
--enable-collection
'
,
'
--enable-collection
'
,
...
@@ -602,6 +603,87 @@ class Client:
...
@@ -602,6 +603,87 @@ class Client:
def
cwd
(
self
):
def
cwd
(
self
):
return
self
.
_workspace
.
twd
return
self
.
_workspace
.
twd
class
ClientLibc
:
"""
A class to represent a GekkoFS client process with a patched LD_PRELOAD.
This class allows tests to interact with the file system using I/O-related
function calls, be them system calls (e.g. read()) or glibc I/O functions
(e.g. opendir()).
"""
def
__init__
(
self
,
workspace
):
self
.
_parser
=
IOParser
()
self
.
_workspace
=
workspace
self
.
_cmd
=
sh
.
Command
(
gkfs_client_cmd
,
self
.
_workspace
.
bindirs
)
self
.
_env
=
os
.
environ
.
copy
()
libdirs
=
'
:
'
.
join
(
filter
(
None
,
[
os
.
environ
.
get
(
'
LD_LIBRARY_PATH
'
,
''
)]
+
[
str
(
p
)
for
p
in
self
.
_workspace
.
libdirs
]))
# ensure the client interception library is available:
# to avoid running code with potentially installed libraries,
# it must be found in one (and only one) of the workspace's bindirs
preloads
=
[]
for
d
in
self
.
_workspace
.
bindirs
:
search_path
=
Path
(
d
)
/
gkfs_client_lib_libc_file
if
search_path
.
exists
():
preloads
.
append
(
search_path
)
if
len
(
preloads
)
==
0
:
logger
.
error
(
f
'
No client libraries found in the test
\'
s binary directories:
'
)
pytest
.
exit
(
"
Aborted due to initialization error. Check test logs.
"
)
if
len
(
preloads
)
!=
1
:
logger
.
error
(
f
'
Multiple client libraries found in the test
\'
s binary directories:
'
)
for
p
in
preloads
:
logger
.
error
(
f
'
{
p
}
'
)
logger
.
error
(
f
'
Make sure that only one copy of the client library is available.
'
)
pytest
.
exit
(
"
Aborted due to initialization error. Check test logs.
"
)
self
.
_preload_library
=
preloads
[
0
]
self
.
_patched_env
=
{
'
LD_LIBRARY_PATH
'
:
libdirs
,
'
LD_PRELOAD
'
:
str
(
self
.
_preload_library
),
'
LIBGKFS_HOSTS_FILE
'
:
str
(
self
.
cwd
/
gkfs_hosts_file
),
'
LIBGKFS_LOG
'
:
gkfs_client_log_level
,
'
LIBGKFS_LOG_OUTPUT
'
:
str
(
self
.
_workspace
.
logdir
/
gkfs_client_log_file
),
'
LIBGKFS_LOG_SYSCALL_FILTER
'
:
gkfs_client_log_syscall_filter
}
self
.
_env
.
update
(
self
.
_patched_env
)
@property
def
preload_library
(
self
):
"""
Return the preload library detected for this client
"""
return
self
.
_preload_library
def
run
(
self
,
cmd
,
*
args
):
logger
.
debug
(
f
"
running client
"
)
logger
.
debug
(
f
"
cmdline:
{
self
.
_cmd
}
"
+
"
"
.
join
(
map
(
str
,
list
(
args
))))
logger
.
debug
(
f
"
patched env:
{
pformat
(
self
.
_patched_env
)
}
"
)
out
=
self
.
_cmd
(
[
cmd
]
+
list
(
args
),
_env
=
self
.
_env
,
# _out=sys.stdout,
# _err=sys.stderr,
)
logger
.
debug
(
f
"
command output:
{
out
.
stdout
}
"
)
return
self
.
_parser
.
parse
(
cmd
,
out
.
stdout
)
def
__getattr__
(
self
,
name
):
return
_proxy_exec
(
self
,
name
)
@property
def
cwd
(
self
):
return
self
.
_workspace
.
twd
class
ShellCommand
:
class
ShellCommand
:
"""
"""
A wrapper class for sh.RunningCommand that allows seamlessly using all
A wrapper class for sh.RunningCommand that allows seamlessly using all
...
@@ -814,6 +896,233 @@ class ShellClient:
...
@@ -814,6 +896,233 @@ class ShellClient:
found_cmd
=
shutil
.
which
(
cmd
,
path
=
'
:
'
.
join
(
str
(
p
)
for
p
in
self
.
_search_paths
)
)
if
not
found_cmd
:
raise
sh
.
CommandNotFound
(
cmd
)
self
.
_cmd
=
sh
.
Command
(
found_cmd
)
logger
.
debug
(
f
"
running program
"
)
logger
.
debug
(
f
"
cmd:
{
cmd
}
{
'
'
.
join
(
str
(
a
)
for
a
in
args
)
}
"
)
logger
.
debug
(
f
"
search_paths:
{
'
:
'
.join(str(p) for p in self._search_paths)
}
"
)
logger
.
debug
(
f
"
timeout:
{
timeout
}
seconds
"
)
logger
.
debug
(
f
"
timeout_signal:
{
signal
.
Signals
(
timeout_signal
).
name
}
"
)
logger
.
debug
(
f
"
patched env:
\n
{
pformat
(
self
.
_patched_env
)
}
"
)
# 'sh' raises an exception if the return code is not zero;
# since we'd rather check for return codes explictly, we
# whitelist all exit codes from 1 to 255 as 'ok' using the
# _ok_code argument
proc
=
self
.
_cmd
(
args
,
_env
=
self
.
_env
,
# _out=sys.stdout,
# _err=sys.stderr,
_timeout
=
timeout
,
_timeout_signal
=
timeout_signal
,
# _ok_code=list(range(0, 256))
)
logger
.
debug
(
f
"
program stdout:
{
proc
.
stdout
}
"
)
logger
.
debug
(
f
"
program stderr:
{
proc
.
stderr
}
"
)
return
ShellCommand
(
cmd
,
proc
)
def
__getattr__
(
self
,
name
):
return
_proxy_exec
(
self
,
name
)
@property
def
cwd
(
self
):
return
self
.
_workspace
.
twd
class
ShellClientLibc
:
"""
A class to represent a GekkoFS shell client process.
This class allows tests to execute shell commands or scripts via bash -c
on a GekkoFS instance.
"""
def
__init__
(
self
,
workspace
):
self
.
_workspace
=
workspace
self
.
_search_paths
=
_find_search_paths
(
self
.
_workspace
.
bindirs
)
self
.
_env
=
os
.
environ
.
copy
()
libdirs
=
'
:
'
.
join
(
filter
(
None
,
[
os
.
environ
.
get
(
'
LD_LIBRARY_PATH
'
,
''
)]
+
[
str
(
p
)
for
p
in
self
.
_workspace
.
libdirs
]))
# ensure the client interception library is available:
# to avoid running code with potentially installed libraries,
# it must be found in one (and only one) of the workspace's bindirs
preloads
=
[]
for
d
in
self
.
_workspace
.
bindirs
:
search_path
=
Path
(
d
)
/
gkfs_client_lib_libc_file
if
search_path
.
exists
():
preloads
.
append
(
search_path
)
if
len
(
preloads
)
!=
1
:
logger
.
error
(
f
'
Multiple client libraries found in the test
\'
s binary directories:
'
)
for
p
in
preloads
:
logger
.
error
(
f
'
{
p
}
'
)
logger
.
error
(
f
'
Make sure that only one copy of the client library is available.
'
)
pytest
.
exit
(
"
Aborted due to initialization error
"
)
self
.
_preload_library
=
preloads
[
0
]
self
.
_patched_env
=
{
'
LD_LIBRARY_PATH
'
:
libdirs
,
'
LD_PRELOAD
'
:
str
(
self
.
_preload_library
),
'
LIBGKFS_HOSTS_FILE
'
:
str
(
self
.
cwd
/
gkfs_hosts_file
),
'
LIBGKFS_LOG
'
:
gkfs_client_log_level
,
'
LIBGKFS_LOG_OUTPUT
'
:
str
(
self
.
_workspace
.
logdir
/
gkfs_client_log_file
),
'
LIBGKFS_LOG_SYSCALL_FILTER
'
:
gkfs_client_log_syscall_filter
}
self
.
_env
.
update
(
self
.
_patched_env
)
@property
def
patched_environ
(
self
):
"""
Return the patched environment required to run a test as a string that
can be prepended to a shell command.
"""
return
'
'
.
join
(
f
'
{
k
}
=
"
{
v
}
"'
for
k
,
v
in
self
.
_patched_env
.
items
())
def
script
(
self
,
code
,
intercept_shell
=
True
,
timeout
=
60
,
timeout_signal
=
signal
.
SIGKILL
):
"""
Execute a shell script passed as an argument in bash.
For instance, the following snippet:
mountdir = pathlib.Path(
'
/tmp
'
)
file01 =
'
file01
'
ShellClient().script(
f
'''
expected_pathname={mountdir / file01}
if [[ -e ${{expected_pathname}} ]];
then
exit 0
fi
exit 1
'''
)
transforms into:
bash -c
'
expected_pathname=/tmp/file01
if [[ -e ${expected_pathname} ]];
then
exit 0
fi
exit 1
'
Note that since we are using Python
'
s f-strings, for variable
expansions to work correctly, they need to be defined with double
braces, e.g. ${{expected_pathname}}.
Parameters
----------
code: `str`
The script code to be passed to
'
bash -c
'
.
intercept_shell: `bool`
Controls whether the shell executing the script should be
executed with LD_PRELOAD=libgkfs_intercept.so (default: True).
timeout: `int`
How much time, in seconds, we should give the process to complete.
If the process does not finish within the timeout, it will be sent
the signal defined by `timeout_signal`.
Default value: 60
timeout_signal: `int`
The signal to be sent to the process if `timeout` is not None.
Default value: signal.SIGKILL
Returns
-------
A sh.RunningCommand instance that allows interacting with
the finished process.
"""
logger
.
debug
(
f
"
running bash
"
)
logger
.
debug
(
f
"
cmd: bash -c
'
{
code
}
'"
)
logger
.
debug
(
f
"
timeout:
{
timeout
}
seconds
"
)
logger
.
debug
(
f
"
timeout_signal:
{
signal
.
Signals
(
timeout_signal
).
name
}
"
)
if
intercept_shell
:
logger
.
debug
(
f
"
patched env:
{
self
.
_patched_env
}
"
)
self
.
_cmd
=
sh
.
Command
(
"
bash
"
)
# 'sh' raises an exception if the return code is not zero;
# since we'd rather check for return codes explictly, we
# whitelist all exit codes from 1 to 255 as 'ok' using the
# _ok_code argument
return
self
.
_cmd
(
'
-c
'
,
code
,
_env
=
(
self
.
_env
if
intercept_shell
else
os
.
environ
),
# _out=sys.stdout,
# _err=sys.stderr,
_timeout
=
timeout
,
_timeout_signal
=
timeout_signal
,
# _ok_code=list(range(0, 256))
)
def
run
(
self
,
cmd
,
*
args
,
timeout
=
60
,
timeout_signal
=
signal
.
SIGKILL
):
"""
Execute a shell command with arguments.
For example, the following snippet:
mountdir = pathlib.Path(
'
/tmp
'
)
file01 =
'
file01
'
ShellClient().stat(
'
--terse
'
, mountdir / file01)
transforms into:
bash -c
'
stat --terse /tmp/file01
'
Parameters:
-----------
cmd: `str`
The command to execute.
args: `list`
The list of arguments for the command.
timeout: `number`
How much time, in seconds, we should give the process to complete.
If the process does not finish within the timeout, it will be sent
the signal defined by `timeout_signal`.
Default value: 60
timeout_signal: `int`
The signal to be sent to the process if `timeout` is not None.
Default value: signal.SIGKILL
Returns
-------
A ShellCommand instance that allows interacting with the finished
process. Note that ShellCommand wraps sh.RunningCommand and adds s
extra properties to it.
"""
found_cmd
=
shutil
.
which
(
cmd
,
found_cmd
=
shutil
.
which
(
cmd
,
path
=
'
:
'
.
join
(
str
(
p
)
for
p
in
self
.
_search_paths
)
path
=
'
:
'
.
join
(
str
(
p
)
for
p
in
self
.
_search_paths
)
)
)
...
...
Prev
1
2
3
Next