diff --git a/libc/calls/close-nt.c b/libc/calls/close-nt.c index 180f03f4a..1952e4daa 100644 --- a/libc/calls/close-nt.c +++ b/libc/calls/close-nt.c @@ -65,7 +65,7 @@ textwindows int sys_close_nt(int fd, int fildes) { default: break; } - if (f->shared && !f->isdup) - munmap(f->shared, sizeof(struct Cursor)); + if (f->cursor) + __cursor_unref(f->cursor); return CloseHandle(f->handle) ? 0 : __winerr(); } diff --git a/libc/calls/dup-nt.c b/libc/calls/dup-nt.c index 898a653d3..4bf3f68fc 100644 --- a/libc/calls/dup-nt.c +++ b/libc/calls/dup-nt.c @@ -24,6 +24,7 @@ #include "libc/calls/struct/sigset.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" +#include "libc/intrin/fds.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/weaken.h" #include "libc/nt/files.h" @@ -82,8 +83,7 @@ static textwindows int sys_dup_nt_impl(int oldfd, int newfd, int flags, g_fds.p[newfd] = g_fds.p[oldfd]; g_fds.p[newfd].handle = handle; - g_fds.p[newfd].isdup = true; - g_fds.p[oldfd].isdup = true; // TODO(jart): is it possible to avoid leak? + __cursor_ref(g_fds.p[newfd].cursor); if (flags & _O_CLOEXEC) { g_fds.p[newfd].flags |= _O_CLOEXEC; } else { diff --git a/libc/calls/fcntl-nt.c b/libc/calls/fcntl-nt.c index 5c746b487..f18945ce8 100644 --- a/libc/calls/fcntl-nt.c +++ b/libc/calls/fcntl-nt.c @@ -151,7 +151,7 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int fd, int cmd, case SEEK_SET: break; case SEEK_CUR: - off = f->shared->pointer + off; + off = f->cursor->shared->pointer + off; break; case SEEK_END: { int64_t size; @@ -352,7 +352,7 @@ textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) { rc = 0; } else if (cmd == F_SETLK || cmd == F_SETLKW || cmd == F_GETLK) { struct Fd *f = g_fds.p + fd; - if (f->shared) { + if (f->cursor) { pthread_mutex_lock(&g_locks.mu); rc = sys_fcntl_nt_lock(f, fd, cmd, arg); pthread_mutex_unlock(&g_locks.mu); diff --git a/libc/calls/isapemagic.c b/libc/calls/isapemagic.c index a1ca56460..e387880cc 100644 --- a/libc/calls/isapemagic.c +++ b/libc/calls/isapemagic.c @@ -25,6 +25,5 @@ bool IsApeLoadable(char buf[8]) { return READ32LE(buf) == READ32LE("\177ELF") || READ64LE(buf) == READ64LE("MZqFpD='") || - READ64LE(buf) == READ64LE("jartsr='") || - READ64LE(buf) == READ64LE("APEDBG='"); + READ64LE(buf) == READ64LE("jartsr='"); } diff --git a/libc/calls/lseek-nt.c b/libc/calls/lseek-nt.c index 9ca3342e9..9e073b8c4 100644 --- a/libc/calls/lseek-nt.c +++ b/libc/calls/lseek-nt.c @@ -19,6 +19,7 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/syscall_support-nt.internal.h" +#include "libc/intrin/fds.h" #include "libc/nt/enum/filetype.h" #include "libc/nt/files.h" #include "libc/nt/struct/byhandlefileinformation.h" @@ -31,7 +32,7 @@ static textwindows int64_t GetPosition(struct Fd *f, int whence) { case SEEK_SET: return 0; case SEEK_CUR: - return f->shared->pointer; + return f->cursor->shared->pointer; case SEEK_END: { struct NtByHandleFileInformation wst; if (!GetFileInformationByHandle(f->handle, &wst)) { @@ -69,12 +70,12 @@ textwindows int64_t sys_lseek_nt(int fd, int64_t offset, int whence) { int filetype = GetFileType(f->handle); if (filetype != kNtFileTypePipe && // filetype != kNtFileTypeChar && // - f->shared) { + f->cursor->shared) { int64_t res; - __fd_lock(f); + __cursor_lock(f->cursor); if ((res = Seek(f, offset, whence)) != -1) - f->shared->pointer = res; - __fd_unlock(f); + f->cursor->shared->pointer = res; + __cursor_unlock(f->cursor); return res; } else { return espipe(); diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 58d0cefc5..9a2f5808f 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -24,6 +24,7 @@ #include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" +#include "libc/intrin/fds.h" #include "libc/macros.internal.h" #include "libc/nt/console.h" #include "libc/nt/createfile.h" @@ -138,7 +139,7 @@ static textwindows int sys_open_nt_file(int dirfd, const char *file, int64_t handle; if ((handle = sys_open_nt_impl(dirfd, file, flags, mode, kNtFileFlagOverlapped)) != -1) { - g_fds.p[fd].shared = __cursor_new(); + g_fds.p[fd].cursor = __cursor_new(); g_fds.p[fd].handle = handle; g_fds.p[fd].kind = kFdFile; g_fds.p[fd].flags = flags; @@ -178,8 +179,8 @@ static textwindows int sys_open_nt_dup(int fd, int flags, int mode, int oldfd) { kNtDuplicateSameAccess)) { g_fds.p[fd] = g_fds.p[oldfd]; g_fds.p[fd].handle = handle; - g_fds.p[fd].isdup = true; g_fds.p[fd].mode = mode; + __cursor_ref(g_fds.p[fd].cursor); if (!sys_fcntl_nt_setfl(fd, flags)) { return fd; } else { diff --git a/libc/calls/readwrite-nt.c b/libc/calls/readwrite-nt.c index 5ad587ac3..6ef3f376c 100644 --- a/libc/calls/readwrite-nt.c +++ b/libc/calls/readwrite-nt.c @@ -61,29 +61,29 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset, return espipe(); // determine if we need to lock a file descriptor across processes - bool locked = isdisk && !pwriting && f->shared; + bool locked = isdisk && !pwriting && f->cursor; if (locked) - __fd_lock(f); + __cursor_lock(f->cursor); +RestartOperation: // when a file is opened in overlapped mode win32 requires that we // take over full responsibility for managing our own file pointer // which is fine, because the one win32 has was never very good in // the sense that it behaves so differently from linux, that using // win32 i/o required more compatibilty toil than doing it by hand if (!pwriting) { - if (seekable && f->shared) { - offset = f->shared->pointer; + if (seekable && f->cursor) { + offset = f->cursor->shared->pointer; } else { offset = 0; } } -RestartOperation: bool eagained = false; // check for signals and cancelation if (_check_cancel() == -1) { if (locked) - __fd_unlock(f); + __cursor_unlock(f->cursor); return -1; // ECANCELED } if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))) { @@ -122,10 +122,10 @@ RestartOperation: // if i/o succeeded then return its result if (ok) { - if (!pwriting && seekable && f->shared) - f->shared->pointer = offset + exchanged; + if (!pwriting && seekable && f->cursor) + f->cursor->shared->pointer = offset + exchanged; if (locked) - __fd_unlock(f); + __cursor_unlock(f->cursor); return exchanged; } @@ -134,31 +134,31 @@ RestartOperation: // raise EAGAIN if it's due to O_NONBLOCK mmode if (eagained) { if (locked) - __fd_unlock(f); + __cursor_unlock(f->cursor); return eagain(); } // otherwise it must be due to a kill() via __sig_cancel() if (_weaken(__sig_relay) && (sig = _weaken(__sig_get)(waitmask))) { HandleInterrupt: if (locked) - __fd_unlock(f); + __cursor_unlock(f->cursor); int handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask); if (_check_cancel() == -1) return -1; // possible if we SIGTHR'd if (locked) - __fd_lock(f); + __cursor_lock(f->cursor); // read() is @restartable unless non-SA_RESTART hands were called if (!(handler_was_called & SIG_HANDLED_NO_RESTART)) goto RestartOperation; } if (locked) - __fd_unlock(f); + __cursor_unlock(f->cursor); return eintr(); } // read() and write() have generally different error-handling paths if (locked) - __fd_unlock(f); + __cursor_unlock(f->cursor); return -2; } diff --git a/libc/calls/write-nt.c b/libc/calls/write-nt.c index 3e5eb0163..528c28332 100644 --- a/libc/calls/write-nt.c +++ b/libc/calls/write-nt.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" -#include "libc/intrin/fds.h" #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.internal.h" @@ -26,6 +25,7 @@ #include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" #include "libc/intrin/atomic.h" +#include "libc/intrin/fds.h" #include "libc/intrin/nomultics.h" #include "libc/intrin/weaken.h" #include "libc/nt/console.h" diff --git a/libc/integral/c.inc b/libc/integral/c.inc index 34c8a443b..0f29ff5f0 100644 --- a/libc/integral/c.inc +++ b/libc/integral/c.inc @@ -6,12 +6,6 @@ #define COSMOPOLITAN_CXX_USING_ #endif -#ifndef __cplusplus -#pragma GCC diagnostic warning "-Wimplicit-function-declaration" -#pragma GCC diagnostic warning "-Wincompatible-pointer-types" -#pragma GCC diagnostic warning "-Wint-conversion" -#endif - #if !defined(__GNUC__) && __cplusplus + 0 >= 201103L #define typeof(x) decltype(x) #elif !defined(__GNUC__) && __STDC_VERSION__ + 0 < 201112 diff --git a/libc/intrin/BUILD.mk b/libc/intrin/BUILD.mk index 99b0cdf89..16a5526f7 100644 --- a/libc/intrin/BUILD.mk +++ b/libc/intrin/BUILD.mk @@ -62,6 +62,7 @@ o/$(MODE)/libc/intrin/kprintf.o: private \ -Wframe-larger-than=128 \ -Walloca-larger-than=128 +o/$(MODE)/libc/intrin/cursor.o \ o/$(MODE)/libc/intrin/mmap.o \ o/$(MODE)/libc/intrin/tree.o: private \ CFLAGS += \ diff --git a/libc/intrin/cursor.c b/libc/intrin/cursor.c new file mode 100644 index 000000000..e0c686d4f --- /dev/null +++ b/libc/intrin/cursor.c @@ -0,0 +1,64 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2024 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/fds.h" +#include "libc/runtime/runtime.h" + +struct Cursor *__cursor_new(void) { + struct Cursor *c; + if ((c = _mapanon(sizeof(struct Cursor)))) { + if ((c->shared = _mapshared(sizeof(struct CursorShared)))) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + pthread_mutex_init(&c->shared->lock, &attr); + pthread_mutexattr_destroy(&attr); + } else { + munmap(c, sizeof(struct Cursor)); + c = 0; + } + } + return c; +} + +void __cursor_ref(struct Cursor *c) { + if (!c) + return; + unassert(atomic_fetch_add_explicit(&c->refs, 1, memory_order_relaxed) >= 0); +} + +int __cursor_unref(struct Cursor *c) { + if (!c) + return 0; + if (atomic_fetch_sub_explicit(&c->refs, 1, memory_order_release)) + return 0; + atomic_thread_fence(memory_order_acquire); + int rc = munmap(c->shared, sizeof(struct CursorShared)); + rc |= munmap(c, sizeof(struct Cursor)); + return rc; +} + +void __cursor_lock(struct Cursor *c) { + pthread_mutex_lock(&c->shared->lock); +} + +void __cursor_unlock(struct Cursor *c) { + pthread_mutex_unlock(&c->shared->lock); +} diff --git a/libc/intrin/fds.c b/libc/intrin/fds.c index 6f830dfbb..d2883cccf 100644 --- a/libc/intrin/fds.c +++ b/libc/intrin/fds.c @@ -23,7 +23,7 @@ #include "libc/dce.h" #include "libc/intrin/atomic.h" #include "libc/intrin/extend.h" -#include "libc/intrin/kprintf.h" +#include "libc/intrin/maps.h" #include "libc/intrin/nomultics.h" #include "libc/intrin/pushpop.h" #include "libc/intrin/weaken.h" @@ -32,7 +32,9 @@ #include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/creationdisposition.h" #include "libc/nt/enum/fileflagandattributes.h" +#include "libc/nt/enum/filemapflags.h" #include "libc/nt/enum/filesharemode.h" +#include "libc/nt/memory.h" #include "libc/nt/runtime.h" #include "libc/runtime/internal.h" #include "libc/runtime/memtrack.internal.h" @@ -55,7 +57,11 @@ static struct Fd g_fds_static[OPEN_MAX]; static bool TokAtoi(const char **str, long *res) { int c, d; unsigned long x = 0; - d = **str == '-' ? -1 : 1; + d = 1; + if (**str == '-') { + (*str)++; + d = -1; + } while ((c = *(*str)++)) { if (('0' <= c && c <= '9')) { x *= 10; @@ -122,10 +128,11 @@ textstartup void __init_fds(int argc, char **argv, char **envp) { // inherit file descriptors from cosmo parent process if (IsWindows()) { const char *fdspec; - if ((fdspec = getenv("_COSMO_FDS"))) { + if ((fdspec = getenv("_COSMO_FDS_V2"))) { unsetenv("_COSMO_FDS"); + unsetenv("_COSMO_FDS_V2"); for (;;) { - long fd, kind, flags, mode, handle, pointer, type, family, protocol; + long fd, kind, flags, mode, handle, shand, type, family, protocol; if (!TokAtoi(&fdspec, &fd)) break; if (!TokAtoi(&fdspec, &handle)) @@ -136,7 +143,7 @@ textstartup void __init_fds(int argc, char **argv, char **envp) { break; if (!TokAtoi(&fdspec, &mode)) break; - if (!TokAtoi(&fdspec, &pointer)) + if (!TokAtoi(&fdspec, &shand)) break; if (!TokAtoi(&fdspec, &type)) break; @@ -149,9 +156,8 @@ textstartup void __init_fds(int argc, char **argv, char **envp) { struct Fd *f = fds->p + fd; if (f->handle && f->handle != -1 && f->handle != handle) { CloseHandle(f->handle); - if (fd < 3) { + if (fd < 3) SetStdHandle(kNtStdio[fd], handle); - } } f->handle = handle; f->kind = kind; @@ -162,24 +168,31 @@ textstartup void __init_fds(int argc, char **argv, char **envp) { f->protocol = protocol; atomic_store_explicit(&fds->f, fd + 1, memory_order_relaxed); - // - // - v1 abi: This field was originally the file pointer. - // - // - v2 abi: This field is the negated shared memory address. - // - if (f->kind == kFdFile) { - if (pointer < 0) { - f->shared = (struct Cursor *)(uintptr_t)-pointer; - } else if ((f->shared = __cursor_new())) { - f->shared->pointer = pointer; + if (shand) { + struct Map *map; + struct CursorShared *shared; + if ((shared = MapViewOfFileEx(shand, kNtFileMapWrite, 0, 0, + sizeof(struct CursorShared), 0))) { + if ((f->cursor = _mapanon(sizeof(struct Cursor)))) { + f->cursor->shared = shared; + if ((map = __maps_alloc())) { + map->addr = (char *)shared; + map->size = sizeof(struct CursorShared); + map->off = 0; + map->prot = PROT_READ | PROT_WRITE; + map->flags = MAP_SHARED | MAP_ANONYMOUS; + map->hand = shand; + __maps_insert(map); + } + } } } } } for (int i = 0; i < 3; ++i) { struct Fd *f = fds->p + i; - if (f->kind == kFdFile && !f->shared) - f->shared = __cursor_new(); + if (f->kind == kFdFile && !f->cursor) + f->cursor = __cursor_new(); } } } diff --git a/libc/intrin/fds.h b/libc/intrin/fds.h index dc6ac70f4..b9d0f490a 100644 --- a/libc/intrin/fds.h +++ b/libc/intrin/fds.h @@ -1,6 +1,5 @@ #ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_FD_INTERNAL_H_ #define COSMOPOLITAN_LIBC_CALLS_STRUCT_FD_INTERNAL_H_ -#include "libc/atomic.h" #include "libc/thread/thread.h" COSMOPOLITAN_C_START_ @@ -15,14 +14,18 @@ COSMOPOLITAN_C_START_ #define kFdDevNull 9 #define kFdDevRandom 10 -struct Cursor { +struct CursorShared { pthread_mutex_t lock; long pointer; }; +struct Cursor { + struct CursorShared *shared; + _Atomic(int) refs; +}; + struct Fd { char kind; - bool isdup; bool isbound; unsigned flags; unsigned mode; @@ -33,7 +36,7 @@ struct Fd { unsigned rcvtimeo; /* millis; 0 means wait forever */ unsigned sndtimeo; /* millis; 0 means wait forever */ void *connect_op; - struct Cursor *shared; + struct Cursor *cursor; }; struct Fds { @@ -42,9 +45,11 @@ struct Fds { struct Fd *p, *e; }; -void __fd_lock(struct Fd *); -void __fd_unlock(struct Fd *); struct Cursor *__cursor_new(void); +void __cursor_ref(struct Cursor *); +int __cursor_unref(struct Cursor *); +void __cursor_lock(struct Cursor *); +void __cursor_unlock(struct Cursor *); COSMOPOLITAN_C_END_ #endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_FD_INTERNAL_H_ */ diff --git a/libc/intrin/fds_lock.c b/libc/intrin/fds_lock.c index dd3d28491..c32367d85 100644 --- a/libc/intrin/fds_lock.c +++ b/libc/intrin/fds_lock.c @@ -17,8 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/state.internal.h" -#include "libc/intrin/fds.h" -#include "libc/runtime/runtime.h" #include "libc/thread/thread.h" void __fds_lock(void) { @@ -28,23 +26,3 @@ void __fds_lock(void) { void __fds_unlock(void) { pthread_mutex_unlock(&__fds_lock_obj); } - -void __fd_lock(struct Fd *f) { - pthread_mutex_lock(&f->shared->lock); -} - -void __fd_unlock(struct Fd *f) { - pthread_mutex_unlock(&f->shared->lock); -} - -struct Cursor *__cursor_new(void) { - struct Cursor *c; - if ((c = _mapshared(sizeof(struct Cursor)))) { - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); - pthread_mutex_init(&c->lock, &attr); - pthread_mutexattr_destroy(&attr); - } - return c; -} diff --git a/libc/runtime/mapanon.c b/libc/intrin/mapanon.c similarity index 100% rename from libc/runtime/mapanon.c rename to libc/intrin/mapanon.c diff --git a/libc/intrin/maps.h b/libc/intrin/maps.h index ee18dbcb3..5fc9b721b 100644 --- a/libc/intrin/maps.h +++ b/libc/intrin/maps.h @@ -52,6 +52,7 @@ void *__maps_randaddr(void); void *__maps_pickaddr(size_t); void __maps_add(struct Map *); void __maps_free(struct Map *); +void __maps_insert(struct Map *); struct Map *__maps_alloc(void); struct Map *__maps_floor(const char *); void __maps_stack(char *, int, int, size_t, int, intptr_t); diff --git a/libc/intrin/mmap.c b/libc/intrin/mmap.c index 55fef3d38..4a1c02486 100644 --- a/libc/intrin/mmap.c +++ b/libc/intrin/mmap.c @@ -138,7 +138,7 @@ StartOver: __maps.count -= 1; __maps_check(); } else if (IsWindows()) { - // you can't carve up memory maps on windows ;_; + STRACE("you can't carve up memory maps on windows ;_;"); rc = einval(); } else if (addr <= map_addr) { // shave off lefthand side of mapping @@ -246,7 +246,7 @@ static void __maps_free_all(struct Map *list) { } } -static void __maps_insert(struct Map *map) { +void __maps_insert(struct Map *map) { map->flags &= MAP_TYPE | MAP_ANONYMOUS | MAP_NOFORK; // coalesce adjacent mappings @@ -351,12 +351,12 @@ static int __munmap(char *addr, size_t size) { } // untrack mappings + int rc; struct Map *deleted = 0; - __muntrack(addr, pgup_size, pagesz, &deleted); + rc = __muntrack(addr, pgup_size, pagesz, &deleted); __maps_unlock(); // delete mappings - int rc = 0; for (struct Map *map = deleted; map; map = map->freed) { if (!IsWindows()) { if (sys_munmap(map->addr, map->size)) diff --git a/libc/intrin/strace.h b/libc/intrin/strace.h index 3c521857f..adda49caa 100644 --- a/libc/intrin/strace.h +++ b/libc/intrin/strace.h @@ -5,7 +5,7 @@ #define SYSDEBUG 0 #endif -#define _NTTRACE 0 /* not configurable w/ flag yet */ +#define _NTTRACE 1 /* not configurable w/ flag yet */ #define _POLLTRACE 0 /* not configurable w/ flag yet */ #define _DATATRACE 1 /* not configurable w/ flag yet */ #define _LOCKTRACE 0 /* not configurable w/ flag yet */ diff --git a/libc/proc/describefds.c b/libc/proc/describefds.c index e54615a2f..6cf25d78b 100644 --- a/libc/proc/describefds.c +++ b/libc/proc/describefds.c @@ -18,8 +18,10 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/syscall_support-nt.internal.h" +#include "libc/errno.h" #include "libc/fmt/itoa.h" #include "libc/intrin/fds.h" +#include "libc/intrin/maps.h" #include "libc/intrin/strace.h" #include "libc/mem/mem.h" #include "libc/nt/files.h" @@ -27,7 +29,7 @@ #include "libc/nt/struct/startupinfo.h" #include "libc/sysv/consts/o.h" -#define FDS_VAR "_COSMO_FDS=" +#define FDS_VAR "_COSMO_FDS_V2=" #define MAX_ENTRY_BYTES 256 @@ -99,6 +101,8 @@ textwindows char *__describe_fds(const struct Fd *fds, size_t fdslen, if (__is_cloexec(f)) continue; ++handlecount; + if (f->cursor) + ++handlecount; } if (!(handles = calloc(handlecount, sizeof(*handles)))) { OnFailure: @@ -116,17 +120,31 @@ textwindows char *__describe_fds(const struct Fd *fds, size_t fdslen, // make inheritable version of handle exist in creator process if (!DuplicateHandle(GetCurrentProcess(), f->handle, hCreatorProcess, &handle, 0, true, kNtDuplicateSameAccess)) { - STRACE("__describe_fds() DuplicateHandle() failed w/ %d", GetLastError()); __winerr(); goto OnFailure; } - for (uint32_t i = 0; i < 3; ++i) { - if (lpStartupInfo->stdiofds[i] == f->handle) { + for (uint32_t i = 0; i < 3; ++i) + if (lpStartupInfo->stdiofds[i] == f->handle) lpStartupInfo->stdiofds[i] = handle; - } - } handles[hi++] = handle; + // get shared memory handle for the file offset pointer + intptr_t shand = 0; + if (f->cursor) { + struct Map *map; + if (!(map = __maps_floor((const char *)f->cursor->shared)) || + map->addr != (const char *)f->cursor->shared) { + errno = EFAULT; + goto OnFailure; + } + if (!DuplicateHandle(GetCurrentProcess(), map->hand, hCreatorProcess, + &shand, 0, true, kNtDuplicateSameAccess)) { + __winerr(); + goto OnFailure; + } + handles[hi++] = shand; + } + // ensure output string has enough space for new entry if (sb.i + MAX_ENTRY_BYTES > sb.n) { char *p2; @@ -151,12 +169,7 @@ textwindows char *__describe_fds(const struct Fd *fds, size_t fdslen, *p++ = '_'; p = FormatInt64(p, f->mode); *p++ = '_'; - // - // - v1 abi: This field was originally the file pointer. - // - // - v2 abi: This field is the negated shared memory address. - // - p = FormatInt64(p, -(uintptr_t)f->shared); + p = FormatInt64(p, shand); *p++ = '_'; p = FormatInt64(p, f->type); *p++ = '_'; diff --git a/libc/proc/describefds.internal.h b/libc/proc/describefds.internal.h index dd192630a..1cde5234b 100644 --- a/libc/proc/describefds.internal.h +++ b/libc/proc/describefds.internal.h @@ -4,6 +4,8 @@ #include "libc/nt/struct/startupinfo.h" COSMOPOLITAN_C_START_ +#define CURSOR_ADDRESS_FLAG 0x4000000000000000 + bool __is_cloexec(const struct Fd *) libcesque; void __undescribe_fds(int64_t, int64_t *, uint32_t) libcesque; char *__describe_fds(const struct Fd *, size_t, struct NtStartupInfo *, int64_t, diff --git a/libc/runtime/straceinit.greg.c b/libc/runtime/straceinit.greg.c index 7817b547a..92bf2ce18 100644 --- a/libc/runtime/straceinit.greg.c +++ b/libc/runtime/straceinit.greg.c @@ -28,9 +28,8 @@ */ textstartup int __strace_init(int argc, char **argv, char **envp, long *auxv) { /* asan isn't initialized yet at runlevel 300 */ - if ((__intercept_flag(&argc, argv, "--strace") || - __atoul(nulltoempty(__getenv(envp, "STRACE").s))) && - !issetugid()) { + if (__intercept_flag(&argc, argv, "--strace") || + __atoul(nulltoempty(__getenv(envp, "STRACE").s))) { strace_enabled(+1); } return (__argc = argc); diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 77ebd63c2..6ecbaa16d 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -21,6 +21,7 @@ #include "libc/calls/sig.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/dll.h" +#include "libc/intrin/kprintf.h" #include "libc/intrin/maps.h" #include "libc/intrin/nomultics.h" #include "libc/intrin/weaken.h" diff --git a/libc/runtime/zipos-get.c b/libc/runtime/zipos-get.c index c9b39737f..e97918d14 100644 --- a/libc/runtime/zipos-get.c +++ b/libc/runtime/zipos-get.c @@ -21,6 +21,7 @@ #include "libc/calls/metalfile.internal.h" #include "libc/calls/struct/stat.h" #include "libc/cosmo.h" +#include "libc/dce.h" #include "libc/fmt/conv.h" #include "libc/intrin/cmpxchg.h" #include "libc/intrin/promises.h" @@ -62,15 +63,11 @@ static void __zipos_dismiss(uint8_t *map, const uint8_t *cdir, long pg) { } // unmap the executable portion beneath the local files - mo = ROUNDDOWN(lo, __gransize); - if (mo) - munmap(map, mo); - - // this is supposed to reduce our rss usage but does it really? - lo = ROUNDDOWN(lo, pg); - hi = MIN(ROUNDUP(hi, pg), ROUNDDOWN(c, pg)); - if (hi > lo) - posix_madvise(map + lo, hi - lo, POSIX_MADV_DONTNEED); + if (!IsWindows()) { + mo = ROUNDDOWN(lo, __gransize); + if (mo) + munmap(map, mo); + } } static int __zipos_compare_names(const void *a, const void *b, void *c) { diff --git a/libc/sock/sendfile.c b/libc/sock/sendfile.c index 249793784..e10611006 100644 --- a/libc/sock/sendfile.c +++ b/libc/sock/sendfile.c @@ -65,7 +65,7 @@ static dontinline textwindows ssize_t sys_sendfile_nt( bool locked = false; int64_t ih, oh, eof, offset; struct NtByHandleFileInformation wst; - if (!__isfdkind(infd, kFdFile) || !g_fds.p[infd].shared) + if (!__isfdkind(infd, kFdFile) || !g_fds.p[infd].cursor) return ebadf(); if (!__isfdkind(outfd, kFdSocket)) return ebadf(); @@ -75,8 +75,8 @@ static dontinline textwindows ssize_t sys_sendfile_nt( offset = *opt_in_out_inoffset; } else { locked = true; - __fd_lock(&g_fds.p[infd]); - offset = g_fds.p[infd].shared->pointer; + __cursor_lock(g_fds.p[infd].cursor); + offset = g_fds.p[infd].cursor->shared->pointer; } if (GetFileInformationByHandle(ih, &wst)) { // TransmitFile() returns EINVAL if `uptobytes` goes past EOF. @@ -86,7 +86,7 @@ static dontinline textwindows ssize_t sys_sendfile_nt( } } else { if (locked) - __fd_unlock(&g_fds.p[infd]); + __cursor_unlock(g_fds.p[infd].cursor); return ebadf(); } struct NtOverlapped ov = {.hEvent = WSACreateEvent(), .Pointer = offset}; @@ -99,7 +99,7 @@ static dontinline textwindows ssize_t sys_sendfile_nt( if (opt_in_out_inoffset) { *opt_in_out_inoffset = offset + rc; } else { - g_fds.p[infd].shared->pointer = offset + rc; + g_fds.p[infd].cursor->shared->pointer = offset + rc; } } else { rc = __winsockerr(); @@ -108,7 +108,7 @@ static dontinline textwindows ssize_t sys_sendfile_nt( rc = __winsockerr(); } if (locked) - __fd_unlock(&g_fds.p[infd]); + __cursor_unlock(g_fds.p[infd].cursor); WSACloseEvent(ov.hEvent); return rc; } diff --git a/test/libc/calls/lseek_test.c b/test/libc/calls/lseek_test.c index bff1cbdd4..72214fdb3 100644 --- a/test/libc/calls/lseek_test.c +++ b/test/libc/calls/lseek_test.c @@ -17,12 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" -#include "libc/dce.h" #include "libc/errno.h" -#include "libc/limits.h" -#include "libc/log/check.h" -#include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/sysv/consts/af.h" #include "libc/sysv/consts/ipproto.h" @@ -30,23 +25,22 @@ #include "libc/sysv/consts/sock.h" #include "libc/testlib/subprocess.h" #include "libc/testlib/testlib.h" -#include "libc/x/x.h" void SetUpOnce(void) { testlib_enable_tmp_setup_teardown(); ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr proc inet", 0)); } -/* TEST(lseek, ebadf) { */ -/* ASSERT_SYS(EBADF, -1, lseek(-1, 0, SEEK_SET)); */ -/* ASSERT_SYS(EBADF, -1, lseek(+3, 0, SEEK_SET)); */ -/* } */ +TEST(lseek, ebadf) { + ASSERT_SYS(EBADF, -1, lseek(-1, 0, SEEK_SET)); + ASSERT_SYS(EBADF, -1, lseek(+3, 0, SEEK_SET)); +} -/* TEST(lseek, badWhence_einval) { */ -/* ASSERT_SYS(0, 3, creat("foo", 0644)); */ -/* ASSERT_SYS(EINVAL, -1, lseek(3, 0, -1)); */ -/* EXPECT_SYS(0, 0, close(3)); */ -/* } */ +TEST(lseek, badWhence_einval) { + ASSERT_SYS(0, 3, creat("foo", 0644)); + ASSERT_SYS(EINVAL, -1, lseek(3, 0, -1)); + EXPECT_SYS(0, 0, close(3)); +} TEST(lseek, negativeComputedOffset_einval) { ASSERT_SYS(0, 3, creat("foo", 0644)); @@ -59,68 +53,66 @@ TEST(lseek, negativeComputedOffset_einval) { EXPECT_SYS(0, 0, close(3)); } -/* TEST(lseek, 64bit) { */ -/* ASSERT_SYS(0, 3, creat("foo", 0644)); */ -/* ASSERT_SYS(0, 0x100000001, lseek(3, 0x100000001, SEEK_SET)); */ -/* EXPECT_SYS(0, 0, close(3)); */ -/* } */ +TEST(lseek, 64bit) { + ASSERT_SYS(0, 3, creat("foo", 0644)); + ASSERT_SYS(0, 0x100000001, lseek(3, 0x100000001, SEEK_SET)); + EXPECT_SYS(0, 0, close(3)); +} -/* TEST(lseek, isPipe_ESPIPE) { */ -/* int fds[2]; */ -/* char buf[2]; */ -/* ASSERT_SYS(0, 0, pipe(fds)); */ -/* ASSERT_SYS(ESPIPE, -1, lseek(3, 0, SEEK_SET)); */ -/* ASSERT_SYS(ESPIPE, -1, pwrite(4, "hi", 2, 0)); */ -/* ASSERT_SYS(ESPIPE, -1, pread(3, buf, 2, 0)); */ -/* EXPECT_SYS(0, 0, close(4)); */ -/* EXPECT_SYS(0, 0, close(3)); */ -/* } */ +TEST(lseek, isPipe_ESPIPE) { + int fds[2]; + char buf[2]; + ASSERT_SYS(0, 0, pipe(fds)); + ASSERT_SYS(ESPIPE, -1, lseek(3, 0, SEEK_SET)); + ASSERT_SYS(ESPIPE, -1, pwrite(4, "hi", 2, 0)); + ASSERT_SYS(ESPIPE, -1, pread(3, buf, 2, 0)); + EXPECT_SYS(0, 0, close(4)); + EXPECT_SYS(0, 0, close(3)); +} -/* TEST(lseek, isSocket_ESPIPE) { */ -/* char buf[2]; */ -/* ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); */ -/* ASSERT_SYS(ESPIPE, -1, lseek(3, 0, SEEK_SET)); */ -/* ASSERT_SYS(ESPIPE, -1, pwrite(3, "hi", 2, 0)); */ -/* ASSERT_SYS(ESPIPE, -1, pread(3, buf, 2, 0)); */ -/* EXPECT_SYS(0, 0, close(3)); */ -/* } */ +TEST(lseek, isSocket_ESPIPE) { + char buf[2]; + ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + ASSERT_SYS(ESPIPE, -1, lseek(3, 0, SEEK_SET)); + ASSERT_SYS(ESPIPE, -1, pwrite(3, "hi", 2, 0)); + ASSERT_SYS(ESPIPE, -1, pread(3, buf, 2, 0)); + EXPECT_SYS(0, 0, close(3)); +} -/* TEST(lseek, filePositionChanges_areObservableAcrossDup) { */ -/* if (IsWindows()) return; // do not want to support */ -/* ASSERT_SYS(0, 3, creat("wut", 0644)); */ -/* ASSERT_SYS(0, 4, dup(3)); */ -/* ASSERT_SYS(0, 0, lseek(3, 0, SEEK_CUR)); */ -/* ASSERT_SYS(0, 1, lseek(4, 1, SEEK_SET)); */ -/* ASSERT_SYS(0, 1, lseek(3, 0, SEEK_CUR)); */ -/* EXPECT_SYS(0, 0, close(4)); */ -/* EXPECT_SYS(0, 0, close(3)); */ -/* } */ +TEST(lseek, filePositionChanges_areObservableAcrossDup) { + ASSERT_SYS(0, 3, creat("wut", 0644)); + ASSERT_SYS(0, 4, dup(3)); + ASSERT_SYS(0, 0, lseek(3, 0, SEEK_CUR)); + ASSERT_SYS(0, 1, lseek(4, 1, SEEK_SET)); + ASSERT_SYS(0, 1, lseek(3, 0, SEEK_CUR)); + EXPECT_SYS(0, 0, close(4)); + EXPECT_SYS(0, 0, close(3)); +} -/* TEST(lseek, filePositionChanges_areObservableAcrossProcesses) { */ -/* if (IsWindows()) return; // do not want to support */ -/* char buf[8] = {0}; */ -/* ASSERT_SYS(0, 3, open("wut", O_RDWR | O_CREAT, 0644)); */ -/* ASSERT_SYS(0, 3, write(3, "wut", 3)); */ -/* ASSERT_SYS(0, 0, lseek(3, 0, SEEK_SET)); */ -/* SPAWN(fork); */ -/* ASSERT_SYS(0, 1, lseek(3, 1, SEEK_SET)); */ -/* EXITS(0); */ -/* EXPECT_SYS(0, 1, read(3, buf, 1)); */ -/* EXPECT_EQ('u', buf[0]); */ -/* EXPECT_SYS(0, 0, close(3)); */ -/* } */ +TEST(lseek, filePositionChanges_areObservableAcrossProcesses) { + char buf[8] = {0}; + ASSERT_SYS(0, 3, open("wut", O_RDWR | O_CREAT, 0644)); + ASSERT_SYS(0, 3, write(3, "wut", 3)); + ASSERT_SYS(0, 0, lseek(3, 0, SEEK_SET)); + SPAWN(fork); + ASSERT_SYS(0, 1, lseek(3, 1, SEEK_SET)); + EXITS(0); + EXPECT_SYS(0, 1, read(3, buf, 1)); + EXPECT_EQ('u', buf[0]); + EXPECT_SYS(0, 0, close(3)); +} -/* TEST(lseek, beyondEndOfFile_isZeroExtendedUponSubsequentWrite) { */ -/* char buf[8] = {1, 1}; */ -/* ASSERT_SYS(0, 3, open("foo", O_RDWR | O_CREAT | O_TRUNC, 0644)); */ -/* ASSERT_SYS(0, 2, lseek(3, 2, SEEK_SET)); */ -/* ASSERT_SYS(0, 2, lseek(3, 0, SEEK_CUR)); */ -/* ASSERT_SYS(0, 0, pread(3, buf, 8, 0)); // lseek() alone doesn't extend */ -/* ASSERT_SYS(0, 2, write(3, buf, 2)); // does extend once i/o happens */ -/* ASSERT_SYS(0, 4, pread(3, buf, 8, 0)); */ -/* ASSERT_EQ(0, buf[0]); */ -/* ASSERT_EQ(0, buf[1]); */ -/* ASSERT_EQ(1, buf[2]); */ -/* ASSERT_EQ(1, buf[3]); */ -/* ASSERT_SYS(0, 0, close(3)); */ -/* } */ +TEST(lseek, beyondEndOfFile_isZeroExtendedUponSubsequentWrite) { + char buf[8] = {1, 1}; + ASSERT_SYS(0, 3, open("foo", O_RDWR | O_CREAT | O_TRUNC, 0644)); + ASSERT_SYS(0, 2, lseek(3, 2, SEEK_SET)); + ASSERT_SYS(0, 2, lseek(3, 0, SEEK_CUR)); + ASSERT_SYS(0, 0, pread(3, buf, 8, 0)); // lseek() alone doesn't extend + ASSERT_SYS(0, 2, write(3, buf, 2)); // does extend once i/o happens + ASSERT_SYS(0, 4, pread(3, buf, 8, 0)); + ASSERT_EQ(0, buf[0]); + ASSERT_EQ(0, buf[1]); + ASSERT_EQ(1, buf[2]); + ASSERT_EQ(1, buf[3]); + ASSERT_SYS(0, 0, close(3)); +} diff --git a/test/posix/BUILD.mk b/test/posix/BUILD.mk index 0c1209cf5..420d6ea31 100644 --- a/test/posix/BUILD.mk +++ b/test/posix/BUILD.mk @@ -3,60 +3,74 @@ PKGS += TEST_POSIX -TEST_POSIX_SRCS := \ +TEST_POSIX_SRCS := \ $(wildcard test/posix/*.c) -TEST_POSIX_SRCS_TEST = \ +TEST_POSIX_SRCS_TEST = \ $(filter %_test.c,$(TEST_POSIX_SRCS)) -TEST_POSIX_OBJS = \ +TEST_POSIX_OBJS = \ $(TEST_POSIX_SRCS:%.c=o/$(MODE)/%.o) -TEST_POSIX_COMS = \ - $(TEST_POSIX_SRCS_TEST:%.c=o/$(MODE)/%) +TEST_POSIX_COMS = \ + $(TEST_POSIX_SRCS_TEST:%.c=o/$(MODE)/%) \ + o/$(MODE)/test/posix/file_offset_exec_prog -TEST_POSIX_BINS = \ - $(TEST_POSIX_COMS) \ +TEST_POSIX_BINS = \ + $(TEST_POSIX_COMS) \ $(TEST_POSIX_COMS:%=%.dbg) -TEST_POSIX_TESTS = \ +TEST_POSIX_TESTS = \ $(TEST_POSIX_SRCS_TEST:%.c=o/$(MODE)/%.ok) -TEST_POSIX_CHECKS = \ +TEST_POSIX_CHECKS = \ $(TEST_POSIX_SRCS_TEST:%.c=o/$(MODE)/%.runs) -TEST_POSIX_DIRECTDEPS = \ - LIBC_CALLS \ - LIBC_FMT \ - LIBC_INTRIN \ - LIBC_MEM \ - LIBC_PROC \ - LIBC_RUNTIME \ - LIBC_STDIO \ - LIBC_STR \ - LIBC_SYSV \ - LIBC_THREAD \ - THIRD_PARTY_MUSL \ +TEST_POSIX_DIRECTDEPS = \ + LIBC_CALLS \ + LIBC_FMT \ + LIBC_INTRIN \ + LIBC_MEM \ + LIBC_PROC \ + LIBC_RUNTIME \ + LIBC_STDIO \ + LIBC_STR \ + LIBC_SYSV \ + LIBC_THREAD \ + THIRD_PARTY_MUSL \ -TEST_POSIX_DEPS := \ +TEST_POSIX_DEPS := \ $(call uniq,$(foreach x,$(TEST_POSIX_DIRECTDEPS),$($(x)))) -o/$(MODE)/test/posix/posix.pkg: \ - $(TEST_POSIX_OBJS) \ +o/$(MODE)/test/posix/posix.pkg: \ + $(TEST_POSIX_OBJS) \ $(foreach x,$(TEST_POSIX_DIRECTDEPS),$($(x)_A).pkg) -o/$(MODE)/test/posix/%.dbg: \ - $(TEST_POSIX_DEPS) \ - o/$(MODE)/test/posix/%.o \ - o/$(MODE)/test/posix/posix.pkg \ - $(CRT) \ +o/$(MODE)/test/posix/%.dbg: \ + $(TEST_POSIX_DEPS) \ + o/$(MODE)/test/posix/%.o \ + o/$(MODE)/test/posix/posix.pkg \ + $(CRT) \ $(APE_NO_MODIFY_SELF) @$(APELINK) -o/$(MODE)/test/posix/fread3gb_test.runs: \ +o/$(MODE)/test/posix/file_offset_exec_test.dbg: \ + $(TEST_POSIX_DEPS) \ + o/$(MODE)/test/posix/file_offset_exec_test.o \ + o/$(MODE)/test/posix/file_offset_exec_prog.zip.o \ + o/$(MODE)/test/posix/posix.pkg \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + +o/$(MODE)/test/posix/file_offset_exec_prog.zip.o: private \ + ZIPOBJ_FLAGS += \ + -B + +o/$(MODE)/test/posix/fread3gb_test.runs: \ private QUOTA += -F5gb -M5gb .PHONY: o/$(MODE)/test/posix -o/$(MODE)/test/posix: \ - $(TEST_POSIX_BINS) \ +o/$(MODE)/test/posix: \ + $(TEST_POSIX_BINS) \ $(TEST_POSIX_CHECKS) diff --git a/test/posix/file_offset_exec_prog.c b/test/posix/file_offset_exec_prog.c new file mode 100644 index 000000000..31f19560b --- /dev/null +++ b/test/posix/file_offset_exec_prog.c @@ -0,0 +1,63 @@ +// Copyright 2024 Justine Alexandra Roberts Tunney +// +// Permission to use, copy, modify, and/or distribute this software for +// any purpose with or without fee is hereby granted, provided that the +// above copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include +#include +#include + +// subprogram for testing that lseek() is shared across execve() + +atomic_int *phase; + +int main(int argc, char *argv[]) { + + if (argc != 3) + return 101; + + int fd = atoi(argv[1]); + int mapfd = atoi(argv[2]); + + if ((phase = mmap(0, sizeof(atomic_int), PROT_READ | PROT_WRITE, MAP_SHARED, + mapfd, 0)) == MAP_FAILED) + return 102; + + if (write(fd, "1", 1) != 1) + return 103; + if (lseek(fd, 0, SEEK_CUR) != 2) + return 104; + + *phase = 1; + for (;;) + if (*phase == 2) + break; + + if (write(fd, "3", 1) != 1) + return 105; + if (lseek(fd, 0, SEEK_CUR) != 4) + return 106; + + *phase = 3; + for (;;) + if (*phase == 4) + break; + + if (munmap(phase, sizeof(atomic_int))) + return 107; + if (close(mapfd)) + return 108; + if (close(fd)) + return 109; +} diff --git a/test/posix/file_offset_exec_test.c b/test/posix/file_offset_exec_test.c new file mode 100644 index 000000000..aafc9061a --- /dev/null +++ b/test/posix/file_offset_exec_test.c @@ -0,0 +1,162 @@ +// Copyright 2024 Justine Alexandra Roberts Tunney +// +// Permission to use, copy, modify, and/or distribute this software for +// any purpose with or without fee is hereby granted, provided that the +// above copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include +#include +#include +#include +#include + +// test that lseek() is shared across execve() + +__static_yoink("zipos"); + +void on_unexpected_death(int sig) { + int ws; + if (wait(&ws) == -1) + _Exit(33); + if (!WIFEXITED(ws)) + _Exit(34); + if (!(WEXITSTATUS(ws) & 255)) + _Exit(35); + _Exit(WEXITSTATUS(ws)); +} + +int main() { + signal(SIGCHLD, on_unexpected_death); + + // extract test program + int exefd; + int zipfd; + ssize_t got; + char exepath[] = "/tmp/file_offset_exec_prog.XXXXXX"; + if ((exefd = mkstemp(exepath)) == -1) + return 2; + if (fchmod(exefd, 0755)) + return 3; + if ((zipfd = open("/zip/file_offset_exec_prog", O_RDONLY)) == -1) + return 4; + for (;;) { + char chunk[512]; + if ((got = read(zipfd, chunk, sizeof(chunk))) == -1) + return 5; + if (!got) + break; + if (write(exefd, chunk, got) != got) + return 6; + } + if (close(zipfd)) + return 7; + if (close(exefd)) + return 8; + + // create file shared memory mapping for synchronization + int mapfd; + atomic_int *phase; + char mappath[] = "/tmp/file_offset_exec_phase.XXXXXX"; + if ((mapfd = mkstemp(mappath)) == -1) + return 9; + if (ftruncate(mapfd, sizeof(atomic_int))) + return 10; + if ((phase = mmap(0, sizeof(atomic_int), PROT_READ | PROT_WRITE, MAP_SHARED, + mapfd, 0)) == MAP_FAILED) + return 11; + + // create test file to which both processes shall be writing + int fd; + char path[] = "/tmp/file_offset_exec_file.XXXXXX"; + if ((fd = mkstemp(path)) == -1) + return 12; + if (lseek(fd, 0, SEEK_CUR) != 0) + return 13; + + // start writing to file + if (write(fd, "0", 1) != 1) + return 14; + if (lseek(fd, 0, SEEK_CUR) != 1) + return 15; + + // spawn program + int pid; + if ((pid = fork()) == -1) + return 16; + if (!pid) { + char str[2][12]; + char *envs[] = {0}; + char *args[] = {exepath, str[0], str[1], 0}; + sprintf(str[0], "%d", fd); + sprintf(str[1], "%d", mapfd); + execve(exepath, args, envs); + _Exit(17); + } + + for (;;) + if (*phase == 1) + break; + + if (write(fd, "2", 1) != 1) + return 18; + if (lseek(fd, 0, SEEK_CUR) != 3) + return 19; + + *phase = 2; + for (;;) + if (*phase == 3) + break; + + if (write(fd, "4", 1) != 1) + return 20; + if (lseek(fd, 0, SEEK_CUR) != 5) + return 21; + + signal(SIGCHLD, SIG_DFL); + *phase = 4; + + int ws; + if (wait(&ws) == -1) + return 22; + if (!WIFEXITED(ws)) + return 23; + if (WEXITSTATUS(ws)) + return WEXITSTATUS(ws); + + char buf[16] = {0}; + if (pread(fd, buf, 15, 0) != 5) + return 24; + if (lseek(fd, 0, SEEK_CUR) != 5) + return 25; + + if (close(fd)) + return 26; + + if (unlink(path)) + return 27; + + if (unlink(exepath)) + return 28; + + if (munmap(phase, sizeof(atomic_int))) + return 29; + + if (close(mapfd)) + return 30; + + if (unlink(mappath)) + return 31; + + if (strcmp(buf, "01234")) + return 32; +} diff --git a/test/posix/file_offset_shared_test.c b/test/posix/file_offset_fork_test.c similarity index 75% rename from test/posix/file_offset_shared_test.c rename to test/posix/file_offset_fork_test.c index 5124de186..72a02014b 100644 --- a/test/posix/file_offset_shared_test.c +++ b/test/posix/file_offset_fork_test.c @@ -13,42 +13,54 @@ // TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include #include #include #include #include -// test that file offset is shared across multiple processes +// test that lseek() is shared across fork() -atomic_int *phase; +void on_unexpected_death(int sig) { + int ws; + if (wait(&ws) == -1) + _Exit(33); + if (!WIFEXITED(ws)) + _Exit(34); + if (!(WEXITSTATUS(ws) & 255)) + _Exit(35); + _Exit(WEXITSTATUS(ws)); +} int main() { + signal(SIGCHLD, on_unexpected_death); + atomic_int *phase; if ((phase = mmap(0, sizeof(atomic_int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED) - return 1; + return 2; int fd; - char path[] = "/tmp/fd_test.XXXXXX"; + char path[] = "/tmp/file_offset_fork_test.XXXXXX"; if ((fd = mkstemp(path)) == -1) - return 2; + return 3; if (lseek(fd, 0, SEEK_CUR) != 0) - return 22; + return 4; if (write(fd, "0", 1) != 1) - return 3; + return 5; if (lseek(fd, 0, SEEK_CUR) != 1) - return 33; + return 6; int pid; if ((pid = fork()) == -1) - return 4; + return 7; if (!pid) { if (write(fd, "1", 1) != 1) - _Exit(100); + _Exit(8); if (lseek(fd, 0, SEEK_CUR) != 2) - _Exit(101); + _Exit(9); *phase = 1; for (;;) @@ -56,10 +68,15 @@ int main() { break; if (write(fd, "3", 1) != 1) - _Exit(102); + _Exit(10); if (lseek(fd, 0, SEEK_CUR) != 4) - _Exit(103); + _Exit(11); + *phase = 3; + for (;;) + if (*phase == 4) + break; + _Exit(0); } @@ -68,9 +85,9 @@ int main() { break; if (write(fd, "2", 1) != 1) - return 5; + return 12; if (lseek(fd, 0, SEEK_CUR) != 3) - return 55; + return 13; *phase = 2; for (;;) @@ -78,30 +95,36 @@ int main() { break; if (write(fd, "4", 1) != 1) - return 6; + return 14; if (lseek(fd, 0, SEEK_CUR) != 5) - return 66; + return 15; + + signal(SIGCHLD, SIG_DFL); + *phase = 4; int ws; if (wait(&ws) == -1) - return 7; + return 16; if (!WIFEXITED(ws)) - return 8; + return 17; if (WEXITSTATUS(ws)) return WEXITSTATUS(ws); char buf[16] = {0}; if (pread(fd, buf, 15, 0) != 5) - return 12; + return 18; if (lseek(fd, 0, SEEK_CUR) != 5) - return 77; + return 19; if (close(fd)) - return 13; + return 20; + + if (munmap(phase, sizeof(atomic_int))) + return 21; if (unlink(path)) - return 14; + return 22; if (strcmp(buf, "01234")) - return 15; + return 23; }