Newer
Older
Copyright 2018-2020, Barcelona Supercomputing Center (BSC), Spain
Copyright 2015-2020, Johannes Gutenberg Universitaet Mainz, Germany
This software was partially supported by the
EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu).
This software was partially supported by the
ADA-FS project under the SPPEXA project funded by the DFG.
SPDX-License-Identifier: MIT
*/
#include <client/hooks.hpp>
#include <client/preload.hpp>
#include <client/preload_util.hpp>
#include <client/logging.hpp>
#include <client/gkfs_functions.hpp>
#include <client/open_dir.hpp>
#include <global/path_util.hpp>
#include <memory>
extern "C" {
#include <sys/stat.h>
#include <sys/statfs.h>
namespace {
inline int with_errno(int ret) {
return (ret < 0) ? -errno : ret;
} // namespace
namespace gkfs {
namespace hook {
int hook_openat(int dirfd, const char* cpath, int flags, mode_t mode) {
LOG(DEBUG, "{}() called with fd: {}, path: \"{}\", flags: {}, mode: {}",
__func__, dirfd, cpath, flags, mode);
std::string resolved;
auto rstatus = CTX->relativize_fd_path(dirfd, cpath, resolved);
switch (rstatus) {
case gkfs::preload::RelativizeStatus::fd_unknown:
return syscall_no_intercept(SYS_openat, dirfd, cpath, flags, mode);
case gkfs::preload::RelativizeStatus::external:
return syscall_no_intercept(SYS_openat, dirfd, resolved.c_str(), flags, mode);
case gkfs::preload::RelativizeStatus::fd_not_a_dir:
case gkfs::preload::RelativizeStatus::internal:
return with_errno(gkfs::syscall::gkfs_open(resolved, mode, flags));
LOG(ERROR, "{}() relativize status unknown: {}", __func__);
LOG(DEBUG, "{}() called with fd: {}", __func__, fd);
if (CTX->file_map()->exist(fd)) {
// No call to the daemon is required
CTX->file_map()->remove(fd);
return 0;
}
if (CTX->is_internal_fd(fd)) {
// the client application (for some reason) is trying to close an
// internal fd: ignore it
return 0;
}
return syscall_no_intercept(SYS_close, fd);
}
int hook_stat(const char* path, struct stat* buf) {
LOG(DEBUG, "{}() called with path: \"{}\", buf: {}",
__func__, path, fmt::ptr(buf));
std::string rel_path;
if (CTX->relativize_path(path, rel_path, false)) {
return with_errno(gkfs::syscall::gkfs_stat(rel_path, buf));
}
return syscall_no_intercept(SYS_stat, rel_path.c_str(), buf);
}
int hook_lstat(const char* path, struct stat* buf) {
LOG(DEBUG, "{}() called with path: \"{}\", buf: {}",
__func__, path, fmt::ptr(buf));
std::string rel_path;
if (CTX->relativize_path(path, rel_path)) {
return with_errno(gkfs::syscall::gkfs_stat(rel_path, buf));
}
return syscall_no_intercept(SYS_lstat, rel_path.c_str(), buf);
}
int hook_fstat(unsigned int fd, struct stat* buf) {
LOG(DEBUG, "{}() called with fd: {}, buf: {}",
__func__, fd, fmt::ptr(buf));
if (CTX->file_map()->exist(fd)) {
auto path = CTX->file_map()->get(fd)->path();
return with_errno(gkfs::syscall::gkfs_stat(path, buf));
}
return syscall_no_intercept(SYS_fstat, fd, buf);
}
int hook_fstatat(int dirfd, const char* cpath, struct stat* buf, int flags) {
LOG(DEBUG, "{}() called with path: \"{}\", fd: {}, buf: {}, flags: {}",
__func__, cpath, dirfd, fmt::ptr(buf), flags);
if (flags & AT_EMPTY_PATH) {
LOG(ERROR, "{}() AT_EMPTY_PATH flag not supported", __func__);
return -ENOTSUP;
}
std::string resolved;
auto rstatus = CTX->relativize_fd_path(dirfd, cpath, resolved);
switch (rstatus) {
case gkfs::preload::RelativizeStatus::fd_unknown:
return syscall_no_intercept(SYS_newfstatat, dirfd, cpath, buf, flags);
case gkfs::preload::RelativizeStatus::external:
return syscall_no_intercept(SYS_newfstatat, dirfd, resolved.c_str(), buf, flags);
case gkfs::preload::RelativizeStatus::fd_not_a_dir:
case gkfs::preload::RelativizeStatus::internal:
return with_errno(gkfs::syscall::gkfs_stat(resolved, buf));
LOG(ERROR, "{}() relativize status unknown: {}", __func__);
int hook_read(unsigned int fd, void* buf, size_t count) {
LOG(DEBUG, "{}() called with fd: {}, buf: {} count: {}",
__func__, fd, fmt::ptr(buf), count);
return with_errno(gkfs::syscall::gkfs_read(fd, buf, count));
}
return syscall_no_intercept(SYS_read, fd, buf, count);
}
int hook_pread(unsigned int fd, char* buf, size_t count, loff_t pos) {
LOG(DEBUG, "{}() called with fd: {}, buf: {}, count: {}, pos: {}",
__func__, fd, fmt::ptr(buf), count, pos);
return with_errno(gkfs::syscall::gkfs_pread_ws(fd, buf, count, pos));
}
/* Since kernel 2.6: pread() became pread64(), and pwrite() became pwrite64(). */
return syscall_no_intercept(SYS_pread64, fd, buf, count, pos);
}
int hook_write(unsigned int fd, const char* buf, size_t count) {
LOG(DEBUG, "{}() called with fd: {}, buf: {}, count {}",
__func__, fd, fmt::ptr(buf), count);
return with_errno(gkfs::syscall::gkfs_write(fd, buf, count));
}
return syscall_no_intercept(SYS_write, fd, buf, count);
}
int hook_pwrite(unsigned int fd, const char* buf, size_t count, loff_t pos) {
LOG(DEBUG, "{}() called with fd: {}, buf: {}, count: {}, pos: {}",
__func__, fd, fmt::ptr(buf), count, pos);
return with_errno(gkfs::syscall::gkfs_pwrite_ws(fd, buf, count, pos));
}
/* Since kernel 2.6: pread() became pread64(), and pwrite() became pwrite64(). */
return syscall_no_intercept(SYS_pwrite64, fd, buf, count, pos);
}
int hook_writev(unsigned long fd, const struct iovec* iov, unsigned long iovcnt) {
LOG(DEBUG, "{}() called with fd: {}, iov: {}, iovcnt: {}",
__func__, fd, fmt::ptr(iov), iovcnt);
return with_errno(gkfs::syscall::gkfs_writev(fd, iov, iovcnt));
}
return syscall_no_intercept(SYS_writev, fd, iov, iovcnt);
}
int hook_pwritev(unsigned long fd, const struct iovec* iov, unsigned long iovcnt,
LOG(DEBUG, "{}() called with fd: {}, iov: {}, iovcnt: {}, "
"pos_l: {}," "pos_h: {}",
__func__, fd, fmt::ptr(iov), iovcnt, pos_l, pos_h);
return with_errno(gkfs::syscall::gkfs_pwritev(fd, iov, iovcnt, pos_l));
}
return syscall_no_intercept(SYS_pwritev, fd, iov, iovcnt);
}
int hook_unlinkat(int dirfd, const char* cpath, int flags) {
LOG(DEBUG, "{}() called with dirfd: {}, path: \"{}\", flags: {}",
__func__, dirfd, cpath, flags);
LOG(ERROR, "{}() Flags unknown: {}", __func__, flags);
return -EINVAL;
}
std::string resolved;
auto rstatus = CTX->relativize_fd_path(dirfd, cpath, resolved, false);
switch (rstatus) {
case gkfs::preload::RelativizeStatus::fd_unknown:
return syscall_no_intercept(SYS_unlinkat, dirfd, cpath, flags);
case gkfs::preload::RelativizeStatus::external:
return syscall_no_intercept(SYS_unlinkat, dirfd, resolved.c_str(), flags);
case gkfs::preload::RelativizeStatus::fd_not_a_dir:
case gkfs::preload::RelativizeStatus::internal:
if (flags & AT_REMOVEDIR) {
return with_errno(gkfs::syscall::gkfs_rmdir(resolved));
return with_errno(gkfs::syscall::gkfs_remove(resolved));
LOG(ERROR, "{}() relativize status unknown: {}", __func__);
int hook_symlinkat(const char* oldname, int newdfd, const char* newname) {
LOG(DEBUG, "{}() called with oldname: \"{}\", newfd: {}, newname: \"{}\"",
__func__, oldname, newdfd, newname);
std::string oldname_resolved;
if (CTX->relativize_path(oldname, oldname_resolved)) {
LOG(WARNING, "{}() operation not supported", __func__);
return -ENOTSUP;
}
std::string newname_resolved;
auto rstatus = CTX->relativize_fd_path(newdfd, newname, newname_resolved, false);
switch (rstatus) {
case gkfs::preload::RelativizeStatus::fd_unknown:
return syscall_no_intercept(SYS_symlinkat, oldname, newdfd, newname);
case gkfs::preload::RelativizeStatus::external:
return syscall_no_intercept(SYS_symlinkat, oldname, newdfd, newname_resolved.c_str());
case gkfs::preload::RelativizeStatus::fd_not_a_dir:
case gkfs::preload::RelativizeStatus::internal:
LOG(WARNING, "{}() operation not supported", __func__);
LOG(ERROR, "{}() relativize status unknown", __func__);
LOG(DEBUG, "{}() called path: \"{}\", mask: {}",
__func__, path, mask);
std::string rel_path;
if (CTX->relativize_path(path, rel_path)) {
auto ret = gkfs::syscall::gkfs_access(rel_path, mask);
if (ret < 0) {
return -errno;
}
return ret;
}
return syscall_no_intercept(SYS_access, rel_path.c_str(), mask);
}
int hook_faccessat(int dirfd, const char* cpath, int mode) {
LOG(DEBUG, "{}() called with dirfd: {}, path: \"{}\", mode: {}",
__func__, dirfd, cpath, mode);
std::string resolved;
auto rstatus = CTX->relativize_fd_path(dirfd, cpath, resolved);
switch (rstatus) {
case gkfs::preload::RelativizeStatus::fd_unknown:
return syscall_no_intercept(SYS_faccessat, dirfd, cpath, mode);
case gkfs::preload::RelativizeStatus::external:
return syscall_no_intercept(SYS_faccessat, dirfd, resolved.c_str(), mode);
case gkfs::preload::RelativizeStatus::fd_not_a_dir:
case gkfs::preload::RelativizeStatus::internal:
return with_errno(gkfs::syscall::gkfs_access(resolved, mode));
LOG(ERROR, "{}() relativize status unknown: {}", __func__);
off_t hook_lseek(unsigned int fd, off_t offset, unsigned int whence) {
LOG(DEBUG, "{}() called with fd: {}, offset: {}, whence: {}",
__func__, fd, offset, whence);
auto off_ret = gkfs::syscall::gkfs_lseek(fd, static_cast<off64_t>(offset), whence);
if (off_ret > std::numeric_limits<off_t>::max()) {
return -EOVERFLOW;
} else if (off_ret < 0) {
LOG(DEBUG, "{}() returning {}", __func__, off_ret);
return syscall_no_intercept(SYS_lseek, fd, offset, whence);
int hook_truncate(const char* path, long length) {
LOG(DEBUG, "{}() called with path: {}, offset: {}",
__func__, path, length);
std::string rel_path;
if (CTX->relativize_path(path, rel_path)) {
return with_errno(gkfs::syscall::gkfs_truncate(rel_path, length));
}
return syscall_no_intercept(SYS_truncate, rel_path.c_str(), length);
}
int hook_ftruncate(unsigned int fd, unsigned long length) {
LOG(DEBUG, "{}() called with fd: {}, offset: {}",
__func__, fd, length);
if (CTX->file_map()->exist(fd)) {
auto path = CTX->file_map()->get(fd)->path();
return with_errno(gkfs::syscall::gkfs_truncate(path, length));
}
return syscall_no_intercept(SYS_ftruncate, fd, length);
}
LOG(DEBUG, "{}() called with oldfd: {}",
return with_errno(gkfs::syscall::gkfs_dup(fd));
}
return syscall_no_intercept(SYS_dup, fd);
}
int hook_dup2(unsigned int oldfd, unsigned int newfd) {
LOG(DEBUG, "{}() called with oldfd: {}, newfd: {}",
__func__, oldfd, newfd);
return with_errno(gkfs::syscall::gkfs_dup2(oldfd, newfd));
}
return syscall_no_intercept(SYS_dup2, oldfd, newfd);
}
int hook_dup3(unsigned int oldfd, unsigned int newfd, int flags) {
LOG(DEBUG, "{}() called with oldfd: {}, newfd: {}, flags: {}",
__func__, oldfd, newfd, flags);
if (CTX->file_map()->exist(oldfd)) {
// TODO implement O_CLOEXEC flag first which is used with fcntl(2)
// It is in glibc since kernel 2.9. So maybe not that important :)
LOG(WARNING, "{}() Not supported", __func__);
return -ENOTSUP;
}
return syscall_no_intercept(SYS_dup3, oldfd, newfd, flags);
}
int hook_getdents(unsigned int fd, struct dirent* dirp, unsigned int count) {
LOG(DEBUG, "{}() called with fd: {}, dirp: {}, count: {}",
__func__, fd, fmt::ptr(dirp), count);
return with_errno(gkfs::syscall::gkfs_getdents(fd, dirp, count));
}
return syscall_no_intercept(SYS_getdents, fd, dirp, count);
}
int hook_getdents64(unsigned int fd, struct dirent64* dirp, unsigned int count) {
LOG(DEBUG, "{}() called with fd: {}, dirp: {}, count: {}",
__func__, fd, fmt::ptr(dirp), count);
if (CTX->file_map()->exist(fd)) {
return with_errno(gkfs::syscall::gkfs_getdents64(fd, dirp, count));
}
return syscall_no_intercept(SYS_getdents64, fd, dirp, count);
}
int hook_mkdirat(int dirfd, const char* cpath, mode_t mode) {
LOG(DEBUG, "{}() called with dirfd: {}, path: \"{}\", mode: {}",
__func__, dirfd, cpath, mode);
std::string resolved;
auto rstatus = CTX->relativize_fd_path(dirfd, cpath, resolved);
switch (rstatus) {
case gkfs::preload::RelativizeStatus::external:
return syscall_no_intercept(SYS_mkdirat, dirfd, resolved.c_str(), mode);
case gkfs::preload::RelativizeStatus::fd_unknown:
return syscall_no_intercept(SYS_mkdirat, dirfd, cpath, mode);
case gkfs::preload::RelativizeStatus::fd_not_a_dir:
case gkfs::preload::RelativizeStatus::internal:
return with_errno(gkfs::syscall::gkfs_create(resolved, mode | S_IFDIR));
LOG(ERROR, "{}() relativize status unknown: {}", __func__);
int hook_fchmodat(int dirfd, const char* cpath, mode_t mode) {
LOG(DEBUG, "{}() called dirfd: {}, path: \"{}\", mode: {}",
__func__, dirfd, cpath, mode);
auto rstatus = CTX->relativize_fd_path(dirfd, cpath, resolved);
switch (rstatus) {
case gkfs::preload::RelativizeStatus::fd_unknown:
return syscall_no_intercept(SYS_fchmodat, dirfd, cpath, mode);
case gkfs::preload::RelativizeStatus::external:
return syscall_no_intercept(SYS_fchmodat, dirfd, resolved.c_str(), mode);
case gkfs::preload::RelativizeStatus::fd_not_a_dir:
case gkfs::preload::RelativizeStatus::internal:
LOG(WARNING, "{}() operation not supported", __func__);
LOG(ERROR, "{}() relativize status unknown: {}", __func__);
return -EINVAL;
}
}
int hook_fchmod(unsigned int fd, mode_t mode) {
LOG(DEBUG, "{}() called with fd: {}, mode: {}",
__func__, fd, mode);
LOG(WARNING, "{}() operation not supported", __func__);
return -ENOTSUP;
}
return syscall_no_intercept(SYS_fchmod, fd, mode);
}
int hook_chdir(const char* path) {
LOG(DEBUG, "{}() called with path: \"{}\"",
std::string rel_path;
bool internal = CTX->relativize_path(path, rel_path);
if (internal) {
//path falls in our namespace
auto md = gkfs::util::get_metadata(rel_path);
LOG(ERROR, "{}() path does not exists", __func__);
if (!S_ISDIR(md->mode())) {
LOG(ERROR, "{}() path is not a directory", __func__);
return -ENOTDIR;
}
//TODO get complete path from relativize_path instead of
// removing mountdir and then adding again here
rel_path.insert(0, CTX->mountdir());
if (gkfs::path::has_trailing_slash(rel_path)) {
// open_dir is '/'
rel_path.pop_back();
}
}
try {
gkfs::path::set_cwd(rel_path, internal);
} catch (const std::system_error& se) {
return -(se.code().value());
}
return 0;
}
int hook_fchdir(unsigned int fd) {
LOG(DEBUG, "{}() called with fd: {}",
if (CTX->file_map()->exist(fd)) {
auto open_dir = CTX->file_map()->get_dir(fd);
if (open_dir == nullptr) {
//Cast did not succeeded: open_file is a regular file
LOG(ERROR, "{}() file descriptor refers to a normal file: '{}'",
__func__, open_dir->path());
return -EBADF;
}
std::string new_path = CTX->mountdir() + open_dir->path();
if (gkfs::path::has_trailing_slash(new_path)) {
// open_dir is '/'
new_path.pop_back();
}
try {
gkfs::path::set_cwd(new_path, true);
} catch (const std::system_error& se) {
return -(se.code().value());
}
} else {
long ret = syscall_no_intercept(SYS_fchdir, fd);
if (ret < 0) {
throw std::system_error(syscall_error_code(ret),
std::system_category(),
"Failed to change directory (fchdir syscall)");
}
gkfs::path::unset_env_cwd();
CTX->cwd(gkfs::path::get_sys_cwd());
int hook_getcwd(char* buf, unsigned long size) {
LOG(DEBUG, "{}() called with buf: {}, size: {}",
__func__, fmt::ptr(buf), size);
if (CTX->cwd().size() + 1 > size) {
LOG(ERROR, "{}() buffer too small to host current working dir", __func__);
return -ERANGE;
}
strcpy(buf, CTX->cwd().c_str());
return (CTX->cwd().size() + 1);
}
int hook_readlinkat(int dirfd, const char* cpath, char* buf, int bufsiz) {
LOG(DEBUG, "{}() called with dirfd: {}, path \"{}\", buf: {}, bufsize: {}",
__func__, dirfd, cpath, fmt::ptr(buf), bufsiz);
std::string resolved;
auto rstatus = CTX->relativize_fd_path(dirfd, cpath, resolved, false);
switch (rstatus) {
case gkfs::preload::RelativizeStatus::fd_unknown:
return syscall_no_intercept(SYS_readlinkat, dirfd, cpath, buf, bufsiz);
case gkfs::preload::RelativizeStatus::external:
return syscall_no_intercept(SYS_readlinkat, dirfd, resolved.c_str(), buf, bufsiz);
case gkfs::preload::RelativizeStatus::fd_not_a_dir:
case gkfs::preload::RelativizeStatus::internal:
LOG(WARNING, "{}() not supported", __func__);
LOG(ERROR, "{}() relativize status unknown: {}", __func__);
int hook_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) {
LOG(DEBUG, "{}() called with fd: {}, cmd: {}, arg: {}",
__func__, fd, cmd, arg);
if (!CTX->file_map()->exist(fd)) {
return syscall_no_intercept(SYS_fcntl, fd, cmd, arg);
}
int ret;
switch (cmd) {
case F_DUPFD:
LOG(DEBUG, "{}() F_DUPFD on fd {}", __func__, fd);
return with_errno(gkfs::syscall::gkfs_dup(fd));
LOG(DEBUG, "{}() F_DUPFD_CLOEXEC on fd {}", __func__, fd);
ret = gkfs::syscall::gkfs_dup(fd);
if (ret == -1) {
CTX->file_map()->get(fd)->set_flag(gkfs::filemap::OpenFile_flags::cloexec, true);
LOG(DEBUG, "{}() F_GETFD on fd {}", __func__, fd);
if (CTX->file_map()->get(fd)
->get_flag(gkfs::filemap::OpenFile_flags::cloexec)) {
return FD_CLOEXEC;
}
return 0;
case F_GETFL:
LOG(DEBUG, "{}() F_GETFL on fd {}", __func__, fd);
if (CTX->file_map()->get(fd)
->get_flag(gkfs::filemap::OpenFile_flags::rdonly)) {
if (CTX->file_map()->get(fd)
->get_flag(gkfs::filemap::OpenFile_flags::wronly)) {
if (CTX->file_map()->get(fd)
->get_flag(gkfs::filemap::OpenFile_flags::rdwr)) {
ret |= O_RDWR;
}
return ret;
case F_SETFD:
LOG(DEBUG, "{}() [fd: {}, cmd: F_SETFD, FD_CLOEXEC: {}]",
__func__, fd, (arg & FD_CLOEXEC));
CTX->file_map()->get(fd)
->set_flag(gkfs::filemap::OpenFile_flags::cloexec, (arg & FD_CLOEXEC));
LOG(ERROR, "{}() unrecognized command {} on fd {}",
__func__, cmd, fd);
int hook_renameat(int olddfd, const char* oldname,
int newdfd, const char* newname,
LOG(DEBUG, "{}() called with olddfd: {}, oldname: \"{}\", newfd: {}, "
"newname \"{}\", flags {}",
__func__, olddfd, oldname, newdfd, newname, flags);
const char* oldpath_pass;
std::string oldpath_resolved;
auto oldpath_status = CTX->relativize_fd_path(olddfd, oldname, oldpath_resolved);
switch (oldpath_status) {
case gkfs::preload::RelativizeStatus::fd_unknown:
case gkfs::preload::RelativizeStatus::external:
oldpath_pass = oldpath_resolved.c_str();
break;
case gkfs::preload::RelativizeStatus::fd_not_a_dir:
case gkfs::preload::RelativizeStatus::internal:
LOG(WARNING, "{}() not supported", __func__);
LOG(ERROR, "{}() relativize status unknown", __func__);
const char* newpath_pass;
std::string newpath_resolved;
auto newpath_status = CTX->relativize_fd_path(newdfd, newname, newpath_resolved);
switch (newpath_status) {
case gkfs::preload::RelativizeStatus::fd_unknown:
case gkfs::preload::RelativizeStatus::external:
newpath_pass = newpath_resolved.c_str();
break;
case gkfs::preload::RelativizeStatus::fd_not_a_dir:
case gkfs::preload::RelativizeStatus::internal:
LOG(WARNING, "{}() not supported", __func__);
LOG(ERROR, "{}() relativize status unknown", __func__);
return syscall_no_intercept(SYS_renameat2, olddfd, oldpath_pass, newdfd, newpath_pass, flags);
int hook_statfs(const char* path, struct statfs* buf) {
LOG(DEBUG, "{}() called with path: \"{}\", buf: {}",
__func__, path, fmt::ptr(buf));
std::string rel_path;
if (CTX->relativize_path(path, rel_path)) {
return with_errno(gkfs::syscall::gkfs_statfs(buf));
}
return syscall_no_intercept(SYS_statfs, rel_path.c_str(), buf);
}
int hook_fstatfs(unsigned int fd, struct statfs* buf) {
LOG(DEBUG, "{}() called with fd: {}, buf: {}",
__func__, fd, fmt::ptr(buf));
return with_errno(gkfs::syscall::gkfs_statfs(buf));
}
return syscall_no_intercept(SYS_fstatfs, fd, buf);
}
} // namespace hook
} // namespace gkfs