mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-12 09:17:53 +00:00
This is a breaking change. It defines the new environment variable named _COSMO_FDS_V2 which is used for inheriting non-stdio file descriptors on execve() or posix_spawn(). No effort has been spent thus far integrating with the older variable. If a new binary launches the older ones or vice versa they'll only be able to pass stdin / stdout / stderr to each other therefore it's important that you upgrade all your cosmo binaries if you depend on this functionality. You'll be glad you did because inheritance of file descriptors is more aligned with the POSIX standard than before.
165 lines
6.8 KiB
C
165 lines
6.8 KiB
C
/*-*- 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 2023 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/calls/createfileflags.internal.h"
|
|
#include "libc/calls/internal.h"
|
|
#include "libc/calls/sig.internal.h"
|
|
#include "libc/calls/struct/sigset.h"
|
|
#include "libc/calls/syscall_support-nt.internal.h"
|
|
#include "libc/intrin/fds.h"
|
|
#include "libc/intrin/weaken.h"
|
|
#include "libc/nt/enum/filetype.h"
|
|
#include "libc/nt/errors.h"
|
|
#include "libc/nt/events.h"
|
|
#include "libc/nt/files.h"
|
|
#include "libc/nt/runtime.h"
|
|
#include "libc/nt/struct/overlapped.h"
|
|
#include "libc/nt/synchronization.h"
|
|
#include "libc/nt/thread.h"
|
|
#include "libc/stdio/sysparam.h"
|
|
#include "libc/sysv/consts/sicode.h"
|
|
#include "libc/sysv/errfuns.h"
|
|
#ifdef __x86_64__
|
|
|
|
/**
|
|
* Runs code that's common to read/write/pread/pwrite/etc on Windows.
|
|
*
|
|
* @return bytes exchanged, or -1 w/ errno, or -2 if operation failed
|
|
* and caller needs to do more work, examining the GetLastError()
|
|
*/
|
|
textwindows ssize_t
|
|
sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
|
|
int64_t handle, sigset_t waitmask,
|
|
bool32 ReadOrWriteFile(int64_t, void *, uint32_t, uint32_t *,
|
|
struct NtOverlapped *)) {
|
|
int sig;
|
|
uint32_t exchanged;
|
|
struct Fd *f = g_fds.p + fd;
|
|
|
|
// pread() and pwrite() perform an implicit lseek() operation, so
|
|
// similar to the lseek() system call, they too raise ESPIPE when
|
|
// operating on a non-seekable file.
|
|
bool pwriting = offset != -1;
|
|
bool isdisk = f->kind == kFdFile && GetFileType(handle) == kNtFileTypeDisk;
|
|
bool seekable = isdisk || f->kind == kFdDevNull || f->kind == kFdDevRandom;
|
|
if (pwriting && !seekable)
|
|
return espipe();
|
|
|
|
// determine if we need to lock a file descriptor across processes
|
|
bool locked = isdisk && !pwriting && f->cursor;
|
|
if (locked)
|
|
__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->cursor) {
|
|
offset = f->cursor->shared->pointer;
|
|
} else {
|
|
offset = 0;
|
|
}
|
|
}
|
|
|
|
bool eagained = false;
|
|
// check for signals and cancelation
|
|
if (_check_cancel() == -1) {
|
|
if (locked)
|
|
__cursor_unlock(f->cursor);
|
|
return -1; // ECANCELED
|
|
}
|
|
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))) {
|
|
goto HandleInterrupt;
|
|
}
|
|
|
|
// signals have already been fully blocked by caller
|
|
// perform i/o operation with atomic signal/cancel checking
|
|
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 1, 0, 0),
|
|
.Pointer = offset};
|
|
bool32 ok = ReadOrWriteFile(handle, data, size, 0, &overlap);
|
|
if (!ok && GetLastError() == kNtErrorIoPending) {
|
|
// win32 says this i/o operation needs to block
|
|
if (f->flags & _O_NONBLOCK) {
|
|
// abort the i/o operation if file descriptor is in non-blocking mode
|
|
CancelIoEx(handle, &overlap);
|
|
eagained = true;
|
|
} else {
|
|
// wait until i/o either completes or is canceled by another thread
|
|
// we avoid a race condition by having a second mask for unblocking
|
|
struct PosixThread *pt;
|
|
pt = _pthread_self();
|
|
pt->pt_blkmask = waitmask;
|
|
pt->pt_iohandle = handle;
|
|
pt->pt_ioverlap = &overlap;
|
|
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_IO,
|
|
memory_order_release);
|
|
WaitForSingleObject(overlap.hEvent, -1u);
|
|
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
|
}
|
|
ok = true;
|
|
}
|
|
if (ok)
|
|
ok = GetOverlappedResult(handle, &overlap, &exchanged, true);
|
|
CloseHandle(overlap.hEvent);
|
|
|
|
// if i/o succeeded then return its result
|
|
if (ok) {
|
|
if (!pwriting && seekable && f->cursor)
|
|
f->cursor->shared->pointer = offset + exchanged;
|
|
if (locked)
|
|
__cursor_unlock(f->cursor);
|
|
return exchanged;
|
|
}
|
|
|
|
// only raise EINTR or EAGAIN if I/O got canceled
|
|
if (GetLastError() == kNtErrorOperationAborted) {
|
|
// raise EAGAIN if it's due to O_NONBLOCK mmode
|
|
if (eagained) {
|
|
if (locked)
|
|
__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)
|
|
__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)
|
|
__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)
|
|
__cursor_unlock(f->cursor);
|
|
return eintr();
|
|
}
|
|
|
|
// read() and write() have generally different error-handling paths
|
|
if (locked)
|
|
__cursor_unlock(f->cursor);
|
|
return -2;
|
|
}
|
|
|
|
#endif /* __x86_64__ */
|