Commit 3eff7d54 authored by Ramon Nou's avatar Ramon Nou
Browse files

fix(client): retain fd lookup ownership across syscall and libc hooks

parent 7569624d
Loading
Loading
Loading
Loading
Loading
+95 −103
Changes for src/client/gkfs_libc.cpp: 95 added lines, 103 removed lines.
Original line number Diff line number Diff line
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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;
@@ -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;
@@ -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
@@ -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;
@@ -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;
@@ -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
@@ -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:
+26 −23
Changes for src/client/hooks.cpp: 26 added lines, 23 removed lines.
Original line number Diff line number Diff line
@@ -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
@@ -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);
@@ -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);
@@ -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
@@ -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);
@@ -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);
@@ -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;
@@ -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) {
@@ -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));
@@ -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>(
@@ -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;
@@ -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);
@@ -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));
    }
@@ -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;
@@ -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;
@@ -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",
@@ -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>(
@@ -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));
    }

@@ -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);
@@ -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);
@@ -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);
+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
@@ -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)
@@ -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)
@@ -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
@@ -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)
@@ -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):