Implement __zipos_dup (#972)

* Implement __zipos_dup

Makes ZiposHandle reference-counted by an `rc` field in a union with its
freelist `next` pointer. The functions `__zipos_free` and `__zipos_keep`
function as incref/decref for it. Adds `__zipos_postdup` to fix metadata
on file descriptors after dup-like operations, and adds zipos support to
`sys_dup_nt` + `sys_close_nt`.

* Remove noop __zipos_postdup

rc is never a zipos file because it is always a previously unused file
descriptor. fd is never a zipos file because that case has been handled
above by __zipos_fcntl.
This commit is contained in:
Jōshin 2023-12-01 03:08:30 -05:00 committed by GitHub
parent 6556dd2673
commit d1a745c17c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 138 additions and 39 deletions

View file

@ -24,6 +24,7 @@
#include "libc/nt/enum/filetype.h" #include "libc/nt/enum/filetype.h"
#include "libc/nt/files.h" #include "libc/nt/files.h"
#include "libc/nt/runtime.h" #include "libc/nt/runtime.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sock/syscall_fd.internal.h" #include "libc/sock/syscall_fd.internal.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.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(); if (fd + 0u >= g_fds.n) return ebadf();
struct Fd *f = g_fds.p + fd; struct Fd *f = g_fds.p + fd;
switch (f->kind) { switch (f->kind) {
case kFdZip:
return _weaken(__zipos_close)(fd);
case kFdEmpty: case kFdEmpty:
return ebadf(); return ebadf();
case kFdFile: case kFdFile:

View file

@ -59,30 +59,34 @@ static textwindows int sys_dup_nt_impl(int oldfd, int newfd, int flags,
return -1; return -1;
} }
if (g_fds.p[newfd].kind) { if (g_fds.p[newfd].kind) {
if (g_fds.p[newfd].kind == kFdZip) { sys_close_nt(newfd, newfd);
_weaken(__zipos_close)(newfd);
} else {
sys_close_nt(newfd, newfd);
}
} }
} }
if (DuplicateHandle(GetCurrentProcess(), g_fds.p[oldfd].handle, if (__isfdkind(oldfd, kFdZip)) {
GetCurrentProcess(), &handle, 0, true, handle = (intptr_t)_weaken(__zipos_keep)(
kNtDuplicateSameAccess)) { (struct ZiposHandle *)(intptr_t)g_fds.p[oldfd].handle);
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;
}
rc = newfd; rc = newfd;
} else { } else {
__releasefd(newfd); if (DuplicateHandle(GetCurrentProcess(), g_fds.p[oldfd].handle,
rc = __winerr(); 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(); __fds_unlock();
return rc; return rc;
} }

View file

@ -22,7 +22,9 @@
#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
#include "libc/runtime/zipos.internal.h"
/** /**
* Duplicates file descriptor. * Duplicates file descriptor.
@ -51,10 +53,11 @@
*/ */
int dup(int fd) { int dup(int fd) {
int rc; int rc;
if (__isfdkind(fd, kFdZip)) { if (!IsWindows()) {
rc = enotsup();
} else if (!IsWindows()) {
rc = sys_dup(fd); rc = sys_dup(fd);
if (rc != -1 && __isfdkind(fd, kFdZip)) {
_weaken(__zipos_postdup)(fd, rc);
}
} else { } else {
rc = sys_dup_nt(fd, -1, 0, -1); rc = sys_dup_nt(fd, -1, 0, -1);
} }

View file

@ -67,20 +67,24 @@ int dup2(int oldfd, int newfd) {
int rc; int rc;
// helps guarantee stderr log gets duplicated before user closes // helps guarantee stderr log gets duplicated before user closes
if (_weaken(kloghandle)) _weaken(kloghandle)(); if (_weaken(kloghandle)) _weaken(kloghandle)();
if (__isfdkind(oldfd, kFdZip)) {
rc = enotsup();
#ifdef __aarch64__ #ifdef __aarch64__
} else if (oldfd == newfd) { if (oldfd == newfd) {
// linux aarch64 defines dup3() but not dup2(), which wasn't such a // linux aarch64 defines dup3() but not dup2(), which wasn't such a
// great decision, since the two syscalls don't behave the same way // great decision, since the two syscalls don't behave the same way
if (!(rc = read(oldfd, 0, 0))) rc = oldfd; if (!(rc = read(oldfd, 0, 0))) rc = oldfd;
} else
#endif #endif
} else if (!IsWindows()) { if (!IsWindows()) {
rc = sys_dup2(oldfd, newfd, 0); if (__isfdkind(oldfd, kFdZip) || __isfdkind(newfd, kFdZip)) {
if (rc != -1 && oldfd != newfd && __isfdkind(newfd, kFdZip) && !__vforked) { if (__vforked) {
_weaken(__zipos_free)( return enotsup();
(struct ZiposHandle *)(intptr_t)g_fds.p[newfd].handle); }
bzero(g_fds.p + newfd, sizeof(*g_fds.p)); 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) { } else if (newfd < 0) {
rc = ebadf(); rc = ebadf();

View file

@ -70,14 +70,17 @@ int dup3(int oldfd, int newfd, int flags) {
rc = einval(); // NetBSD doesn't do this rc = einval(); // NetBSD doesn't do this
} else if (oldfd < 0 || newfd < 0) { } else if (oldfd < 0 || newfd < 0) {
rc = ebadf(); rc = ebadf();
} else if (__isfdkind(oldfd, kFdZip)) {
rc = enotsup();
} else if (!IsWindows()) { } else if (!IsWindows()) {
rc = sys_dup3(oldfd, newfd, flags); if (__isfdkind(oldfd, kFdZip) || __isfdkind(newfd, kFdZip)) {
if (rc != -1 && __isfdkind(newfd, kFdZip) && !__vforked) { if (__vforked) {
_weaken(__zipos_free)( return enotsup();
(struct ZiposHandle *)(intptr_t)g_fds.p[newfd].handle); }
bzero(g_fds.p + newfd, sizeof(*g_fds.p)); rc = sys_dup3(oldfd, newfd, flags);
if (rc != -1) {
_weaken(__zipos_postdup)(oldfd, newfd);
}
} else {
rc = sys_dup3(oldfd, newfd, flags);
} }
} else { } else {
rc = sys_dup_nt(oldfd, newfd, flags, -1); rc = sys_dup_nt(oldfd, newfd, flags, -1);

View file

@ -16,13 +16,30 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/createfileflags.internal.h"
#include "libc/calls/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/runtime/zipos.internal.h"
#include "libc/sysv/consts/f.h" #include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h" #include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.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) { int __zipos_fcntl(int fd, int cmd, uintptr_t arg) {
if (cmd == F_GETFD) { if (cmd == F_GETFD) {
if (g_fds.p[fd].flags & O_CLOEXEC) { 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; g_fds.p[fd].flags &= ~O_CLOEXEC;
return 0; return 0;
} }
} else if (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC) {
return __zipos_dupfd(fd, cmd, arg);
} else { } else {
return einval(); return einval();
} }

View file

@ -78,7 +78,20 @@ static void *__zipos_mmap_space(size_t mapsize) {
return start + offset; 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) { void __zipos_free(struct ZiposHandle *h) {
if (!__zipos_drop(h)) {
return;
}
if (IsAsan()) { if (IsAsan()) {
__asan_poison((char *)h + sizeof(struct ZiposHandle), __asan_poison((char *)h + sizeof(struct ZiposHandle),
h->mapsize - sizeof(struct ZiposHandle), kAsanHeapFree); h->mapsize - sizeof(struct ZiposHandle), kAsanHeapFree);
@ -100,7 +113,7 @@ StartOver:
while ((h = *ph)) { while ((h = *ph)) {
if (h->mapsize >= mapsize) { if (h->mapsize >= mapsize) {
if (!_cmpxchg(ph, h, h->next)) goto StartOver; if (!_cmpxchg(ph, h, h->next)) goto StartOver;
h->next = 0; atomic_init(&h->refs, 0);
break; break;
} }
ph = &h->next; ph = &h->next;
@ -209,6 +222,27 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, int flags,
return -1; 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. * Loads compressed file from αcτµαlly pδrταblε εxεcµταblε object store.
* *

View file

@ -33,6 +33,9 @@
.yoink __zipos_stat .yoink __zipos_stat
.yoink __zipos_notat .yoink __zipos_notat
.yoink __zipos_mmap .yoink __zipos_mmap
.yoink __zipos_postdup
.yoink __zipos_keep
.yoink __zipos_free
// TODO(jart): why does corruption happen when zip has no assets? // TODO(jart): why does corruption happen when zip has no assets?
.yoink .cosmo .yoink .cosmo

View file

@ -20,8 +20,9 @@ struct ZiposHandle {
struct Zipos *zipos; struct Zipos *zipos;
size_t size; size_t size;
size_t mapsize; size_t mapsize;
size_t pos;
size_t cfile; size_t cfile;
_Atomic(int) refs;
size_t pos; // TODO atomic
uint8_t *mem; uint8_t *mem;
uint8_t data[]; uint8_t data[];
}; };
@ -38,6 +39,7 @@ struct Zipos {
int __zipos_close(int); int __zipos_close(int);
void __zipos_free(struct ZiposHandle *); void __zipos_free(struct ZiposHandle *);
struct ZiposHandle *__zipos_keep(struct ZiposHandle *);
struct Zipos *__zipos_get(void) pureconst; struct Zipos *__zipos_get(void) pureconst;
size_t __zipos_normpath(char *, const char *, size_t); size_t __zipos_normpath(char *, const char *, size_t);
ssize_t __zipos_find(struct Zipos *, struct ZiposUri *); 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 *); ssize_t __zipos_parseuri(const char *, struct ZiposUri *);
uint64_t __zipos_inode(struct Zipos *, int64_t, const void *, size_t); uint64_t __zipos_inode(struct Zipos *, int64_t, const void *, size_t);
int __zipos_open(struct ZiposUri *, int); int __zipos_open(struct ZiposUri *, int);
void __zipos_postdup(int, int);
int __zipos_access(struct ZiposUri *, int); int __zipos_access(struct ZiposUri *, int);
int __zipos_stat(struct ZiposUri *, struct stat *); int __zipos_stat(struct ZiposUri *, struct stat *);
int __zipos_fstat(struct ZiposHandle *, struct stat *); int __zipos_fstat(struct ZiposHandle *, struct stat *);

View file

@ -75,7 +75,7 @@ TEST(dup, bigNumber) {
ASSERT_SYS(0, 0, close(100)); ASSERT_SYS(0, 0, close(100));
} }
TEST(dup2, zipos) { TEST(dup2, ziposdest) {
ASSERT_SYS(0, 3, creat("real", 0644)); ASSERT_SYS(0, 3, creat("real", 0644));
ASSERT_SYS(0, 4, open("/zip/libc/testlib/hyperion.txt", O_RDONLY)); ASSERT_SYS(0, 4, open("/zip/libc/testlib/hyperion.txt", O_RDONLY));
ASSERT_SYS(0, 2, write(3, "hi", 2)); ASSERT_SYS(0, 2, write(3, "hi", 2));
@ -86,6 +86,15 @@ TEST(dup2, zipos) {
ASSERT_SYS(0, 0, close(3)); 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__ #ifdef __x86_64__
TEST(dup, clearsCloexecFlag) { TEST(dup, clearsCloexecFlag) {
static bool once; static bool once;

View file

@ -32,6 +32,9 @@
#include "libc/thread/thread.h" #include "libc/thread/thread.h"
#include "libc/x/x.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 Lock(int fd, int type, long start, long len) {
int e; int e;
struct flock lock = { struct flock lock = {
@ -121,6 +124,17 @@ TEST(fcntl, F_DUPFD_CLOEXEC) {
ASSERT_SYS(0, 0, close(3)); 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) { void OnSig(int sig) {
} }