Loading src/client/gkfs_libc.cpp +95 −103 Changes for src/client/gkfs_libc.cpp: 95 added lines, 103 removed lines. Original line number Diff line number Diff line Loading @@ -1099,15 +1099,11 @@ statfs(const char* path, struct statfs* buf) { int fstatfs(int fd, struct statfs* buf) { gkfs_init_routine_placeholder(); if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { auto file_info = CTX->file_map()->get(fd); if(file_info) { if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] fstatfs(fd={})", fd); return gkfs::syscall::gkfs_statfs(buf); } else { errno = EBADF; return -1; } } return dlsym_fstatfs(fd, buf); } Loading Loading @@ -1157,36 +1153,25 @@ int fstatfs64(int fd, struct statfs64* buf) { gkfs_init_routine_placeholder(); struct statfs stfs; if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { auto file_info = CTX->file_map()->get(fd); if(file_info) { if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] fstatfs64(fd={})", fd); if(gkfs::syscall::gkfs_statfs(&stfs) == 0) { memset(buf, 0, sizeof(struct statfs64)); buf->f_bsize = static_cast<decltype(buf->f_bsize)>(stfs.f_bsize); buf->f_frsize = static_cast<decltype(buf->f_frsize)>(stfs.f_frsize); buf->f_blocks = static_cast<decltype(buf->f_blocks)>(stfs.f_blocks); buf->f_bfree = static_cast<decltype(buf->f_bfree)>(stfs.f_bfree); buf->f_bavail = static_cast<decltype(buf->f_bavail)>(stfs.f_bavail); buf->f_files = static_cast<decltype(buf->f_files)>(stfs.f_files); buf->f_ffree = static_cast<decltype(buf->f_ffree)>(stfs.f_ffree); buf->f_bsize = static_cast<decltype(buf->f_bsize)>(stfs.f_bsize); buf->f_frsize = static_cast<decltype(buf->f_frsize)>(stfs.f_frsize); buf->f_blocks = static_cast<decltype(buf->f_blocks)>(stfs.f_blocks); buf->f_bfree = static_cast<decltype(buf->f_bfree)>(stfs.f_bfree); buf->f_bavail = static_cast<decltype(buf->f_bavail)>(stfs.f_bavail); buf->f_files = static_cast<decltype(buf->f_files)>(stfs.f_files); buf->f_ffree = static_cast<decltype(buf->f_ffree)>(stfs.f_ffree); buf->f_fsid = static_cast<decltype(buf->f_fsid)>(stfs.f_fsid); buf->f_namelen = static_cast<decltype(buf->f_namelen)>(stfs.f_namelen); return 0; } return -1; } else { errno = EBADF; return -1; } } return dlsym_fstatfs64(fd, buf); } Loading Loading @@ -1374,16 +1359,12 @@ truncate64(const char* path, off64_t length) { int ftruncate(int fd, off_t length) { gkfs_init_routine_placeholder(); if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { auto file_info = CTX->file_map()->get(fd); if(file_info) { DEBUG_INFO("[GKFS] ftruncate(path='{}', length={})", file_info->path(), length); if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] ftruncate(path='{}', length={})", file_info->path(), length); return gkfs::syscall::gkfs_truncate(file_info->path(), length); } else { errno = EBADF; // Should not happen if is_gkfs_fd is true return -1; } } GKFS_FALLBACK(ftruncate, fd, length); } Loading Loading @@ -1496,15 +1477,11 @@ lstat64(const char* path, struct stat64* buf) { int fstat(int fd, struct stat* buf) { gkfs_init_routine_placeholder(); if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { auto file_info = CTX->file_map()->get(fd); if(file_info) { if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] fstat(path='{}')", file_info->path()); return gkfs::syscall::gkfs_stat(file_info->path(), buf, true, true); } else { errno = EBADF; return -1; } } GKFS_FALLBACK(fstat, fd, buf); } Loading @@ -1513,20 +1490,15 @@ int fstat64(int fd, struct stat64* buf) { gkfs_init_routine_placeholder(); struct stat st; // Intermediate for GekkoFS if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { auto file_info = CTX->file_map()->get(fd); if(file_info) { if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] fstat64(path='{}')", file_info->path()); if(gkfs::syscall::gkfs_stat(file_info->path(), &st, true, true) == 0) { if(gkfs::syscall::gkfs_stat(file_info->path(), &st, true, true) == 0) { convert(&st, buf); return 0; } return -1; // errno set by gkfs_stat } else { errno = EBADF; return -1; } } GKFS_FALLBACK(fstat64, fd, buf); } Loading Loading @@ -1690,15 +1662,11 @@ __lxstat64(int ver, const char* path, struct stat64* buf) { int __fxstat(int ver, int fd, struct stat* buf) { gkfs_init_routine_placeholder(); if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { auto file_info = CTX->file_map()->get(fd); if(file_info) { if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] __fxstat(path='{}')", file_info->path()); return gkfs::syscall::gkfs_stat(file_info->path(), buf, true, true); } else { errno = EBADF; return -1; } } GKFS_FALLBACK(__fxstat, ver, fd, buf); } Loading Loading @@ -1729,20 +1697,15 @@ int __fxstat64(int ver, int fd, struct stat64* buf) { gkfs_init_routine_placeholder(); struct stat st; if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { auto file_info = CTX->file_map()->get(fd); if(file_info) { if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] __fxstat64(path='{}')", file_info->path()); if(gkfs::syscall::gkfs_stat(file_info->path(), &st, true, true) == 0) { if(gkfs::syscall::gkfs_stat(file_info->path(), &st, true, true) == 0) { convert(&st, buf); return 0; } return -1; } else { errno = EBADF; return -1; } } GKFS_FALLBACK(fxstat64, ver, fd, buf); } Loading Loading @@ -1860,16 +1823,12 @@ opendir64(const char* dirname) { DIR* fdopendir(int fd) { gkfs_init_routine_placeholder(); if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { auto file_info = CTX->file_map()->get(fd); if(file_info) { if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] fdopendir(path='{}')", file_info->path()); // Ensure this FD actually refers to a directory in GekkoFS return gekko_opendir(file_info->path()); } else { errno = EBADF; return nullptr; } } GKFS_FALLBACK(fdopendir, fd); } Loading @@ -1878,10 +1837,16 @@ struct dirent* readdir(DIR* dirp) { gkfs_init_routine_placeholder(); if(GKFS_INTERCEPTION_ENABLED() && CTX->file_map()->exist(reinterpret_cast<struct GkfsDir*>(dirp)->fd)) { int fd = reinterpret_cast<struct GkfsDir*>(dirp)->fd; auto open_dir = CTX->file_map()->get_dir(fd); if(auto file = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get( reinterpret_cast<struct GkfsDir*>(dirp)->fd) : nullptr; file) { auto open_dir = file->type() == gkfs::filemap::FileType::directory ? std::static_pointer_cast<gkfs::filemap::OpenDir>(file) : nullptr; if(open_dir == nullptr) { errno = EBADF; // Not a directory or other error return nullptr; Loading Loading @@ -1923,10 +1888,16 @@ struct dirent64* readdir64(DIR* dirp) { gkfs_init_routine_placeholder(); // Similar logic and concerns as readdir if(GKFS_INTERCEPTION_ENABLED() && CTX->file_map()->exist(reinterpret_cast<struct GkfsDir*>(dirp)->fd)) { int fd = reinterpret_cast<struct GkfsDir*>(dirp)->fd; auto open_dir = CTX->file_map()->get_dir(fd); if(auto file = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get( reinterpret_cast<struct GkfsDir*>(dirp)->fd) : nullptr; file) { auto open_dir = file->type() == gkfs::filemap::FileType::directory ? std::static_pointer_cast<gkfs::filemap::OpenDir>(file) : nullptr; if(open_dir == nullptr) { errno = EBADF; return nullptr; Loading Loading @@ -1975,8 +1946,12 @@ int closedir(DIR* dirp) { gkfs_init_routine_placeholder(); // Similar check for GekkoFS DIR* as in readdir if(GKFS_INTERCEPTION_ENABLED() && CTX->file_map()->exist(reinterpret_cast<struct GkfsDir*>(dirp)->fd)) { if(auto file = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get( reinterpret_cast<struct GkfsDir*>(dirp)->fd) : nullptr; file) { int fd = reinterpret_cast<struct GkfsDir*>(dirp)->fd; DEBUG_INFO("[GKFS] closedir(fd={})", fd); int ret = gkfs::syscall::gkfs_close(fd); // Close GekkoFS internal FD Loading @@ -1996,10 +1971,17 @@ closedir(DIR* dirp) { void seekdir(DIR* dirp, long loc) throw() { gkfs_init_routine_placeholder(); if(GKFS_INTERCEPTION_ENABLED() && CTX->file_map()->exist(reinterpret_cast<struct GkfsDir*>(dirp)->fd)) { if(auto file = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get( reinterpret_cast<struct GkfsDir*>(dirp)->fd) : nullptr; file) { int fd = reinterpret_cast<struct GkfsDir*>(dirp)->fd; auto open_dir = CTX->file_map()->get_dir(fd); auto open_dir = file->type() == gkfs::filemap::FileType::directory ? std::static_pointer_cast<gkfs::filemap::OpenDir>(file) : nullptr; if(open_dir == nullptr) { errno = EBADF; return; Loading @@ -2022,10 +2004,16 @@ seekdir(DIR* dirp, long loc) throw() { long telldir(DIR* dirp) throw() { gkfs_init_routine_placeholder(); if(GKFS_INTERCEPTION_ENABLED() && CTX->file_map()->exist(reinterpret_cast<struct GkfsDir*>(dirp)->fd)) { int fd = reinterpret_cast<struct GkfsDir*>(dirp)->fd; auto open_dir = CTX->file_map()->get_dir(fd); if(auto file = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get( reinterpret_cast<struct GkfsDir*>(dirp)->fd) : nullptr; file) { auto open_dir = file->type() == gkfs::filemap::FileType::directory ? std::static_pointer_cast<gkfs::filemap::OpenDir>(file) : nullptr; if(open_dir == nullptr) { errno = EBADF; return -1; Loading @@ -2039,12 +2027,19 @@ void rewinddir(DIR* dirstream) throw() { gkfs_init_routine_placeholder(); // rewinddir(dirp) is equivalent to seekdir(dirp, 0L). if(GKFS_INTERCEPTION_ENABLED() && CTX->file_map()->exist( reinterpret_cast<struct GkfsDir*>(dirstream)->fd)) { if(auto file = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get( reinterpret_cast<struct GkfsDir*>(dirstream) ->fd) : nullptr; file) { int fd = reinterpret_cast<struct GkfsDir*>(dirstream)->fd; DEBUG_INFO("[GKFS] rewinddir(fd={})", fd); auto open_dir = CTX->file_map()->get_dir(fd); auto open_dir = file->type() == gkfs::filemap::FileType::directory ? std::static_pointer_cast<gkfs::filemap::OpenDir>(file) : nullptr; if(open_dir == nullptr) { errno = EBADF; // Or just do nothing as per some rewinddir specs on // error Loading Loading @@ -2851,13 +2846,10 @@ fcntl(int fd, int cmd, ...) { } } if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] fcntl(fd={}, cmd={})", fd, cmd); auto file_info = CTX->file_map()->get(fd); if(!file_info) { errno = EBADF; return -1; } // Should not happen switch(cmd) { case F_DUPFD: Loading src/client/hooks.cpp +26 −23 Changes for src/client/hooks.cpp: 26 added lines, 23 removed lines. Original line number Diff line number Diff line Loading @@ -401,7 +401,7 @@ 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); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_pread(fd, buf, count, pos)); } /* Since kernel 2.6: pread() became pread64(), and pwrite() became Loading @@ -415,7 +415,7 @@ hook_readv(unsigned long fd, const struct iovec* iov, unsigned long iovcnt) { LOG(DEBUG, "{}() called with fd: {}, iov: {}, iovcnt: {}", __func__, fd, fmt::ptr(iov), iovcnt); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_readv(fd, iov, iovcnt)); } return syscall_no_intercept_wrapper(SYS_readv, fd, iov, iovcnt); Loading @@ -431,7 +431,7 @@ hook_preadv(unsigned long fd, const struct iovec* iov, unsigned long iovcnt, // "pos_h: {}", // __func__, fd, fmt::ptr(iov), iovcnt, pos_l, pos_h); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_preadv(fd, iov, iovcnt, pos_l)); } return syscall_no_intercept_wrapper(SYS_preadv, fd, iov, iovcnt, pos_l); Loading Loading @@ -460,7 +460,7 @@ 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); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_pwrite(fd, buf, count, pos)); } /* Since kernel 2.6: pread() became pread64(), and pwrite() became Loading @@ -474,7 +474,7 @@ 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); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_writev(fd, iov, iovcnt)); } return syscall_no_intercept_wrapper(SYS_writev, fd, iov, iovcnt); Loading @@ -490,7 +490,7 @@ hook_pwritev(unsigned long fd, const struct iovec* iov, unsigned long iovcnt, "pos_h: {}", __func__, fd, fmt::ptr(iov), iovcnt, pos_l, pos_h); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_pwritev(fd, iov, iovcnt, pos_l)); } return syscall_no_intercept_wrapper(SYS_pwritev, fd, iov, iovcnt, pos_l); Loading Loading @@ -676,7 +676,7 @@ hook_flock(unsigned long fd, int flags) { LOG(WARNING, "{}() flock is not supported for GekkoFS fd '{}' flags '{}'", __func__, fd, flags); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { LOG(WARNING, "{}() flock on GekkoFS fd '{}' ignored; returning success", __func__, fd); return 0; Loading Loading @@ -774,9 +774,9 @@ hook_lseek(unsigned int fd, off_t offset, unsigned int whence) { LOG(DEBUG, "{}() called with fd: {}, offset: {}, whence: {}", __func__, fd, offset, whence); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { auto off_ret = gkfs::syscall::gkfs_lseek( fd, static_cast<off64_t>(offset), whence); file, static_cast<off64_t>(offset), whence); if(off_ret > std::numeric_limits<off_t>::max()) { return -EOVERFLOW; } else if(off_ret < 0) { Loading Loading @@ -821,7 +821,7 @@ hook_dup(unsigned int fd) { LOG(DEBUG, "{}() called with oldfd: {}", __func__, fd); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_dup(fd)); } return gsl::narrow_cast<int>(syscall_no_intercept_wrapper(SYS_dup, fd)); Loading @@ -832,7 +832,7 @@ hook_dup2(unsigned int oldfd, unsigned int newfd) { LOG(DEBUG, "{}() called with oldfd: {}, newfd: {}", __func__, oldfd, newfd); if(CTX->file_map()->exist(oldfd)) { if(auto file = CTX->file_map()->get(oldfd)) { return with_errno(gkfs::syscall::gkfs_dup2(oldfd, newfd)); } return gsl::narrow_cast<int>( Loading @@ -845,7 +845,7 @@ 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)) { if(auto file = CTX->file_map()->get(oldfd)) { if(flags & O_CLOEXEC) { LOG(WARNING, "{}() Not supported", __func__); return -ENOTSUP; Loading @@ -865,7 +865,7 @@ hook_getdents(unsigned int fd, struct linux_dirent* dirp, unsigned int count) { return -EFAULT; } if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_getdents(fd, dirp, count)); } return syscall_no_intercept_wrapper(SYS_getdents, fd, dirp, count); Loading @@ -882,7 +882,7 @@ hook_getdents64(unsigned int fd, struct linux_dirent64* dirp, return -EFAULT; } if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { LOG(WARNING, "DEBUG: hook_getdents64 fd {} exists in filemap", fd); return with_errno(gkfs::syscall::gkfs_getdents64(fd, dirp, count)); } Loading Loading @@ -945,7 +945,7 @@ int hook_fchmod(unsigned int fd, mode_t mode) { LOG(DEBUG, "{}() called with fd: {}, mode: {}", __func__, fd, mode); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { LOG(WARNING, "{}() chmod on GekkoFS fd '{}' ignored; returning success", __func__, fd); return 0; Loading Loading @@ -1022,7 +1022,7 @@ hook_fchown(unsigned int fd, uid_t owner, gid_t group) { LOG(DEBUG, "{}() called with fd: {}, owner: {}, group: {}", __func__, fd, owner, group); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { LOG(WARNING, "{}() chown on GekkoFS fd '{}' ignored; returning success", __func__, fd); return 0; Loading Loading @@ -1104,8 +1104,11 @@ hook_fchdir(unsigned int fd) { LOG(DEBUG, "{}() called with fd: {}", __func__, fd); if(CTX->file_map()->exist(fd)) { auto open_dir = CTX->file_map()->get_dir(fd); if(auto file = CTX->file_map()->get(fd)) { auto open_dir = file->type() == gkfs::filemap::FileType::directory ? std::static_pointer_cast<gkfs::filemap::OpenDir>(file) : nullptr; if(open_dir == nullptr) { // Cast did not succeeded: open_file is a regular file LOG(ERROR, "{}() file descriptor refers to a normal file", Loading Loading @@ -1430,7 +1433,7 @@ hook_fstatfs(unsigned int fd, struct statfs* buf) { return -EFAULT; } if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_statfs(buf)); } return gsl::narrow_cast<int>( Loading @@ -1443,7 +1446,7 @@ int hook_fsync(unsigned int fd) { LOG(DEBUG, "{}() called with fd: {}", __func__, fd); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_fsync(fd)); } Loading Loading @@ -1516,7 +1519,7 @@ hook_flistxattr(int fd, char* list, size_t size) { LOG(DEBUG, "{}() called with fd '{}' list {} size '{}'", __func__, fd, fmt::ptr(list), size); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return -ENOTSUP; } return syscall_no_intercept_wrapper(SYS_flistxattr, fd, list, size); Loading @@ -1527,7 +1530,7 @@ hook_fallocate(int fd, int mode, off_t offset, off_t len) { LOG(DEBUG, "{}() called with fd '{}' mode '{}' offset '{}' len '{}'", __func__, fd, mode, offset, len); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return -ENOTSUP; } return syscall_no_intercept_wrapper(SYS_fallocate, fd, mode, offset, len); Loading @@ -1538,7 +1541,7 @@ hook_fadvise64(int fd, off_t offset, off_t len, int advice) { LOG(DEBUG, "{}() called with fd '{}' offset '{}' len '{}' advice '{}'", __func__, fd, offset, len, advice); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return -ENOTSUP; } return syscall_no_intercept_wrapper(SYS_fadvise64, fd, offset, len, advice); Loading tests/integration/syscalls/test_fd_close_races.py +32 −9 Changes for tests/integration/syscalls/test_fd_close_races.py: 32 added lines, 9 removed lines. Original line number Diff line number Diff line Loading @@ -3,20 +3,29 @@ import sys import pytest def test_dup_keeps_last_descriptor_visible(gkfs_shell, gkfs_daemon): @pytest.mark.parametrize("shell_fixture", ["gkfs_shell", "gkfs_shellLibc"]) def test_dup_keeps_last_descriptor_visible(shell_fixture, request, gkfs_daemon): """Duplicated descriptors must keep the map nonempty after source close.""" shell = gkfs_shell shell = request.getfixturevalue(shell_fixture) command = shell.run("env", "LIBGKFS_LOG=none", sys.executable, "-c", """ import ctypes import fcntl import os import sys path = sys.argv[1] for duplicate in (lambda fd: os.dup(fd), lambda fd: os.dup2(fd, 200000)): libc = ctypes.CDLL(None, use_errno=True) libc.dup.argtypes = [ctypes.c_int] libc.dup.restype = ctypes.c_int libc.dup2.argtypes = [ctypes.c_int, ctypes.c_int] libc.dup2.restype = ctypes.c_int libc.fcntl.restype = ctypes.c_int for duplicate in (libc.dup, lambda fd: libc.dup2(fd, 200000)): fd = os.open(path, os.O_CREAT | os.O_RDWR, 0o600) copy = duplicate(fd) assert copy >= 0, ctypes.get_errno() os.close(fd) assert fcntl.fcntl(copy, fcntl.F_GETFL) & os.O_RDWR assert libc.fcntl(copy, fcntl.F_GETFL) & os.O_RDWR os.ftruncate(copy, 0) assert os.fstat(copy).st_size == 0 os.close(copy) Loading @@ -24,7 +33,7 @@ for duplicate in (lambda fd: os.dup(fd), lambda fd: os.dup2(fd, 200000)): # Replacing an existing target must not lose or double-count its map entry. fd = os.open(path, os.O_RDWR) target = os.open(path, os.O_RDWR) assert os.dup2(fd, target) == target assert libc.dup2(fd, target) == target os.close(fd) assert os.fstat(target).st_size == 0 os.close(target) Loading @@ -41,6 +50,7 @@ def test_fd_hooks_racing_close(shell_fixture, request, gkfs_daemon): shell = request.getfixturevalue(shell_fixture) command = shell.run("env", "LIBGKFS_LOG=none", sys.executable, "-c", """ import concurrent.futures import ctypes import errno import fcntl import os Loading @@ -50,7 +60,18 @@ import threading path = sys.argv[1] source = os.open(path, os.O_CREAT | os.O_RDWR, 0o600) target = 200000 ready = threading.Barrier(5) ready = threading.Barrier(7) libc = ctypes.CDLL(None, use_errno=True) libc.fcntl.restype = ctypes.c_int def control(command, argument=0): # Call the exported fcntl entry point: some Python builds use fcntl64, # which is not a libc-interceptor entry point. result = libc.fcntl(target, command, argument) if result < 0: error = ctypes.get_errno() raise OSError(error, os.strerror(error)) return result def worker(operation): ready.wait(timeout=20) Loading @@ -62,12 +83,14 @@ def worker(operation): assert error.errno == errno.EBADF, error operations = ( lambda: fcntl.fcntl(target, fcntl.F_GETFL), lambda: fcntl.fcntl(target, fcntl.F_SETFD, fcntl.FD_CLOEXEC), lambda: control(fcntl.F_GETFL), lambda: control(fcntl.F_GETFD), lambda: control(fcntl.F_SETFL, os.O_RDWR), lambda: control(fcntl.F_SETFD, fcntl.FD_CLOEXEC), lambda: os.ftruncate(target, 0), lambda: os.utime(target, ns=(1000000000, 1000000000)), ) with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool: with concurrent.futures.ThreadPoolExecutor(max_workers=6) as pool: futures = [pool.submit(worker, operation) for operation in operations] ready.wait(timeout=20) for _ in range(1000): Loading Loading
src/client/gkfs_libc.cpp +95 −103 Changes for src/client/gkfs_libc.cpp: 95 added lines, 103 removed lines. Original line number Diff line number Diff line Loading @@ -1099,15 +1099,11 @@ statfs(const char* path, struct statfs* buf) { int fstatfs(int fd, struct statfs* buf) { gkfs_init_routine_placeholder(); if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { auto file_info = CTX->file_map()->get(fd); if(file_info) { if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] fstatfs(fd={})", fd); return gkfs::syscall::gkfs_statfs(buf); } else { errno = EBADF; return -1; } } return dlsym_fstatfs(fd, buf); } Loading Loading @@ -1157,36 +1153,25 @@ int fstatfs64(int fd, struct statfs64* buf) { gkfs_init_routine_placeholder(); struct statfs stfs; if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { auto file_info = CTX->file_map()->get(fd); if(file_info) { if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] fstatfs64(fd={})", fd); if(gkfs::syscall::gkfs_statfs(&stfs) == 0) { memset(buf, 0, sizeof(struct statfs64)); buf->f_bsize = static_cast<decltype(buf->f_bsize)>(stfs.f_bsize); buf->f_frsize = static_cast<decltype(buf->f_frsize)>(stfs.f_frsize); buf->f_blocks = static_cast<decltype(buf->f_blocks)>(stfs.f_blocks); buf->f_bfree = static_cast<decltype(buf->f_bfree)>(stfs.f_bfree); buf->f_bavail = static_cast<decltype(buf->f_bavail)>(stfs.f_bavail); buf->f_files = static_cast<decltype(buf->f_files)>(stfs.f_files); buf->f_ffree = static_cast<decltype(buf->f_ffree)>(stfs.f_ffree); buf->f_bsize = static_cast<decltype(buf->f_bsize)>(stfs.f_bsize); buf->f_frsize = static_cast<decltype(buf->f_frsize)>(stfs.f_frsize); buf->f_blocks = static_cast<decltype(buf->f_blocks)>(stfs.f_blocks); buf->f_bfree = static_cast<decltype(buf->f_bfree)>(stfs.f_bfree); buf->f_bavail = static_cast<decltype(buf->f_bavail)>(stfs.f_bavail); buf->f_files = static_cast<decltype(buf->f_files)>(stfs.f_files); buf->f_ffree = static_cast<decltype(buf->f_ffree)>(stfs.f_ffree); buf->f_fsid = static_cast<decltype(buf->f_fsid)>(stfs.f_fsid); buf->f_namelen = static_cast<decltype(buf->f_namelen)>(stfs.f_namelen); return 0; } return -1; } else { errno = EBADF; return -1; } } return dlsym_fstatfs64(fd, buf); } Loading Loading @@ -1374,16 +1359,12 @@ truncate64(const char* path, off64_t length) { int ftruncate(int fd, off_t length) { gkfs_init_routine_placeholder(); if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { auto file_info = CTX->file_map()->get(fd); if(file_info) { DEBUG_INFO("[GKFS] ftruncate(path='{}', length={})", file_info->path(), length); if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] ftruncate(path='{}', length={})", file_info->path(), length); return gkfs::syscall::gkfs_truncate(file_info->path(), length); } else { errno = EBADF; // Should not happen if is_gkfs_fd is true return -1; } } GKFS_FALLBACK(ftruncate, fd, length); } Loading Loading @@ -1496,15 +1477,11 @@ lstat64(const char* path, struct stat64* buf) { int fstat(int fd, struct stat* buf) { gkfs_init_routine_placeholder(); if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { auto file_info = CTX->file_map()->get(fd); if(file_info) { if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] fstat(path='{}')", file_info->path()); return gkfs::syscall::gkfs_stat(file_info->path(), buf, true, true); } else { errno = EBADF; return -1; } } GKFS_FALLBACK(fstat, fd, buf); } Loading @@ -1513,20 +1490,15 @@ int fstat64(int fd, struct stat64* buf) { gkfs_init_routine_placeholder(); struct stat st; // Intermediate for GekkoFS if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { auto file_info = CTX->file_map()->get(fd); if(file_info) { if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] fstat64(path='{}')", file_info->path()); if(gkfs::syscall::gkfs_stat(file_info->path(), &st, true, true) == 0) { if(gkfs::syscall::gkfs_stat(file_info->path(), &st, true, true) == 0) { convert(&st, buf); return 0; } return -1; // errno set by gkfs_stat } else { errno = EBADF; return -1; } } GKFS_FALLBACK(fstat64, fd, buf); } Loading Loading @@ -1690,15 +1662,11 @@ __lxstat64(int ver, const char* path, struct stat64* buf) { int __fxstat(int ver, int fd, struct stat* buf) { gkfs_init_routine_placeholder(); if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { auto file_info = CTX->file_map()->get(fd); if(file_info) { if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] __fxstat(path='{}')", file_info->path()); return gkfs::syscall::gkfs_stat(file_info->path(), buf, true, true); } else { errno = EBADF; return -1; } } GKFS_FALLBACK(__fxstat, ver, fd, buf); } Loading Loading @@ -1729,20 +1697,15 @@ int __fxstat64(int ver, int fd, struct stat64* buf) { gkfs_init_routine_placeholder(); struct stat st; if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { auto file_info = CTX->file_map()->get(fd); if(file_info) { if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] __fxstat64(path='{}')", file_info->path()); if(gkfs::syscall::gkfs_stat(file_info->path(), &st, true, true) == 0) { if(gkfs::syscall::gkfs_stat(file_info->path(), &st, true, true) == 0) { convert(&st, buf); return 0; } return -1; } else { errno = EBADF; return -1; } } GKFS_FALLBACK(fxstat64, ver, fd, buf); } Loading Loading @@ -1860,16 +1823,12 @@ opendir64(const char* dirname) { DIR* fdopendir(int fd) { gkfs_init_routine_placeholder(); if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { auto file_info = CTX->file_map()->get(fd); if(file_info) { if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] fdopendir(path='{}')", file_info->path()); // Ensure this FD actually refers to a directory in GekkoFS return gekko_opendir(file_info->path()); } else { errno = EBADF; return nullptr; } } GKFS_FALLBACK(fdopendir, fd); } Loading @@ -1878,10 +1837,16 @@ struct dirent* readdir(DIR* dirp) { gkfs_init_routine_placeholder(); if(GKFS_INTERCEPTION_ENABLED() && CTX->file_map()->exist(reinterpret_cast<struct GkfsDir*>(dirp)->fd)) { int fd = reinterpret_cast<struct GkfsDir*>(dirp)->fd; auto open_dir = CTX->file_map()->get_dir(fd); if(auto file = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get( reinterpret_cast<struct GkfsDir*>(dirp)->fd) : nullptr; file) { auto open_dir = file->type() == gkfs::filemap::FileType::directory ? std::static_pointer_cast<gkfs::filemap::OpenDir>(file) : nullptr; if(open_dir == nullptr) { errno = EBADF; // Not a directory or other error return nullptr; Loading Loading @@ -1923,10 +1888,16 @@ struct dirent64* readdir64(DIR* dirp) { gkfs_init_routine_placeholder(); // Similar logic and concerns as readdir if(GKFS_INTERCEPTION_ENABLED() && CTX->file_map()->exist(reinterpret_cast<struct GkfsDir*>(dirp)->fd)) { int fd = reinterpret_cast<struct GkfsDir*>(dirp)->fd; auto open_dir = CTX->file_map()->get_dir(fd); if(auto file = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get( reinterpret_cast<struct GkfsDir*>(dirp)->fd) : nullptr; file) { auto open_dir = file->type() == gkfs::filemap::FileType::directory ? std::static_pointer_cast<gkfs::filemap::OpenDir>(file) : nullptr; if(open_dir == nullptr) { errno = EBADF; return nullptr; Loading Loading @@ -1975,8 +1946,12 @@ int closedir(DIR* dirp) { gkfs_init_routine_placeholder(); // Similar check for GekkoFS DIR* as in readdir if(GKFS_INTERCEPTION_ENABLED() && CTX->file_map()->exist(reinterpret_cast<struct GkfsDir*>(dirp)->fd)) { if(auto file = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get( reinterpret_cast<struct GkfsDir*>(dirp)->fd) : nullptr; file) { int fd = reinterpret_cast<struct GkfsDir*>(dirp)->fd; DEBUG_INFO("[GKFS] closedir(fd={})", fd); int ret = gkfs::syscall::gkfs_close(fd); // Close GekkoFS internal FD Loading @@ -1996,10 +1971,17 @@ closedir(DIR* dirp) { void seekdir(DIR* dirp, long loc) throw() { gkfs_init_routine_placeholder(); if(GKFS_INTERCEPTION_ENABLED() && CTX->file_map()->exist(reinterpret_cast<struct GkfsDir*>(dirp)->fd)) { if(auto file = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get( reinterpret_cast<struct GkfsDir*>(dirp)->fd) : nullptr; file) { int fd = reinterpret_cast<struct GkfsDir*>(dirp)->fd; auto open_dir = CTX->file_map()->get_dir(fd); auto open_dir = file->type() == gkfs::filemap::FileType::directory ? std::static_pointer_cast<gkfs::filemap::OpenDir>(file) : nullptr; if(open_dir == nullptr) { errno = EBADF; return; Loading @@ -2022,10 +2004,16 @@ seekdir(DIR* dirp, long loc) throw() { long telldir(DIR* dirp) throw() { gkfs_init_routine_placeholder(); if(GKFS_INTERCEPTION_ENABLED() && CTX->file_map()->exist(reinterpret_cast<struct GkfsDir*>(dirp)->fd)) { int fd = reinterpret_cast<struct GkfsDir*>(dirp)->fd; auto open_dir = CTX->file_map()->get_dir(fd); if(auto file = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get( reinterpret_cast<struct GkfsDir*>(dirp)->fd) : nullptr; file) { auto open_dir = file->type() == gkfs::filemap::FileType::directory ? std::static_pointer_cast<gkfs::filemap::OpenDir>(file) : nullptr; if(open_dir == nullptr) { errno = EBADF; return -1; Loading @@ -2039,12 +2027,19 @@ void rewinddir(DIR* dirstream) throw() { gkfs_init_routine_placeholder(); // rewinddir(dirp) is equivalent to seekdir(dirp, 0L). if(GKFS_INTERCEPTION_ENABLED() && CTX->file_map()->exist( reinterpret_cast<struct GkfsDir*>(dirstream)->fd)) { if(auto file = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get( reinterpret_cast<struct GkfsDir*>(dirstream) ->fd) : nullptr; file) { int fd = reinterpret_cast<struct GkfsDir*>(dirstream)->fd; DEBUG_INFO("[GKFS] rewinddir(fd={})", fd); auto open_dir = CTX->file_map()->get_dir(fd); auto open_dir = file->type() == gkfs::filemap::FileType::directory ? std::static_pointer_cast<gkfs::filemap::OpenDir>(file) : nullptr; if(open_dir == nullptr) { errno = EBADF; // Or just do nothing as per some rewinddir specs on // error Loading Loading @@ -2851,13 +2846,10 @@ fcntl(int fd, int cmd, ...) { } } if(GKFS_INTERCEPTION_ENABLED() && is_gkfs_fd(fd)) { if(auto file_info = GKFS_INTERCEPTION_ENABLED() ? CTX->file_map()->get(fd) : nullptr; file_info) { DEBUG_INFO("[GKFS] fcntl(fd={}, cmd={})", fd, cmd); auto file_info = CTX->file_map()->get(fd); if(!file_info) { errno = EBADF; return -1; } // Should not happen switch(cmd) { case F_DUPFD: Loading
src/client/hooks.cpp +26 −23 Changes for src/client/hooks.cpp: 26 added lines, 23 removed lines. Original line number Diff line number Diff line Loading @@ -401,7 +401,7 @@ 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); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_pread(fd, buf, count, pos)); } /* Since kernel 2.6: pread() became pread64(), and pwrite() became Loading @@ -415,7 +415,7 @@ hook_readv(unsigned long fd, const struct iovec* iov, unsigned long iovcnt) { LOG(DEBUG, "{}() called with fd: {}, iov: {}, iovcnt: {}", __func__, fd, fmt::ptr(iov), iovcnt); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_readv(fd, iov, iovcnt)); } return syscall_no_intercept_wrapper(SYS_readv, fd, iov, iovcnt); Loading @@ -431,7 +431,7 @@ hook_preadv(unsigned long fd, const struct iovec* iov, unsigned long iovcnt, // "pos_h: {}", // __func__, fd, fmt::ptr(iov), iovcnt, pos_l, pos_h); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_preadv(fd, iov, iovcnt, pos_l)); } return syscall_no_intercept_wrapper(SYS_preadv, fd, iov, iovcnt, pos_l); Loading Loading @@ -460,7 +460,7 @@ 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); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_pwrite(fd, buf, count, pos)); } /* Since kernel 2.6: pread() became pread64(), and pwrite() became Loading @@ -474,7 +474,7 @@ 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); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_writev(fd, iov, iovcnt)); } return syscall_no_intercept_wrapper(SYS_writev, fd, iov, iovcnt); Loading @@ -490,7 +490,7 @@ hook_pwritev(unsigned long fd, const struct iovec* iov, unsigned long iovcnt, "pos_h: {}", __func__, fd, fmt::ptr(iov), iovcnt, pos_l, pos_h); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_pwritev(fd, iov, iovcnt, pos_l)); } return syscall_no_intercept_wrapper(SYS_pwritev, fd, iov, iovcnt, pos_l); Loading Loading @@ -676,7 +676,7 @@ hook_flock(unsigned long fd, int flags) { LOG(WARNING, "{}() flock is not supported for GekkoFS fd '{}' flags '{}'", __func__, fd, flags); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { LOG(WARNING, "{}() flock on GekkoFS fd '{}' ignored; returning success", __func__, fd); return 0; Loading Loading @@ -774,9 +774,9 @@ hook_lseek(unsigned int fd, off_t offset, unsigned int whence) { LOG(DEBUG, "{}() called with fd: {}, offset: {}, whence: {}", __func__, fd, offset, whence); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { auto off_ret = gkfs::syscall::gkfs_lseek( fd, static_cast<off64_t>(offset), whence); file, static_cast<off64_t>(offset), whence); if(off_ret > std::numeric_limits<off_t>::max()) { return -EOVERFLOW; } else if(off_ret < 0) { Loading Loading @@ -821,7 +821,7 @@ hook_dup(unsigned int fd) { LOG(DEBUG, "{}() called with oldfd: {}", __func__, fd); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_dup(fd)); } return gsl::narrow_cast<int>(syscall_no_intercept_wrapper(SYS_dup, fd)); Loading @@ -832,7 +832,7 @@ hook_dup2(unsigned int oldfd, unsigned int newfd) { LOG(DEBUG, "{}() called with oldfd: {}, newfd: {}", __func__, oldfd, newfd); if(CTX->file_map()->exist(oldfd)) { if(auto file = CTX->file_map()->get(oldfd)) { return with_errno(gkfs::syscall::gkfs_dup2(oldfd, newfd)); } return gsl::narrow_cast<int>( Loading @@ -845,7 +845,7 @@ 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)) { if(auto file = CTX->file_map()->get(oldfd)) { if(flags & O_CLOEXEC) { LOG(WARNING, "{}() Not supported", __func__); return -ENOTSUP; Loading @@ -865,7 +865,7 @@ hook_getdents(unsigned int fd, struct linux_dirent* dirp, unsigned int count) { return -EFAULT; } if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_getdents(fd, dirp, count)); } return syscall_no_intercept_wrapper(SYS_getdents, fd, dirp, count); Loading @@ -882,7 +882,7 @@ hook_getdents64(unsigned int fd, struct linux_dirent64* dirp, return -EFAULT; } if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { LOG(WARNING, "DEBUG: hook_getdents64 fd {} exists in filemap", fd); return with_errno(gkfs::syscall::gkfs_getdents64(fd, dirp, count)); } Loading Loading @@ -945,7 +945,7 @@ int hook_fchmod(unsigned int fd, mode_t mode) { LOG(DEBUG, "{}() called with fd: {}, mode: {}", __func__, fd, mode); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { LOG(WARNING, "{}() chmod on GekkoFS fd '{}' ignored; returning success", __func__, fd); return 0; Loading Loading @@ -1022,7 +1022,7 @@ hook_fchown(unsigned int fd, uid_t owner, gid_t group) { LOG(DEBUG, "{}() called with fd: {}, owner: {}, group: {}", __func__, fd, owner, group); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { LOG(WARNING, "{}() chown on GekkoFS fd '{}' ignored; returning success", __func__, fd); return 0; Loading Loading @@ -1104,8 +1104,11 @@ hook_fchdir(unsigned int fd) { LOG(DEBUG, "{}() called with fd: {}", __func__, fd); if(CTX->file_map()->exist(fd)) { auto open_dir = CTX->file_map()->get_dir(fd); if(auto file = CTX->file_map()->get(fd)) { auto open_dir = file->type() == gkfs::filemap::FileType::directory ? std::static_pointer_cast<gkfs::filemap::OpenDir>(file) : nullptr; if(open_dir == nullptr) { // Cast did not succeeded: open_file is a regular file LOG(ERROR, "{}() file descriptor refers to a normal file", Loading Loading @@ -1430,7 +1433,7 @@ hook_fstatfs(unsigned int fd, struct statfs* buf) { return -EFAULT; } if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_statfs(buf)); } return gsl::narrow_cast<int>( Loading @@ -1443,7 +1446,7 @@ int hook_fsync(unsigned int fd) { LOG(DEBUG, "{}() called with fd: {}", __func__, fd); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return with_errno(gkfs::syscall::gkfs_fsync(fd)); } Loading Loading @@ -1516,7 +1519,7 @@ hook_flistxattr(int fd, char* list, size_t size) { LOG(DEBUG, "{}() called with fd '{}' list {} size '{}'", __func__, fd, fmt::ptr(list), size); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return -ENOTSUP; } return syscall_no_intercept_wrapper(SYS_flistxattr, fd, list, size); Loading @@ -1527,7 +1530,7 @@ hook_fallocate(int fd, int mode, off_t offset, off_t len) { LOG(DEBUG, "{}() called with fd '{}' mode '{}' offset '{}' len '{}'", __func__, fd, mode, offset, len); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return -ENOTSUP; } return syscall_no_intercept_wrapper(SYS_fallocate, fd, mode, offset, len); Loading @@ -1538,7 +1541,7 @@ hook_fadvise64(int fd, off_t offset, off_t len, int advice) { LOG(DEBUG, "{}() called with fd '{}' offset '{}' len '{}' advice '{}'", __func__, fd, offset, len, advice); if(CTX->file_map()->exist(fd)) { if(auto file = CTX->file_map()->get(fd)) { return -ENOTSUP; } return syscall_no_intercept_wrapper(SYS_fadvise64, fd, offset, len, advice); Loading
tests/integration/syscalls/test_fd_close_races.py +32 −9 Changes for tests/integration/syscalls/test_fd_close_races.py: 32 added lines, 9 removed lines. Original line number Diff line number Diff line Loading @@ -3,20 +3,29 @@ import sys import pytest def test_dup_keeps_last_descriptor_visible(gkfs_shell, gkfs_daemon): @pytest.mark.parametrize("shell_fixture", ["gkfs_shell", "gkfs_shellLibc"]) def test_dup_keeps_last_descriptor_visible(shell_fixture, request, gkfs_daemon): """Duplicated descriptors must keep the map nonempty after source close.""" shell = gkfs_shell shell = request.getfixturevalue(shell_fixture) command = shell.run("env", "LIBGKFS_LOG=none", sys.executable, "-c", """ import ctypes import fcntl import os import sys path = sys.argv[1] for duplicate in (lambda fd: os.dup(fd), lambda fd: os.dup2(fd, 200000)): libc = ctypes.CDLL(None, use_errno=True) libc.dup.argtypes = [ctypes.c_int] libc.dup.restype = ctypes.c_int libc.dup2.argtypes = [ctypes.c_int, ctypes.c_int] libc.dup2.restype = ctypes.c_int libc.fcntl.restype = ctypes.c_int for duplicate in (libc.dup, lambda fd: libc.dup2(fd, 200000)): fd = os.open(path, os.O_CREAT | os.O_RDWR, 0o600) copy = duplicate(fd) assert copy >= 0, ctypes.get_errno() os.close(fd) assert fcntl.fcntl(copy, fcntl.F_GETFL) & os.O_RDWR assert libc.fcntl(copy, fcntl.F_GETFL) & os.O_RDWR os.ftruncate(copy, 0) assert os.fstat(copy).st_size == 0 os.close(copy) Loading @@ -24,7 +33,7 @@ for duplicate in (lambda fd: os.dup(fd), lambda fd: os.dup2(fd, 200000)): # Replacing an existing target must not lose or double-count its map entry. fd = os.open(path, os.O_RDWR) target = os.open(path, os.O_RDWR) assert os.dup2(fd, target) == target assert libc.dup2(fd, target) == target os.close(fd) assert os.fstat(target).st_size == 0 os.close(target) Loading @@ -41,6 +50,7 @@ def test_fd_hooks_racing_close(shell_fixture, request, gkfs_daemon): shell = request.getfixturevalue(shell_fixture) command = shell.run("env", "LIBGKFS_LOG=none", sys.executable, "-c", """ import concurrent.futures import ctypes import errno import fcntl import os Loading @@ -50,7 +60,18 @@ import threading path = sys.argv[1] source = os.open(path, os.O_CREAT | os.O_RDWR, 0o600) target = 200000 ready = threading.Barrier(5) ready = threading.Barrier(7) libc = ctypes.CDLL(None, use_errno=True) libc.fcntl.restype = ctypes.c_int def control(command, argument=0): # Call the exported fcntl entry point: some Python builds use fcntl64, # which is not a libc-interceptor entry point. result = libc.fcntl(target, command, argument) if result < 0: error = ctypes.get_errno() raise OSError(error, os.strerror(error)) return result def worker(operation): ready.wait(timeout=20) Loading @@ -62,12 +83,14 @@ def worker(operation): assert error.errno == errno.EBADF, error operations = ( lambda: fcntl.fcntl(target, fcntl.F_GETFL), lambda: fcntl.fcntl(target, fcntl.F_SETFD, fcntl.FD_CLOEXEC), lambda: control(fcntl.F_GETFL), lambda: control(fcntl.F_GETFD), lambda: control(fcntl.F_SETFL, os.O_RDWR), lambda: control(fcntl.F_SETFD, fcntl.FD_CLOEXEC), lambda: os.ftruncate(target, 0), lambda: os.utime(target, ns=(1000000000, 1000000000)), ) with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool: with concurrent.futures.ThreadPoolExecutor(max_workers=6) as pool: futures = [pool.submit(worker, operation) for operation in operations] ready.wait(timeout=20) for _ in range(1000): Loading