diff --git a/libc/calls/close-nt.c b/libc/calls/close-nt.c index 74a6dcdbe..fe9184cff 100644 --- a/libc/calls/close-nt.c +++ b/libc/calls/close-nt.c @@ -24,6 +24,7 @@ #include "libc/nt/enum/filetype.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" +#include "libc/runtime/zipos.internal.h" #include "libc/sock/syscall_fd.internal.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" @@ -32,6 +33,8 @@ textwindows int sys_close_nt(int fd, int fildes) { if (fd + 0u >= g_fds.n) return ebadf(); struct Fd *f = g_fds.p + fd; switch (f->kind) { + case kFdZip: + return _weaken(__zipos_close)(fd); case kFdEmpty: return ebadf(); case kFdFile: diff --git a/libc/calls/dup-nt.c b/libc/calls/dup-nt.c index 4d54add88..aa3dd1581 100644 --- a/libc/calls/dup-nt.c +++ b/libc/calls/dup-nt.c @@ -59,30 +59,34 @@ static textwindows int sys_dup_nt_impl(int oldfd, int newfd, int flags, return -1; } if (g_fds.p[newfd].kind) { - if (g_fds.p[newfd].kind == kFdZip) { - _weaken(__zipos_close)(newfd); - } else { - sys_close_nt(newfd, newfd); - } + sys_close_nt(newfd, newfd); } } - if (DuplicateHandle(GetCurrentProcess(), g_fds.p[oldfd].handle, - GetCurrentProcess(), &handle, 0, true, - kNtDuplicateSameAccess)) { - g_fds.p[newfd] = g_fds.p[oldfd]; - g_fds.p[newfd].handle = handle; - if (flags & _O_CLOEXEC) { - g_fds.p[newfd].flags |= _O_CLOEXEC; - } else { - g_fds.p[newfd].flags &= ~_O_CLOEXEC; - } + if (__isfdkind(oldfd, kFdZip)) { + handle = (intptr_t)_weaken(__zipos_keep)( + (struct ZiposHandle *)(intptr_t)g_fds.p[oldfd].handle); rc = newfd; } else { - __releasefd(newfd); - rc = __winerr(); + if (DuplicateHandle(GetCurrentProcess(), g_fds.p[oldfd].handle, + GetCurrentProcess(), &handle, 0, true, + kNtDuplicateSameAccess)) { + rc = newfd; + } else { + rc = __winerr(); + __releasefd(newfd); + __fds_unlock(); + return rc; + } } + g_fds.p[newfd] = g_fds.p[oldfd]; + g_fds.p[newfd].handle = handle; + if (flags & _O_CLOEXEC) { + g_fds.p[newfd].flags |= _O_CLOEXEC; + } else { + g_fds.p[newfd].flags &= ~_O_CLOEXEC; + } __fds_unlock(); return rc; } diff --git a/libc/calls/dup.c b/libc/calls/dup.c index 7ef655a48..15dcf0adb 100644 --- a/libc/calls/dup.c +++ b/libc/calls/dup.c @@ -22,7 +22,9 @@ #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/strace.internal.h" +#include "libc/intrin/weaken.h" #include "libc/sysv/errfuns.h" +#include "libc/runtime/zipos.internal.h" /** * Duplicates file descriptor. @@ -51,10 +53,11 @@ */ int dup(int fd) { int rc; - if (__isfdkind(fd, kFdZip)) { - rc = enotsup(); - } else if (!IsWindows()) { + if (!IsWindows()) { rc = sys_dup(fd); + if (rc != -1 && __isfdkind(fd, kFdZip)) { + _weaken(__zipos_postdup)(fd, rc); + } } else { rc = sys_dup_nt(fd, -1, 0, -1); } diff --git a/libc/calls/dup2.c b/libc/calls/dup2.c index dbb5064a2..828d32bb7 100644 --- a/libc/calls/dup2.c +++ b/libc/calls/dup2.c @@ -67,20 +67,24 @@ int dup2(int oldfd, int newfd) { int rc; // helps guarantee stderr log gets duplicated before user closes if (_weaken(kloghandle)) _weaken(kloghandle)(); - if (__isfdkind(oldfd, kFdZip)) { - rc = enotsup(); #ifdef __aarch64__ - } else if (oldfd == newfd) { + if (oldfd == newfd) { // linux aarch64 defines dup3() but not dup2(), which wasn't such a // great decision, since the two syscalls don't behave the same way if (!(rc = read(oldfd, 0, 0))) rc = oldfd; + } else #endif - } else if (!IsWindows()) { - rc = sys_dup2(oldfd, newfd, 0); - if (rc != -1 && oldfd != newfd && __isfdkind(newfd, kFdZip) && !__vforked) { - _weaken(__zipos_free)( - (struct ZiposHandle *)(intptr_t)g_fds.p[newfd].handle); - bzero(g_fds.p + newfd, sizeof(*g_fds.p)); + if (!IsWindows()) { + if (__isfdkind(oldfd, kFdZip) || __isfdkind(newfd, kFdZip)) { + if (__vforked) { + return enotsup(); + } + rc = sys_dup2(oldfd, newfd, 0); + if (rc != -1) { + _weaken(__zipos_postdup)(oldfd, newfd); + } + } else { + rc = sys_dup2(oldfd, newfd, 0); } } else if (newfd < 0) { rc = ebadf(); diff --git a/libc/calls/dup3.c b/libc/calls/dup3.c index 42a0573c9..2352f6970 100644 --- a/libc/calls/dup3.c +++ b/libc/calls/dup3.c @@ -70,14 +70,17 @@ int dup3(int oldfd, int newfd, int flags) { rc = einval(); // NetBSD doesn't do this } else if (oldfd < 0 || newfd < 0) { rc = ebadf(); - } else if (__isfdkind(oldfd, kFdZip)) { - rc = enotsup(); } else if (!IsWindows()) { - rc = sys_dup3(oldfd, newfd, flags); - if (rc != -1 && __isfdkind(newfd, kFdZip) && !__vforked) { - _weaken(__zipos_free)( - (struct ZiposHandle *)(intptr_t)g_fds.p[newfd].handle); - bzero(g_fds.p + newfd, sizeof(*g_fds.p)); + if (__isfdkind(oldfd, kFdZip) || __isfdkind(newfd, kFdZip)) { + if (__vforked) { + return enotsup(); + } + rc = sys_dup3(oldfd, newfd, flags); + if (rc != -1) { + _weaken(__zipos_postdup)(oldfd, newfd); + } + } else { + rc = sys_dup3(oldfd, newfd, flags); } } else { rc = sys_dup_nt(oldfd, newfd, flags, -1); diff --git a/libc/calls/fcntl.c b/libc/calls/fcntl.c index b53ce4014..d925a46ad 100644 --- a/libc/calls/fcntl.c +++ b/libc/calls/fcntl.c @@ -28,6 +28,7 @@ #include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" #include "libc/runtime/zipos.internal.h" +#include "libc/str/str.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/errfuns.h" @@ -125,6 +126,10 @@ int fcntl(int fd, int cmd, ...) { END_CANCELATION_POINT; } else { rc = sys_fcntl(fd, cmd, arg, __sys_fcntl); + if (rc != -1 && (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC) && + __isfdkind(rc, kFdZip)) { + _weaken(__zipos_postdup)(fd, rc); + } } } else { rc = sys_fcntl_nt(fd, cmd, arg); diff --git a/libc/runtime/zipos-fcntl.c b/libc/runtime/zipos-fcntl.c index 7424dde12..c9e66d6f6 100644 --- a/libc/runtime/zipos-fcntl.c +++ b/libc/runtime/zipos-fcntl.c @@ -16,13 +16,30 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/createfileflags.internal.h" #include "libc/calls/internal.h" +#include "libc/calls/syscall-nt.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/runtime/zipos.internal.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/fd.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" +static int __zipos_dupfd(int fd, int cmd, int start) { + int rc; + if (start < 0) return einval(); + if (IsWindows()) { + return sys_dup_nt(fd, -1, (cmd == F_DUPFD_CLOEXEC ? _O_CLOEXEC : 0), + start); + } + rc = sys_fcntl(fd, cmd, start, __sys_fcntl); + if (rc != -1) { + __zipos_postdup(fd, rc); + } + return rc; +} + int __zipos_fcntl(int fd, int cmd, uintptr_t arg) { if (cmd == F_GETFD) { if (g_fds.p[fd].flags & O_CLOEXEC) { @@ -38,6 +55,8 @@ int __zipos_fcntl(int fd, int cmd, uintptr_t arg) { g_fds.p[fd].flags &= ~O_CLOEXEC; return 0; } + } else if (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC) { + return __zipos_dupfd(fd, cmd, arg); } else { return einval(); } diff --git a/libc/runtime/zipos-open.c b/libc/runtime/zipos-open.c index fa87508c3..5253c40e1 100644 --- a/libc/runtime/zipos-open.c +++ b/libc/runtime/zipos-open.c @@ -78,7 +78,20 @@ static void *__zipos_mmap_space(size_t mapsize) { return start + offset; } +struct ZiposHandle *__zipos_keep(struct ZiposHandle *h) { + atomic_fetch_add_explicit(&h->refs, 1, memory_order_relaxed); + return h; +} + +static bool __zipos_drop(struct ZiposHandle *h) { + int refs = atomic_load_explicit(&h->refs, memory_order_acquire); + return -1 == refs || -1 == atomic_fetch_sub(&h->refs, 1); +} + void __zipos_free(struct ZiposHandle *h) { + if (!__zipos_drop(h)) { + return; + } if (IsAsan()) { __asan_poison((char *)h + sizeof(struct ZiposHandle), h->mapsize - sizeof(struct ZiposHandle), kAsanHeapFree); @@ -100,7 +113,7 @@ StartOver: while ((h = *ph)) { if (h->mapsize >= mapsize) { if (!_cmpxchg(ph, h, h->next)) goto StartOver; - h->next = 0; + atomic_init(&h->refs, 0); break; } ph = &h->next; @@ -209,6 +222,27 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, int flags, return -1; } +void __zipos_postdup(int oldfd, int newfd) { + if (oldfd == newfd) { + return; + } + if (__isfdkind(newfd, kFdZip)) { + __zipos_free((struct ZiposHandle *)(intptr_t)g_fds.p[newfd].handle); + if (!__isfdkind(oldfd, kFdZip)) { + __fds_lock(); + bzero(g_fds.p + newfd, sizeof(*g_fds.p)); + __fds_unlock(); + } + } + if (__isfdkind(oldfd, kFdZip)) { + __zipos_keep((struct ZiposHandle *)(intptr_t)g_fds.p[oldfd].handle); + __fds_lock(); + __ensurefds_unlocked(newfd); + g_fds.p[newfd] = g_fds.p[oldfd]; + __fds_unlock(); + } +} + /** * Loads compressed file from αcτµαlly pδrταblε εxεcµταblε object store. * diff --git a/libc/runtime/zipos.S b/libc/runtime/zipos.S index 9792b4bce..605665379 100644 --- a/libc/runtime/zipos.S +++ b/libc/runtime/zipos.S @@ -33,6 +33,9 @@ .yoink __zipos_stat .yoink __zipos_notat .yoink __zipos_mmap + .yoink __zipos_postdup + .yoink __zipos_keep + .yoink __zipos_free // TODO(jart): why does corruption happen when zip has no assets? .yoink .cosmo diff --git a/libc/runtime/zipos.internal.h b/libc/runtime/zipos.internal.h index a28ffc38d..810010297 100644 --- a/libc/runtime/zipos.internal.h +++ b/libc/runtime/zipos.internal.h @@ -20,8 +20,9 @@ struct ZiposHandle { struct Zipos *zipos; size_t size; size_t mapsize; - size_t pos; size_t cfile; + _Atomic(int) refs; + size_t pos; // TODO atomic uint8_t *mem; uint8_t data[]; }; @@ -38,6 +39,7 @@ struct Zipos { int __zipos_close(int); void __zipos_free(struct ZiposHandle *); +struct ZiposHandle *__zipos_keep(struct ZiposHandle *); struct Zipos *__zipos_get(void) pureconst; size_t __zipos_normpath(char *, const char *, size_t); ssize_t __zipos_find(struct Zipos *, struct ZiposUri *); @@ -45,6 +47,7 @@ ssize_t __zipos_scan(struct Zipos *, struct ZiposUri *); ssize_t __zipos_parseuri(const char *, struct ZiposUri *); uint64_t __zipos_inode(struct Zipos *, int64_t, const void *, size_t); int __zipos_open(struct ZiposUri *, int); +void __zipos_postdup(int, int); int __zipos_access(struct ZiposUri *, int); int __zipos_stat(struct ZiposUri *, struct stat *); int __zipos_fstat(struct ZiposHandle *, struct stat *); diff --git a/test/libc/calls/dup_test.c b/test/libc/calls/dup_test.c index 454ae933c..8a1a3c65a 100644 --- a/test/libc/calls/dup_test.c +++ b/test/libc/calls/dup_test.c @@ -75,7 +75,7 @@ TEST(dup, bigNumber) { ASSERT_SYS(0, 0, close(100)); } -TEST(dup2, zipos) { +TEST(dup2, ziposdest) { ASSERT_SYS(0, 3, creat("real", 0644)); ASSERT_SYS(0, 4, open("/zip/libc/testlib/hyperion.txt", O_RDONLY)); ASSERT_SYS(0, 2, write(3, "hi", 2)); @@ -86,6 +86,15 @@ TEST(dup2, zipos) { ASSERT_SYS(0, 0, close(3)); } +TEST(dup2, zipossrc) { + char b[8]; + ASSERT_SYS(0, 3, open("/zip/libc/testlib/hyperion.txt", O_RDONLY)); + ASSERT_SYS(0, 4, dup2(3, 4)); + ASSERT_SYS(0, 8, read(4, b, 8)); + ASSERT_SYS(0, 0, close(4)); + ASSERT_SYS(0, 0, close(3)); +} + #ifdef __x86_64__ TEST(dup, clearsCloexecFlag) { static bool once; diff --git a/test/libc/calls/fcntl_test.c b/test/libc/calls/fcntl_test.c index 600d0097c..8a719a0d2 100644 --- a/test/libc/calls/fcntl_test.c +++ b/test/libc/calls/fcntl_test.c @@ -32,6 +32,9 @@ #include "libc/thread/thread.h" #include "libc/x/x.h" +__static_yoink("libc/testlib/hyperion.txt"); +__static_yoink("zipos"); + int Lock(int fd, int type, long start, long len) { int e; struct flock lock = { @@ -121,6 +124,17 @@ TEST(fcntl, F_DUPFD_CLOEXEC) { ASSERT_SYS(0, 0, close(3)); } +TEST(fcntl, ziposDupFd) { + char b[8]; + ASSERT_SYS(0, 3, open("/zip/libc/testlib/hyperion.txt", O_RDONLY)); + ASSERT_SYS(0, 4, fcntl(3, F_DUPFD, 4)); + ASSERT_SYS(0, 8, read(3, b, 8)); + ASSERT_SYS(0, 0, lseek(4, 0, SEEK_SET)); + ASSERT_SYS(0, 8, read(4, b, 8)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 0, close(4)); +} + void OnSig(int sig) { }