Rewrite special file handling on Windows

This change gets GNU grep working. What caused it to not work, is it
wouldn't write to an output file descriptor when its dev/ino equaled
/dev/null's. So now we invent special dev/ino values for these files
This commit is contained in:
Justine Tunney 2023-10-14 01:06:00 -07:00
parent aca2261cda
commit 2db2f40a98
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
53 changed files with 485 additions and 299 deletions

View file

@ -33,9 +33,7 @@
* @noreturn
*/
wontreturn void abort(void) {
sigset_t m;
sigemptyset(&m);
sigaddset(&m, SIGABRT);
sigset_t m = 1ull << (SIGABRT - 1);
sigprocmask(SIG_UNBLOCK, &m, 0);
raise(SIGABRT);
signal(SIGABRT, SIG_DFL);

View file

@ -321,63 +321,18 @@ static textwindows int sys_fcntl_nt_dupfd(int fd, int cmd, int start) {
return sys_dup_nt(fd, -1, (cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0), start);
}
static textwindows int sys_fcntl_nt_setfl(int fd, unsigned *flags,
unsigned mode, unsigned arg,
intptr_t *handle) {
// you may change the following:
//
// - O_NONBLOCK make read() raise EAGAIN
// - O_APPEND for toggling append mode
// - O_RANDOM alt. for posix_fadvise()
// - O_SEQUENTIAL alt. for posix_fadvise()
// - O_DIRECT works but haven't tested
//
// the other bits are ignored.
unsigned allowed = O_APPEND | O_SEQUENTIAL | O_RANDOM | O_DIRECT | O_NONBLOCK;
unsigned needreo = O_APPEND | O_SEQUENTIAL | O_RANDOM | O_DIRECT;
unsigned newflag = (*flags & ~allowed) | (arg & allowed);
if ((*flags & needreo) ^ (arg & needreo)) {
unsigned perm, share, attr;
if (GetNtOpenFlags(newflag, mode, &perm, &share, 0, &attr) == -1) {
return -1;
}
// MSDN says only these are allowed, otherwise it returns EINVAL.
attr &= kNtFileFlagBackupSemantics | kNtFileFlagDeleteOnClose |
kNtFileFlagNoBuffering | kNtFileFlagOpenNoRecall |
kNtFileFlagOpenReparsePoint | kNtFileFlagOverlapped |
kNtFileFlagPosixSemantics | kNtFileFlagRandomAccess |
kNtFileFlagSequentialScan | kNtFileFlagWriteThrough;
intptr_t hand;
if ((hand = ReOpenFile(*handle, perm, share, attr)) != -1) {
if (hand != *handle) {
CloseHandle(*handle);
*handle = hand;
}
} else {
return __winerr();
}
}
// 1. ignore flags that aren't access mode flags
// 2. return zero if nothing's changed
*flags = newflag;
return 0;
}
textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) {
int rc;
BLOCK_SIGNALS;
if (__isfdkind(fd, kFdFile) || //
__isfdkind(fd, kFdSocket) || //
__isfdkind(fd, kFdConsole)) {
if (__isfdkind(fd, kFdFile) || //
__isfdkind(fd, kFdSocket) || //
__isfdkind(fd, kFdConsole) || //
__isfdkind(fd, kFdDevNull)) {
if (cmd == F_GETFL) {
rc = g_fds.p[fd].flags & (O_ACCMODE | O_APPEND | O_DIRECT | O_NONBLOCK |
O_RANDOM | O_SEQUENTIAL);
} else if (cmd == F_SETFL) {
rc = sys_fcntl_nt_setfl(fd, &g_fds.p[fd].flags, g_fds.p[fd].mode, arg,
&g_fds.p[fd].handle);
rc = sys_fcntl_nt_setfl(fd, arg);
} else if (cmd == F_GETFD) {
if (g_fds.p[fd].flags & O_CLOEXEC) {
rc = FD_CLOEXEC;

View file

@ -19,7 +19,9 @@
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/stat.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/fmt/wintime.internal.h"
#include "libc/intrin/atomic.h"
@ -76,13 +78,47 @@ static textwindows long GetSizeOfReparsePoint(int64_t fh) {
return z;
}
textwindows int sys_fstat_nt(int64_t handle, struct stat *out_st) {
static textwindows int sys_fstat_nt_socket(int kind, struct stat *st) {
bzero(st, sizeof(*st));
st->st_blksize = 512;
st->st_mode = S_IFSOCK | 0666;
st->st_dev = 0x44444444;
st->st_ino = kind;
return 0;
}
textwindows int sys_fstat_nt_special(int kind, struct stat *st) {
bzero(st, sizeof(*st));
st->st_blksize = 512;
st->st_mode = S_IFCHR | 0666;
st->st_dev = 0x77777777;
st->st_ino = kind;
return 0;
}
textwindows int sys_fstat_nt(int fd, struct stat *st) {
if (fd + 0u >= g_fds.n) return ebadf();
switch (g_fds.p[fd].kind) {
case kFdEmpty:
return ebadf();
case kFdConsole:
case kFdDevNull:
return sys_fstat_nt_special(g_fds.p[fd].kind, st);
case kFdSocket:
return sys_fstat_nt_socket(g_fds.p[fd].kind, st);
default:
return sys_fstat_nt_handle(g_fds.p[fd].handle, st);
}
}
textwindows int sys_fstat_nt_handle(int64_t handle, struct stat *out_st) {
struct stat st = {0};
// Always set st_blksize to avoid divide by zero issues.
// The Linux kernel sets this for /dev/tty and similar too.
// TODO(jart): GetVolumeInformationByHandle?
st.st_blksize = 4096;
st.st_gid = st.st_uid = sys_getuid_nt();
// We'll use the "umask" to fake out the mode bits.
uint32_t umask = atomic_load_explicit(&__umask, memory_order_acquire);
@ -92,9 +128,13 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *out_st) {
break;
case kNtFileTypeChar:
st.st_mode = S_IFCHR | (0666 & ~umask);
st.st_dev = 0x66666666;
st.st_ino = handle;
break;
case kNtFileTypePipe:
st.st_mode = S_IFIFO | (0666 & ~umask);
st.st_dev = 0x55555555;
st.st_ino = handle;
break;
case kNtFileTypeDisk: {
struct NtByHandleFileInformation wst;
@ -126,7 +166,6 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *out_st) {
} else {
st.st_ctim = st.st_mtim;
}
st.st_gid = st.st_uid = sys_getuid_nt();
st.st_size = (wst.nFileSizeHigh + 0ull) << 32 | wst.nFileSizeLow;
st.st_dev = wst.dwVolumeSerialNumber;
st.st_ino = (wst.nFileIndexHigh + 0ull) << 32 | wst.nFileIndexLow;

View file

@ -52,7 +52,7 @@ int fstat(int fd, struct stat *st) {
} else if (IsMetal()) {
rc = sys_fstat_metal(fd, st);
} else if (IsWindows()) {
rc = sys_fstat_nt(__getfdhandleactual(fd), st);
rc = sys_fstat_nt(fd, st);
} else {
rc = enosys();
}

View file

@ -16,9 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/stat.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
@ -29,18 +28,43 @@
#include "libc/nt/enum/filesharemode.h"
#include "libc/nt/errors.h"
#include "libc/nt/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/errfuns.h"
textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st,
int flags) {
int rc, e;
int64_t fh;
uint32_t dwDesiredAccess;
// handle special files
if (startswith(path, "/dev/")) {
if (!strcmp(path + 5, "tty")) {
return sys_fstat_nt_special(kFdConsole, st);
} else if (!strcmp(path + 5, "null")) {
return sys_fstat_nt_special(kFdDevNull, st);
} else if (!strcmp(path + 5, "stdin")) {
return sys_fstat_nt(STDIN_FILENO, st);
} else if (!strcmp(path + 5, "stdout")) {
return sys_fstat_nt(STDOUT_FILENO, st);
} else if (!strcmp(path + 5, "stderr")) {
return sys_fstat_nt(STDERR_FILENO, st);
} else {
return enoent();
}
}
// convert path from utf-8 to utf-16
uint16_t path16[PATH_MAX];
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
if (__mkntpathat(dirfd, path, 0, path16) == -1) {
return -1;
}
// open an actual file
int rc;
int64_t fh;
int e = errno;
uint32_t dwDesiredAccess = kNtFileGenericRead;
BLOCK_SIGNALS;
e = errno;
dwDesiredAccess = kNtFileGenericRead;
TryAgain:
if ((fh = CreateFile(
path16, dwDesiredAccess,
@ -50,7 +74,7 @@ TryAgain:
((flags & AT_SYMLINK_NOFOLLOW) ? kNtFileFlagOpenReparsePoint
: 0),
0)) != -1) {
rc = st ? sys_fstat_nt(fh, st) : 0;
rc = st ? sys_fstat_nt_handle(fh, st) : 0;
CloseHandle(fh);
} else if (dwDesiredAccess == kNtFileGenericRead &&
GetLastError() == kNtErrorSharingViolation) {
@ -61,5 +85,7 @@ TryAgain:
rc = __winerr();
}
ALLOW_SIGNALS;
// mop up errors
return __fix_enotdir(rc, path16);
}

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/fmt/conv.h"
@ -36,6 +37,7 @@ textwindows int sys_getloadavg_nt(double *a, int n) {
int i, rc;
uint64_t elapsed, used;
struct NtFileTime idle, kern, user;
BLOCK_SIGNALS;
pthread_spin_lock(&lock);
if (GetSystemTimes(&idle, &kern, &user)) {
elapsed = (FT(kern) - FT(kern1)) + (FT(user) - FT(user1));
@ -53,6 +55,7 @@ textwindows int sys_getloadavg_nt(double *a, int n) {
rc = __winerr();
}
pthread_spin_unlock(&lock);
ALLOW_SIGNALS;
return rc;
}

View file

@ -20,15 +20,7 @@
#include "libc/intrin/atomic.h"
#include "libc/macros.internal.h"
#include "libc/nt/accounting.h"
static textwindows uint32_t __kmp32(const void *buf, size_t size) {
size_t i;
uint32_t h;
const uint32_t kPhiPrime = 0x9e3779b1;
const unsigned char *p = (const unsigned char *)buf;
for (h = i = 0; i < size; i++) h = (p[i] + h) * kPhiPrime;
return h;
}
#include "libc/str/str.h"
textwindows uint32_t sys_getuid_nt(void) {
char16_t buf[257];
@ -36,7 +28,7 @@ textwindows uint32_t sys_getuid_nt(void) {
uint32_t tmp, size = ARRAYLEN(buf);
if (!(tmp = atomic_load_explicit(&uid, memory_order_acquire))) {
GetUserName(&buf, &size);
tmp = __kmp32(buf, size >> 1) & 32767;
tmp = __fnv(buf, size >> 1) & 32767;
if (!tmp) ++tmp;
atomic_store_explicit(&uid, tmp, memory_order_release);
}

View file

@ -106,6 +106,9 @@ static int ioctl_fionread(int fd, uint32_t *arg) {
int bytes = CountConsoleInputBytes();
*arg = MAX(0, bytes);
return 0;
} else if (g_fds.p[fd].kind == kFdDevNull) {
*arg = 1;
return 0;
} else if (GetFileType(handle) == kNtFileTypePipe) {
uint32_t avail;
if (PeekNamedPipe(handle, 0, 0, 0, &avail, 0)) {

View file

@ -54,7 +54,7 @@ bool32 ischardev(int fd) {
return false;
}
} else {
return __isfdkind(fd, kFdConsole) ||
return __isfdkind(fd, kFdConsole) || __isfdkind(fd, kFdDevNull) ||
(__isfdkind(fd, kFdFile) &&
GetFileType(__getfdhandleactual(fd)) == kNtFileTypeChar);
}

View file

@ -62,7 +62,9 @@ static textwindows int64_t Seek(struct Fd *f, int64_t offset, int whence) {
}
textwindows int64_t sys_lseek_nt(int fd, int64_t offset, int whence) {
if (__isfdkind(fd, kFdFile)) {
if (__isfdkind(fd, kFdDevNull)) {
return offset;
} else if (__isfdkind(fd, kFdFile)) {
struct Fd *f = g_fds.p + fd;
int filetype = GetFileType(f->handle);
if (filetype != kNtFileTypePipe && filetype != kNtFileTypeChar) {

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/ntmagicpaths.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
@ -36,24 +35,6 @@ static inline int IsAlpha(int c) {
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
}
textwindows static const char *FixNtMagicPath(const char *path,
unsigned flags) {
const struct NtMagicPaths *mp = &kNtMagicPaths;
asm("" : "+r"(mp));
if (!IsSlash(path[0])) return path;
if (strcmp(path, mp->devtty) == 0) {
if ((flags & O_ACCMODE) == O_RDONLY) {
return mp->conin;
} else if ((flags & O_ACCMODE) == O_WRONLY) {
return mp->conout;
}
}
if (strcmp(path, mp->devnull) == 0) return mp->nul;
if (strcmp(path, mp->devstdin) == 0) return mp->conin;
if (strcmp(path, mp->devstdout) == 0) return mp->conout;
return path;
}
textwindows size_t __normntpath(char16_t *p, size_t n) {
size_t i, j;
for (j = i = 0; i < n; ++i) {
@ -116,7 +97,6 @@ textwindows int __mkntpath2(const char *path,
if (!path || (IsAsan() && !__asan_is_valid_str(path))) {
return efault();
}
path = FixNtMagicPath(path, flags);
size_t x, z;
char16_t *p = path16;

View file

@ -1,25 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 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/ntmagicpaths.internal.h"
const struct NtMagicPaths kNtMagicPaths = {
#define TAB(NAME, STRING) STRING,
#include "libc/calls/ntmagicpaths.inc"
#undef TAB
};

View file

@ -1,7 +0,0 @@
TAB(devtty, "/dev/tty")
TAB(devnull, "/dev/null")
TAB(devstdin, "/dev/stdin")
TAB(devstdout, "/dev/stdout")
TAB(nul, "NUL")
TAB(conin, "CONIN$")
TAB(conout, "CONOUT$")

View file

@ -1,16 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_NTMAGICPATHS_H_
#define COSMOPOLITAN_LIBC_CALLS_NTMAGICPATHS_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct NtMagicPaths {
#define TAB(NAME, STRING) char NAME[sizeof(STRING)];
#include "libc/calls/ntmagicpaths.inc"
#undef TAB
};
extern const struct NtMagicPaths kNtMagicPaths;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_NTMAGICPATHS_H_ */

View file

@ -18,8 +18,8 @@
*/
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/calls/ntmagicpaths.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
@ -131,24 +131,13 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path,
return __fix_enotdir(hand, path16);
}
static textwindows int sys_open_nt_console(int dirfd,
const struct NtMagicPaths *mp,
uint32_t flags, int32_t mode,
size_t fd) {
g_fds.p[fd].kind = kFdConsole;
g_fds.p[fd].flags = flags;
g_fds.p[fd].mode = mode;
g_fds.p[fd].handle =
CreateFile(u"CONIN$", kNtGenericRead | kNtGenericWrite, kNtFileShareRead,
&kNtIsInheritable, kNtOpenExisting, 0, 0);
return fd;
}
static textwindows int sys_open_nt_file(int dirfd, const char *file,
uint32_t flags, int32_t mode,
size_t fd) {
if ((g_fds.p[fd].handle = sys_open_nt_impl(dirfd, file, flags, mode,
kNtFileFlagOverlapped)) != -1) {
int64_t handle;
if ((handle = sys_open_nt_impl(dirfd, file, flags, mode,
kNtFileFlagOverlapped)) != -1) {
g_fds.p[fd].handle = handle;
g_fds.p[fd].kind = kFdFile;
g_fds.p[fd].flags = flags;
g_fds.p[fd].mode = mode;
@ -158,15 +147,61 @@ static textwindows int sys_open_nt_file(int dirfd, const char *file,
}
}
static textwindows int sys_open_nt_special(int fd, int flags, int mode,
int kind, const char16_t *name) {
g_fds.p[fd].kind = kind;
g_fds.p[fd].mode = mode;
g_fds.p[fd].flags = flags;
g_fds.p[fd].handle = CreateFile(name, kNtGenericRead | kNtGenericWrite,
kNtFileShareRead | kNtFileShareWrite,
&kNtIsInheritable, kNtOpenExisting, 0, 0);
return fd;
}
static textwindows int sys_open_nt_dup(int fd, int flags, int mode, int oldfd) {
int64_t handle;
if (!__isfdopen(oldfd)) {
return enoent();
}
if (DuplicateHandle(GetCurrentProcess(), g_fds.p[oldfd].handle,
GetCurrentProcess(), &handle, 0, true,
kNtDuplicateSameAccess)) {
g_fds.p[fd] = g_fds.p[oldfd];
g_fds.p[fd].handle = handle;
g_fds.p[fd].mode = mode;
if (!sys_fcntl_nt_setfl(fd, flags)) {
return fd;
} else {
CloseHandle(handle);
return -1;
}
} else {
return __winerr();
}
}
textwindows int sys_open_nt(int dirfd, const char *file, uint32_t flags,
int32_t mode) {
int fd;
ssize_t rc;
BLOCK_SIGNALS;
__fds_lock();
if (!(flags & O_CREAT)) mode = 0;
if ((rc = fd = __reservefd_unlocked(-1)) != -1) {
if (!strcmp(file, kNtMagicPaths.devtty)) {
rc = sys_open_nt_console(dirfd, &kNtMagicPaths, flags, mode, fd);
if (startswith(file, "/dev/")) {
if (!strcmp(file + 5, "tty")) {
rc = sys_open_nt_special(fd, flags, mode, kFdConsole, u"CONIN$");
} else if (!strcmp(file + 5, "null")) {
rc = sys_open_nt_special(fd, flags, mode, kFdDevNull, u"NUL");
} else if (!strcmp(file + 5, "stdin")) {
rc = sys_open_nt_dup(fd, flags, mode, STDIN_FILENO);
} else if (!strcmp(file + 5, "stdout")) {
rc = sys_open_nt_dup(fd, flags, mode, STDOUT_FILENO);
} else if (!strcmp(file + 5, "stderr")) {
rc = sys_open_nt_dup(fd, flags, mode, STDERR_FILENO);
} else {
rc = enoent();
}
} else {
rc = sys_open_nt_file(dirfd, file, flags, mode, fd);
}
@ -175,5 +210,6 @@ textwindows int sys_open_nt(int dirfd, const char *file, uint32_t flags,
}
__fds_unlock();
}
ALLOW_SIGNALS;
return rc;
}

View file

@ -27,11 +27,9 @@
int sys_openat(int dirfd, const char *file, int flags, unsigned mode) {
static bool once, modernize;
int d, e, f;
/*
* RHEL5 doesn't support O_CLOEXEC. It's hard to test for this.
* Sometimes the system call succeeds and it just doesn't set the
* flag. Other times, it return -530 which makes no sense.
*/
// RHEL5 doesn't support O_CLOEXEC. It's hard to test for this.
// Sometimes the system call succeeds and it just doesn't set the
// flag. Other times, it return -530 which makes no sense.
if (!IsLinux() || !(flags & O_CLOEXEC) || modernize) {
d = __sys_openat(dirfd, file, flags, mode);
} else if (once) {

View file

@ -47,13 +47,9 @@ static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask,
om = __sig_beginwait(waitmask);
if ((rc = _check_cancel()) != -1 && (rc = _check_signal(restartable)) != -1) {
unassert((wi = WaitForSingleObject(sem, msdelay)) != -1u);
if (wi != kNtWaitTimeout) {
_check_signal(false);
rc = eintr();
_check_cancel();
} else if ((rc = _check_signal(restartable))) {
_check_cancel();
}
if (restartable && !(pt->pt_flags & PT_RESTARTABLE)) rc = eintr();
rc |= _check_signal(restartable);
if (rc == -1 && errno == EINTR) _check_cancel();
}
__sig_finishwait(om);
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_CPU, memory_order_release);

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#ifdef __x86_64__

View file

@ -58,10 +58,10 @@
// Polls on the New Technology.
//
// This function is used to implement poll() and select(). You may poll
// on both sockets and files at the same time. We also poll for signals
// while poll is polling.
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
const sigset_t *sigmask) {
// on sockets, files and the console at the same time. We also poll for
// both signals and posix thread cancelation, while the poll is polling
static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
uint32_t *ms, sigset_t sigmask) {
bool ok;
uint64_t millis;
uint32_t cm, avail, waitfor;
@ -72,7 +72,6 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
struct timespec started, deadline, remain, now;
int i, rc, sn, pn, gotinvals, gotpipes, gotsocks;
BLOCK_SIGNALS;
started = timespec_real();
deadline = timespec_add(started, timespec_frommillis(ms ? *ms : -1u));
@ -128,7 +127,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
__fds_unlock();
if (rc) {
// failed to create a polling solution
goto Finished;
return rc;
}
// perform the i/o and sleeping and looping
@ -170,8 +169,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
// compute a small time slice we don't mind sleeping for
if (sn) {
if ((gotsocks = WSAPoll(sockfds, sn, 0)) == -1) {
rc = __winsockerr();
goto Finished;
return __winsockerr();
}
} else {
gotsocks = 0;
@ -190,8 +188,8 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
if (waitfor) {
POLLTRACE("poll() sleeping for %'d out of %'lu ms", waitfor,
timespec_tomillis(remain));
if ((rc = _park_norestart(waitfor, sigmask ? *sigmask : 0)) == -1) {
goto Finished; // eintr, ecanceled, etc.
if ((rc = _park_norestart(waitfor, sigmask)) == -1) {
return -1; // eintr, ecanceled, etc.
}
}
}
@ -221,9 +219,14 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
}
// and finally return
rc = gotinvals + gotpipes + gotsocks;
return gotinvals + gotpipes + gotsocks;
}
Finished:
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
const sigset_t *sigmask) {
int rc;
BLOCK_SIGNALS;
rc = sys_poll_nt_impl(fds, nfds, ms, sigmask ? *sigmask : 0);
ALLOW_SIGNALS;
return rc;
}

View file

@ -72,7 +72,7 @@ ssize_t pread(int fd, void *buf, size_t size, int64_t offset) {
rc = sys_pread(fd, buf, size, offset, offset);
} else if (__isfdkind(fd, kFdSocket)) {
rc = espipe();
} else if (__isfdkind(fd, kFdFile)) {
} else if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdDevNull)) {
rc = sys_read_nt(fd, (struct iovec[]){{buf, size}}, 1, offset);
} else {
rc = ebadf();

View file

@ -32,6 +32,8 @@ static const char *__fdkind2str(int x) {
return "kFdSocket";
case kFdConsole:
return "kFdConsole";
case kFdDevNull:
return "kFdDevNull";
case kFdSerial:
return "kFdSerial";
case kFdZip:

View file

@ -65,7 +65,7 @@ ssize_t pwrite(int fd, const void *buf, size_t size, int64_t offset) {
rc = sys_pwrite(fd, buf, size, offset, offset);
} else if (__isfdkind(fd, kFdSocket)) {
rc = espipe();
} else if (__isfdkind(fd, kFdFile)) {
} else if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdDevNull)) {
rc = sys_write_nt(fd, (struct iovec[]){{(void *)buf, size}}, 1, offset);
} else {
return ebadf();

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/atomic.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
@ -733,8 +734,10 @@ static textwindows int WaitForConsole(struct Fd *f, sigset_t waitmask) {
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_SEM, memory_order_release);
m = __sig_beginwait(waitmask);
if ((rc = _check_cancel()) != -1 && (rc = _check_signal(true)) != -1) {
WaitForMultipleObjects(2, (int64_t[2]){sem, __keystroke.cin}, 0, ms);
int64_t hands[2] = {sem, __keystroke.cin};
unassert(WaitForMultipleObjects(2, hands, 0, ms) != -1u);
if (~pt->pt_flags & PT_RESTARTABLE) rc = eintr();
rc |= _check_signal(true);
if (rc == -1 && errno == EINTR) _check_cancel();
}
__sig_finishwait(m);
@ -763,6 +766,11 @@ textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size,
// switch to terminal polyfill if reading from win32 console
struct Fd *f = g_fds.p + fd;
if (f->kind == kFdDevNull) {
return 0;
}
if (f->kind == kFdConsole) {
return ReadFromConsole(f, data, size, waitmask);
}

View file

@ -28,6 +28,7 @@ textwindows ssize_t sys_readv_nt(int fd, const struct iovec *iov, int iovlen) {
switch (g_fds.p[fd].kind) {
case kFdFile:
case kFdConsole:
case kFdDevNull:
return sys_read_nt(fd, iov, iovlen, -1);
case kFdSocket:
return _weaken(sys_recv_nt)(fd, iov, iovlen, 0);

View file

@ -83,7 +83,9 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
// similar to the lseek() system call, they too raise ESPIPE when
// operating on a non-seekable file.
bool pwriting = offset != -1;
bool seekable = f->kind == kFdFile && GetFileType(handle) == kNtFileTypeDisk;
bool seekable =
(f->kind == kFdFile && GetFileType(handle) == kNtFileTypeDisk) ||
f->kind == kFdDevNull;
if (pwriting && !seekable) {
return espipe();
}

71
libc/calls/setfl.c Normal file
View file

@ -0,0 +1,71 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net 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/internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/nt/createfile.h"
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/sysv/consts/o.h"
textwindows int sys_fcntl_nt_setfl(int fd, unsigned flags) {
// you may change the following:
//
// - O_NONBLOCK make read() raise EAGAIN
// - O_APPEND for toggling append mode
// - O_RANDOM alt. for posix_fadvise()
// - O_SEQUENTIAL alt. for posix_fadvise()
// - O_DIRECT works but haven't tested
//
// the other bits are ignored.
unsigned allowed = O_APPEND | O_SEQUENTIAL | O_RANDOM | O_DIRECT | O_NONBLOCK;
unsigned needreo = O_APPEND | O_SEQUENTIAL | O_RANDOM | O_DIRECT;
unsigned newflag = (g_fds.p[fd].flags & ~allowed) | (flags & allowed);
if (g_fds.p[fd].kind == kFdFile &&
((g_fds.p[fd].flags & needreo) ^ (flags & needreo))) {
unsigned perm, share, attr;
if (GetNtOpenFlags(newflag, g_fds.p[fd].mode, &perm, &share, 0, &attr) ==
-1) {
return -1;
}
// MSDN says only these are allowed, otherwise it returns EINVAL.
attr &= kNtFileFlagBackupSemantics | kNtFileFlagDeleteOnClose |
kNtFileFlagNoBuffering | kNtFileFlagOpenNoRecall |
kNtFileFlagOpenReparsePoint | kNtFileFlagOverlapped |
kNtFileFlagPosixSemantics | kNtFileFlagRandomAccess |
kNtFileFlagSequentialScan | kNtFileFlagWriteThrough;
intptr_t hand;
if ((hand = ReOpenFile(g_fds.p[fd].handle, perm, share, attr)) != -1) {
if (hand != g_fds.p[fd].handle) {
CloseHandle(g_fds.p[fd].handle);
g_fds.p[fd].handle = hand;
}
} else {
return __winerr();
}
}
// 1. ignore flags that aren't access mode flags
// 2. return zero if nothing's changed
g_fds.p[fd].flags = newflag;
return 0;
}

View file

@ -88,11 +88,13 @@ textwindows bool __sig_ignored(int sig) {
textwindows void __sig_delete(int sig) {
struct Dll *e;
__sig.pending &= ~(1ull << (sig - 1));
BLOCK_SIGNALS;
_pthread_lock();
for (e = dll_last(_pthread_list); e; e = dll_prev(_pthread_list, e)) {
POSIXTHREAD_CONTAINER(e)->tib->tib_sigpending &= ~(1ull << (sig - 1));
}
_pthread_unlock();
ALLOW_SIGNALS;
}
static textwindows bool __sig_should_use_altstack(unsigned flags,
@ -219,7 +221,7 @@ textwindows void __sig_cancel(struct PosixThread *pt, int sig, unsigned flags) {
}
static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
++__sig.count;
atomic_fetch_add_explicit(&__sig.count, 1, memory_order_relaxed);
int sig = sf->si.si_signo;
sigset_t blocksigs = __sighandmask[sig];
if (!(sf->flags & SA_NODEFER)) blocksigs |= 1ull << (sig - 1);
@ -309,7 +311,9 @@ static int __sig_killer(struct PosixThread *pt, int sig, int sic) {
textwindows int __sig_kill(struct PosixThread *pt, int sig, int sic) {
int rc;
BLOCK_SIGNALS;
_pthread_ref(pt);
rc = __sig_killer(pt, sig, sic);
_pthread_unref(pt);
ALLOW_SIGNALS;
return rc;
}
@ -333,7 +337,7 @@ textwindows void __sig_generate(int sig, int sic) {
atomic_load_explicit(&pt->pt_status, memory_order_acquire) <
kPosixThreadTerminated &&
!(pt->tib->tib_sigmask & (1ull << (sig - 1)))) {
mark = pt;
_pthread_ref((mark = pt));
break;
}
}
@ -345,6 +349,7 @@ textwindows void __sig_generate(int sig, int sic) {
STRACE("all threads block %G so adding to pending signals of process", sig);
__sig.pending |= 1ull << (sig - 1);
}
_pthread_unref(mark);
ALLOW_SIGNALS;
}
@ -411,7 +416,7 @@ static void __sig_unmaskable(struct NtExceptionPointers *ep, int code, int sig,
struct CosmoTib *tib) {
// increment the signal count for getrusage()
++__sig.count;
atomic_fetch_add_explicit(&__sig.count, 1, memory_order_relaxed);
// log vital crash information reliably for --strace before doing much
// we don't print this without the flag since raw numbers scare people

View file

@ -18,14 +18,11 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/str/str.h"
/**
* Configures process to ignore signal.
*/
int sigignore(int sig) {
struct sigaction sa;
bzero(&sa, sizeof(sa));
sa.sa_handler = SIG_IGN;
struct sigaction sa = {.sa_handler = SIG_IGN};
return sigaction(sig, &sa, 0);
}

View file

@ -21,6 +21,7 @@
#include "libc/calls/struct/sigset.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/errfuns.h"
@ -53,7 +54,8 @@ int sigpending(sigset_t *pending) {
}
rc = 0;
} else if (IsWindows()) {
*pending = __sig.pending | __get_tls()->tib_sigpending;
*pending = atomic_load_explicit(&__sig.pending, memory_order_acquire) |
__get_tls()->tib_sigpending;
rc = 0;
} else {
rc = enosys();

View file

@ -1,6 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_STATE_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_STATE_INTERNAL_H_
#include "libc/calls/calls.h"
#include "libc/nt/struct/securityattributes.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)

View file

@ -11,6 +11,7 @@ COSMOPOLITAN_C_START_
#define kFdZip 6
#define kFdEpoll 7
#define kFdReserved 8
#define kFdDevNull 9
struct Fd {
char kind;

View file

@ -7,7 +7,9 @@ COSMOPOLITAN_C_START_
int sys_fstat(int, struct stat *);
int sys_fstatat(int, const char *, struct stat *, int);
int sys_fstat_nt(int64_t, struct stat *);
int sys_fstat_nt(int, struct stat *);
int sys_fstat_nt_special(int, struct stat *);
int sys_fstat_nt_handle(int64_t, struct stat *);
int sys_fstatat_nt(int, const char *, struct stat *, int);
int sys_lstat_nt(const char *, struct stat *);
int sys_fstat_metal(int, struct stat *);

View file

@ -16,6 +16,7 @@ int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX], int);
int __mkntpathat(int, const char *, int, char16_t[hasatleast PATH_MAX]);
int __mkntpathath(int64_t, const char *, int, char16_t[hasatleast PATH_MAX]);
int ntaccesscheck(const char16_t *, uint32_t) paramsnonnull();
int sys_fcntl_nt_setfl(int, unsigned);
int sys_pause_nt(void);
int64_t __fix_enotdir(int64_t, char16_t *);
int64_t __fix_enotdir3(int64_t, char16_t *, char16_t *);

View file

@ -27,6 +27,7 @@ textwindows ssize_t sys_writev_nt(int fd, const struct iovec *iov, int iovlen) {
switch (g_fds.p[fd].kind) {
case kFdFile:
case kFdConsole:
case kFdDevNull:
return sys_write_nt(fd, iov, iovlen, -1);
case kFdSocket:
return _weaken(sys_send_nt)(fd, iov, iovlen, 0);