Commit a2019225 authored by Ramon Nou's avatar Ramon Nou
Browse files

fix(client): support spawn descriptor duplication on AArch64

parent eb21ea5c
Loading
Loading
Loading
Loading
+31 −12
Changes for src/client/posix_spawn.cpp: 31 added lines, 12 removed lines.
Original line number Diff line number Diff line
@@ -63,17 +63,36 @@ extern char** environ;

namespace {

static int (*real_posix_spawn)(
        pid_t* __restrict, const char* __restrict,
// AArch64 provides dup3, but not the legacy dup2 system call.
long
spawn_dup2_no_intercept(int oldfd, int newfd) {
    if(oldfd == newfd) {
        const long flags = syscall_no_intercept(SYS_fcntl, oldfd, F_GETFD);
        if(flags < 0) {
            return flags;
        }
        // A spawn dup2 action must keep this descriptor across exec even
        // when no duplication is needed (unlike a plain dup2(fd, fd)).
        const long result = syscall_no_intercept(SYS_fcntl, oldfd, F_SETFD,
                                                 flags & ~FD_CLOEXEC);
        return result < 0 ? result : newfd;
    }
#ifdef SYS_dup2
    return syscall_no_intercept(SYS_dup2, oldfd, newfd);
#else
    return syscall_no_intercept(SYS_dup3, oldfd, newfd, 0);
#endif
}

static int (*real_posix_spawn)(pid_t* __restrict, const char* __restrict,
                               const posix_spawn_file_actions_t* __restrict,
        const posix_spawnattr_t* __restrict, char* const[],
        char* const[]) = nullptr;
                               const posix_spawnattr_t* __restrict,
                               char* const[], char* const[]) = nullptr;

static int (*real_posix_spawnp)(
        pid_t* __restrict, const char* __restrict,
static int (*real_posix_spawnp)(pid_t* __restrict, const char* __restrict,
                                const posix_spawn_file_actions_t* __restrict,
        const posix_spawnattr_t* __restrict, char* const[],
        char* const[]) = nullptr;
                                const posix_spawnattr_t* __restrict,
                                char* const[], char* const[]) = nullptr;

int
dlsym_posix_spawn(pid_t* __restrict pid, const char* __restrict path,
@@ -335,8 +354,8 @@ gkfs_posix_spawn_impl(pid_t* __restrict pid,
                        break;
                    }
                    case 1: /* spawn_do_dup2 */ {
                        long res = syscall_no_intercept(
                                SYS_dup2, act.action.dup2_action.fd,
                        long res = spawn_dup2_no_intercept(
                                act.action.dup2_action.fd,
                                act.action.dup2_action.newfd);
                        if(res < 0) {
                            fail_child(syscall_error_code(res));
@@ -354,8 +373,8 @@ gkfs_posix_spawn_impl(pid_t* __restrict pid,
                        }
                        int fd = static_cast<int>(res);
                        if(fd != act.action.open_action.fd) {
                            long dres = syscall_no_intercept(
                                    SYS_dup2, fd, act.action.open_action.fd);
                            long dres = spawn_dup2_no_intercept(
                                    fd, act.action.open_action.fd);
                            syscall_no_intercept(SYS_close, fd);
                            if(dres < 0) {
                                fail_child(syscall_error_code(dres));
+29 −0
Changes for tests/integration/rename/test_spawn_dup_actions.py: 29 added lines, 0 removed lines.
Original line number Diff line number Diff line
"""Exercise spawn descriptor actions from a virtual working directory."""

import sys


def test_spawn_dup_actions(gkfs_daemon, gkfs_shell, tmp_path):
    output = tmp_path / "spawn-output"
    code = f"""
import os
import sys
os.chdir({str(gkfs_daemon.mountdir)!r})
fd = os.open({str(output)!r}, os.O_CREAT | os.O_RDWR | os.O_CLOEXEC, 0o600)
try:
    # Force GekkoFS's custom spawn path through the virtual cwd. Reuse fd
    # in the child: POSIX requires dup2(fd, fd) to clear FD_CLOEXEC here.
    child = 'import os; os.write(' + str(fd) + ', b"same-fd")'
    pid = os.posix_spawn(sys.executable, [sys.executable, '-c', child],
                         os.environ, file_actions=[(os.POSIX_SPAWN_DUP2, fd, fd)])
    assert os.waitpid(pid, 0)[1] == 0
    pid = os.posix_spawn('/usr/bin/printf', ['printf', '-different-fd'],
                         os.environ, file_actions=[(os.POSIX_SPAWN_DUP2, fd, 1)])
    assert os.waitpid(pid, 0)[1] == 0
    os.lseek(fd, 0, os.SEEK_SET)
    assert os.read(fd, 128) == b'same-fd-different-fd'
finally:
    os.close(fd)
"""
    result = gkfs_shell.run(sys.executable, "-c", code)
    assert result.exit_code == 0, result.stderr.decode()
 No newline at end of file