diff --git a/libc/calls/execve-sysv.c b/libc/calls/execve-sysv.c index 9a3b7d207..f7b5a6abb 100644 --- a/libc/calls/execve-sysv.c +++ b/libc/calls/execve-sysv.c @@ -75,28 +75,21 @@ static const char *Join(const char *a, const char *b, char buf[PATH_MAX]) { int sys_execve(const char *prog, char *const argv[], char *const envp[]) { size_t i; - int e, rc, fd, flags; - char *buf, *newprog; + int e, rc; + char *buf; char **shargs; const char *ape; struct ZiposUri uri; - if (_weaken(__zipos_parseuri)(prog, &uri) != -1) { - if (IsLinux()) { - newprog = alloca(PATH_MAX); - fd = _weaken(__zipos_uri_to_mem_file)(&uri, newprog); - prog = newprog; - } else if (IsFreebsd()) { - if ((fd = _weaken(__zipos_uri_to_mem_file)(&uri, NULL)) != -1) { - return sys_fexecve(fd, argv, envp); - } - } else { - return enosys(); + if (_weaken(__zipos_parseuri) && (_weaken(__zipos_parseuri)(prog, &uri) != -1)) { + rc = _weaken(__zipos_open)(&uri, O_RDONLY, 0); + if (rc != -1) { + const int zipFD = rc; + strace_enabled(-1); + rc = fexecve(zipFD, argv, envp); + close(zipFD); + strace_enabled(+1); } - if (fd == -1) { - return -1; - } - } else { - fd = -1; + return rc; } e = errno; __sys_execve(prog, argv, envp); diff --git a/libc/calls/fexecve.c b/libc/calls/fexecve.c index 5943b4079..a8ad3efad 100644 --- a/libc/calls/fexecve.c +++ b/libc/calls/fexecve.c @@ -22,6 +22,7 @@ #include "libc/calls/calls.h" #include "libc/calls/cp.internal.h" #include "libc/calls/execve-sysv.internal.h" +#include "libc/calls/internal.h" #include "libc/calls/struct/stat.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" @@ -105,13 +106,18 @@ static bool ape_to_elf(void *ape, const size_t apesize) { return false; } -static int ape_fd_to_mem_elf_fd(const int infd, char *path) { +/** + * Creates a memfd and copies fd to it. + * + * This does an inplace conversion of APE to ELF when detected!!!! + */ +static int fd_to_mem_fd(const int infd, char *path) { if (!IsLinux() && !IsFreebsd() || !_weaken(mmap) || !_weaken(munmap)) { return enosys(); } struct stat st; - if (sys_fstat(infd, &st) == -1) { + if (fstat(infd, &st) == -1) { return -1; } int fd; @@ -133,7 +139,7 @@ static int ape_fd_to_mem_elf_fd(const int infd, char *path) { ((space = _weaken(mmap)(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) != MAP_FAILED)) { ssize_t readRc; - readRc = sys_pread(infd, space, st.st_size, 0, 0); + readRc = pread(infd, space, st.st_size, 0); bool success = readRc != -1; if (success && (st.st_size > 8) && IsAPEMagic(space)) { success = ape_to_elf(space, st.st_size); @@ -156,7 +162,7 @@ static int ape_fd_to_mem_elf_fd(const int infd, char *path) { * Executes binary executable at file descriptor. * * This is only supported on Linux and FreeBSD. APE binaries are - * supported. + * supported. Zipos is supported. * * @param fd is opened executable and current file position is ignored * @return doesn't return on success, otherwise -1 w/ errno @@ -172,14 +178,35 @@ int fexecve(int fd, char *const argv[], char *const envp[]) { } else { STRACE("fexecve(%d, %s, %s) → ...", fd, DescribeStringList(argv), DescribeStringList(envp)); - rc = fexecve_impl(fd, argv, envp); - if ((errno == ENOEXEC) && (IsLinux() || IsFreebsd())) { + do { + int fexecvefd = fd; + if (__isfdkind(fd, kFdZip)) { + if(!IsLinux() && !IsFreebsd()) { + rc = enosys(); + break; + } + BEGIN_CANCELLATION_POINT; + BLOCK_SIGNALS; + strace_enabled(-1); + rc = fd_to_mem_fd(fd, NULL); + strace_enabled(+1); + ALLOW_SIGNALS; + END_CANCELLATION_POINT; + if(rc == -1) { + break; + } + fexecvefd = rc; + } + rc = fexecve_impl(fexecvefd, argv, envp); + if ((errno != ENOEXEC) || (!IsLinux() && !IsFreebsd())) { + break; + } int newfd = -1; BEGIN_CANCELLATION_POINT; BLOCK_SIGNALS; strace_enabled(-1); - if (IsAPEFd(fd)) { - newfd = ape_fd_to_mem_elf_fd(fd, NULL); + if (IsAPEFd(fexecvefd)) { + newfd = fd_to_mem_fd(fexecvefd, NULL); } strace_enabled(+1); ALLOW_SIGNALS; @@ -187,7 +214,7 @@ int fexecve(int fd, char *const argv[], char *const envp[]) { if (newfd != -1) { rc = fexecve_impl(newfd, argv, envp); } - } + } while (0); } STRACE("fexecve(%d) failed %d% m", fd, rc); return rc; diff --git a/test/libc/calls/fexecve_test.c b/test/libc/calls/fexecve_test.c index 544cc5c88..64a99f123 100644 --- a/test/libc/calls/fexecve_test.c +++ b/test/libc/calls/fexecve_test.c @@ -107,4 +107,16 @@ TEST(fexecve, zipos) { if (fd == -1 && errno == ENOSYS) _Exit(42); fexecve(fd, (char *const[]){0}, (char *const[]){0}); EXITS(42); + close(fd); +} + +TEST(fexecve, ziposAPE) { + if (!IsLinux() && !IsFreebsd()) return; + int fd = open("/zip/life-nomod.com", O_RDONLY); + SPAWN(fork); + ASSERT_NE(-1, fd); + if (fd == -1 && errno == ENOSYS) _Exit(42); + fexecve(fd, (char *const[]){0}, (char *const[]){0}); + EXITS(42); + close(fd); }