mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-30 22:40:30 +00:00
Make improvements for Actually Portable Emacs
- Get SIGWINCH working again on the New Technology - Correctly handle O_NOFOLLOW in open() on Windows - Implement synthetic umask() functionality on Windows - Do a better job managing file execute access on Windows - Fill in `st_uid` and `st_gid` with username hash on Windows - Munge UNICODE control pictures into control codes on Windows - Do a better job ensuring Windows console settings are restored - Introduce KPRINTF_LOG environment variable to log kprintf to a file
This commit is contained in:
parent
9c7b81ee0f
commit
965516e313
108 changed files with 1126 additions and 807 deletions
|
@ -26,6 +26,7 @@
|
|||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
|
@ -35,6 +36,7 @@
|
|||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
/**
|
||||
|
@ -179,8 +181,16 @@ textwindows bool __sig_handle(int sigops, int sig, int si_code,
|
|||
switch (__sighandrvas[sig]) {
|
||||
case (intptr_t)SIG_DFL:
|
||||
if (__sig_is_fatal(sig)) {
|
||||
STRACE("terminating on %G", sig);
|
||||
_restorewintty();
|
||||
size_t len;
|
||||
char name[16];
|
||||
strsignal_r(sig, name);
|
||||
len = strlen(name);
|
||||
name[len++] = '\n';
|
||||
WriteFile(GetStdHandle(kNtStdErrorHandle), name, len, 0, 0);
|
||||
STRACE("terminating on %s", name);
|
||||
if (_weaken(__restore_console_win32)) {
|
||||
_weaken(__restore_console_win32)();
|
||||
}
|
||||
ExitProcess(sig);
|
||||
}
|
||||
// fallthrough
|
||||
|
|
|
@ -34,7 +34,7 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags,
|
|||
for (;;) {
|
||||
if (sys_clock_gettime_nt(clock, &now)) return -1;
|
||||
if (timespec_cmp(now, abs) >= 0) return 0;
|
||||
if (_check_interrupts(0, g_fds.p)) return -1;
|
||||
if (_check_interrupts(0)) return -1;
|
||||
SleepEx(MIN(__SIG_POLLING_INTERVAL_MS,
|
||||
timespec_tomillis(timespec_sub(abs, now))),
|
||||
false);
|
||||
|
@ -45,7 +45,7 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags,
|
|||
for (;;) {
|
||||
sys_clock_gettime_nt(clock, &now);
|
||||
if (timespec_cmp(now, abs) >= 0) return 0;
|
||||
if (_check_interrupts(0, g_fds.p)) {
|
||||
if (_check_interrupts(0)) {
|
||||
if (rem) *rem = timespec_sub(abs, now);
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -23,11 +23,12 @@
|
|||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
#include "libc/sock/syscall_fd.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
|
||||
/**
|
||||
* Closes file descriptor.
|
||||
|
@ -59,6 +60,8 @@ int close(int fd) {
|
|||
if (fd < 0) {
|
||||
rc = ebadf();
|
||||
} else {
|
||||
// helps guarantee stderr log gets duplicated before user closes
|
||||
if (_weaken(kloghandle)) _weaken(kloghandle)();
|
||||
// for performance reasons we want to avoid holding __fds_lock()
|
||||
// while sys_close() is happening. this leaves the kernel / libc
|
||||
// having a temporarily inconsistent state. routines that obtain
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/enum/filesharemode.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
// code size optimization
|
||||
// <sync libc/sysv/consts.sh>
|
||||
|
@ -34,69 +35,113 @@
|
|||
#define _O_DIRECTORY 0x00010000 // kNtFileFlagBackupSemantics
|
||||
#define _O_TMPFILE 0x00410000 // AttributeTemporary|FlagDeleteOnClose
|
||||
#define _O_DIRECT 0x00004000 // kNtFileFlagNoBuffering
|
||||
#define _O_NDELAY 0x00000800 // kNtFileFlagWriteThrough
|
||||
#define _O_NONBLOCK 0x00000800 // kNtFileFlagWriteThrough
|
||||
#define _O_RANDOM 0x80000000 // kNtFileFlagRandomAccess
|
||||
#define _O_SEQUENTIAL 0x40000000 // kNtFileFlagSequentialScan
|
||||
#define _O_COMPRESSED 0x20000000 // kNtFileAttributeCompressed
|
||||
#define _O_INDEXED 0x10000000 // !kNtFileAttributeNotContentIndexed
|
||||
#define _O_NONBLOCK 0x00000800
|
||||
#define _O_CLOEXEC 0x00080000
|
||||
// </sync libc/sysv/consts.sh>
|
||||
|
||||
textwindows int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm,
|
||||
uint32_t *out_share, uint32_t *out_disp,
|
||||
uint32_t *out_attr) {
|
||||
bool is_creating_file;
|
||||
uint32_t perm, share, disp, attr;
|
||||
|
||||
if (flags &
|
||||
~(O_ACCMODE | _O_APPEND | _O_CREAT | _O_EXCL | _O_TRUNC | _O_DIRECTORY |
|
||||
_O_TMPFILE | _O_NONBLOCK | _O_RANDOM | _O_SEQUENTIAL | _O_COMPRESSED |
|
||||
_O_INDEXED | _O_CLOEXEC | _O_DIRECT)) {
|
||||
return einval();
|
||||
}
|
||||
|
||||
switch (flags & O_ACCMODE) {
|
||||
case O_RDONLY:
|
||||
perm = kNtFileGenericRead | kNtGenericExecute;
|
||||
perm = kNtFileGenericRead;
|
||||
break;
|
||||
case O_WRONLY:
|
||||
perm = kNtFileGenericWrite | kNtGenericExecute;
|
||||
perm = kNtFileGenericWrite;
|
||||
break;
|
||||
case O_RDWR:
|
||||
perm = kNtFileGenericRead | kNtFileGenericWrite | kNtGenericExecute;
|
||||
perm = kNtFileGenericRead | kNtFileGenericWrite;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
return einval();
|
||||
}
|
||||
if (flags & _O_APPEND) {
|
||||
perm = kNtFileAppendData;
|
||||
perm = kNtFileAppendData; // todo: this is part of generic write above
|
||||
}
|
||||
|
||||
attr = 0;
|
||||
is_creating_file = (flags & _O_CREAT) || (flags & _O_TMPFILE) == _O_TMPFILE;
|
||||
|
||||
// POSIX O_EXEC doesn't mean the same thing as kNtGenericExecute. We
|
||||
// request execute access when we can determine it from mode's bits.
|
||||
// When opening existing files we greedily request execute access so
|
||||
// mmap() won't fail. If it causes CreateFile() to fail, our wrapper
|
||||
// will try calling CreateFile a second time without execute access.
|
||||
if (is_creating_file) {
|
||||
if (mode & 0111) {
|
||||
perm |= kNtGenericExecute;
|
||||
}
|
||||
if (~mode & 0200) {
|
||||
attr |= kNtFileAttributeReadonly; // not sure why anyone would
|
||||
}
|
||||
} else {
|
||||
perm |= kNtGenericExecute;
|
||||
}
|
||||
|
||||
// Be as generous as possible in sharing file access. Not doing this
|
||||
// you'll quickly find yourself no longer able to administer Windows
|
||||
if (flags & _O_EXCL) {
|
||||
share = kNtFileShareExclusive;
|
||||
} else {
|
||||
share = kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete;
|
||||
}
|
||||
|
||||
if ((flags & _O_CREAT) && (flags & _O_EXCL)) {
|
||||
disp = kNtCreateNew;
|
||||
} else if ((flags & _O_CREAT) && (flags & _O_TRUNC)) {
|
||||
disp = kNtCreateAlways;
|
||||
} else if (flags & _O_CREAT) {
|
||||
disp = kNtOpenAlways;
|
||||
// These POSIX to WIN32 mappings are relatively straightforward.
|
||||
if (flags & _O_EXCL) {
|
||||
if (is_creating_file) {
|
||||
disp = kNtCreateNew;
|
||||
} else {
|
||||
return einval();
|
||||
}
|
||||
} else if (is_creating_file) {
|
||||
if (flags & _O_TRUNC) {
|
||||
disp = kNtCreateAlways;
|
||||
} else {
|
||||
disp = kNtOpenAlways;
|
||||
}
|
||||
} else if (flags & _O_TRUNC) {
|
||||
disp = kNtTruncateExisting;
|
||||
} else {
|
||||
disp = kNtOpenExisting;
|
||||
}
|
||||
|
||||
// Please use tmpfd() or tmpfile() instead of O_TMPFILE.
|
||||
if ((flags & _O_TMPFILE) == _O_TMPFILE) {
|
||||
attr = kNtFileAttributeTemporary | kNtFileFlagDeleteOnClose;
|
||||
attr |= kNtFileAttributeTemporary | kNtFileFlagDeleteOnClose;
|
||||
} else {
|
||||
attr = kNtFileAttributeNormal;
|
||||
attr |= kNtFileAttributeNormal;
|
||||
if (flags & _O_DIRECTORY) {
|
||||
attr |= kNtFileFlagBackupSemantics;
|
||||
}
|
||||
if (~mode & 0200) {
|
||||
attr |= kNtFileAttributeReadonly;
|
||||
}
|
||||
}
|
||||
|
||||
if (~flags & _O_INDEXED) attr |= kNtFileAttributeNotContentIndexed;
|
||||
if (flags & _O_COMPRESSED) attr |= kNtFileAttributeCompressed;
|
||||
// The Windows content indexing service ravages performance similar to
|
||||
// Windows Defender. Please pass O_INDEXED to openat() to enable this.
|
||||
if (~flags & _O_INDEXED) {
|
||||
attr |= kNtFileAttributeNotContentIndexed;
|
||||
}
|
||||
|
||||
// Windows' transparent filesystem compression is really cool, as such
|
||||
// we've introduced a nonstandard O_COMPRESSED flag to help you use it
|
||||
if (flags & _O_COMPRESSED) {
|
||||
attr |= kNtFileAttributeCompressed;
|
||||
}
|
||||
|
||||
// Not certain yet what benefit these flags offer.
|
||||
if (flags & _O_SEQUENTIAL) attr |= kNtFileFlagSequentialScan;
|
||||
if (flags & _O_RANDOM) attr |= kNtFileFlagRandomAccess;
|
||||
if (flags & _O_DIRECT) attr |= kNtFileFlagNoBuffering;
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
|
@ -60,6 +62,8 @@
|
|||
*/
|
||||
int dup2(int oldfd, int newfd) {
|
||||
int rc;
|
||||
// helps guarantee stderr log gets duplicated before user closes
|
||||
if (_weaken(kloghandle)) _weaken(kloghandle)();
|
||||
if (__isfdkind(oldfd, kFdZip)) {
|
||||
rc = enotsup();
|
||||
#ifdef __aarch64__
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
|
@ -59,6 +61,8 @@
|
|||
*/
|
||||
int dup3(int oldfd, int newfd, int flags) {
|
||||
int rc;
|
||||
// helps guarantee stderr log gets duplicated before user closes
|
||||
if (_weaken(kloghandle)) _weaken(kloghandle)();
|
||||
if (oldfd == newfd || (flags & ~O_CLOEXEC)) {
|
||||
rc = einval(); // NetBSD doesn't do this
|
||||
} else if (oldfd < 0 || newfd < 0) {
|
||||
|
|
|
@ -45,6 +45,8 @@
|
|||
#include "libc/sysv/consts/ok.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
extern long __klog_handle;
|
||||
|
||||
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
|
||||
__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject;
|
||||
__msabi extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess;
|
||||
|
@ -115,6 +117,12 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
|
|||
startinfo.hStdOutput = g_fds.p[1].handle;
|
||||
startinfo.hStdError = g_fds.p[2].handle;
|
||||
|
||||
if (_weaken(__klog_handle) && //
|
||||
*_weaken(__klog_handle) != 0 && //
|
||||
*_weaken(__klog_handle) != -1) {
|
||||
__imp_CloseHandle(*_weaken(__klog_handle));
|
||||
}
|
||||
|
||||
// spawn the process
|
||||
rc = ntspawn(program, argv, envp, v, 0, 0, true, 0, 0, &startinfo, &procinfo);
|
||||
if (rc == -1) {
|
||||
|
|
|
@ -23,6 +23,6 @@
|
|||
textwindows int sys_fdatasync_nt(int fd) {
|
||||
// TODO(jart): what should we do with worker pipes?
|
||||
if (!__isfdkind(fd, kFdFile)) return ebadf();
|
||||
if (_check_interrupts(0, 0)) return -1;
|
||||
if (_check_interrupts(0)) return -1;
|
||||
return FlushFileBuffers(g_fds.p[fd].handle) ? 0 : -1;
|
||||
}
|
||||
|
|
|
@ -17,9 +17,11 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/bsr.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
@ -70,25 +72,27 @@ static textwindows uint32_t GetSizeOfReparsePoint(int64_t fh) {
|
|||
|
||||
textwindows int sys_fstat_nt(int64_t handle, struct stat *st) {
|
||||
int filetype;
|
||||
uint32_t umask;
|
||||
uint64_t actualsize;
|
||||
struct NtFileCompressionInfo fci;
|
||||
struct NtByHandleFileInformation wst;
|
||||
if (!st) return efault();
|
||||
if ((filetype = GetFileType(handle))) {
|
||||
bzero(st, sizeof(*st));
|
||||
umask = atomic_load_explicit(&__umask, memory_order_acquire);
|
||||
switch (filetype) {
|
||||
case kNtFileTypeChar:
|
||||
st->st_mode = S_IFCHR | 0644;
|
||||
st->st_mode = S_IFCHR | (0666 & ~umask);
|
||||
break;
|
||||
case kNtFileTypePipe:
|
||||
st->st_mode = S_IFIFO | 0644;
|
||||
st->st_mode = S_IFIFO | (0666 & ~umask);
|
||||
break;
|
||||
case kNtFileTypeDisk:
|
||||
if (GetFileInformationByHandle(handle, &wst)) {
|
||||
st->st_mode = 0555;
|
||||
st->st_mode = 0555 & ~umask;
|
||||
st->st_flags = wst.dwFileAttributes;
|
||||
if (!(wst.dwFileAttributes & kNtFileAttributeReadonly)) {
|
||||
st->st_mode |= 0200;
|
||||
st->st_mode |= 0222 & ~umask;
|
||||
}
|
||||
if (wst.dwFileAttributes & kNtFileAttributeDirectory) {
|
||||
st->st_mode |= S_IFDIR;
|
||||
|
@ -101,6 +105,7 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *st) {
|
|||
st->st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime);
|
||||
st->st_ctim = FileTimeToTimeSpec(wst.ftCreationFileTime);
|
||||
st->st_birthtim = st->st_ctim;
|
||||
st->st_gid = st->st_uid = __synthesize_uid();
|
||||
st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow;
|
||||
st->st_blksize = 4096;
|
||||
st->st_dev = wst.dwVolumeSerialNumber;
|
||||
|
|
|
@ -30,6 +30,10 @@
|
|||
/**
|
||||
* Returns information about file, via open()'d descriptor.
|
||||
*
|
||||
* On Windows, this implementation always sets `st_uid` and `st_gid` to
|
||||
* `getuid()` and `getgid()`. The `st_mode` field is generated based on
|
||||
* the current umask().
|
||||
*
|
||||
* @return 0 on success or -1 w/ errno
|
||||
* @raise EBADF if `fd` isn't a valid file descriptor
|
||||
* @raise EIO if an i/o error happens while reading from file system
|
||||
|
@ -43,14 +47,14 @@ int fstat(int fd, struct stat *st) {
|
|||
} else if (__isfdkind(fd, kFdZip)) {
|
||||
rc = _weaken(__zipos_fstat)(
|
||||
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, st);
|
||||
} else if (!IsWindows() && !IsMetal()) {
|
||||
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) {
|
||||
rc = sys_fstat(fd, st);
|
||||
} else if (IsMetal()) {
|
||||
rc = sys_fstat_metal(fd, st);
|
||||
} else if (!__isfdkind(fd, kFdFile)) {
|
||||
rc = ebadf();
|
||||
} else {
|
||||
} else if (IsWindows()) {
|
||||
rc = sys_fstat_nt(__getfdhandleactual(fd), st);
|
||||
} else {
|
||||
rc = enosys();
|
||||
}
|
||||
STRACE("fstat(%d, [%s]) → %d% m", fd, DescribeStat(rc, st), rc);
|
||||
return rc;
|
||||
|
|
|
@ -43,10 +43,26 @@ static inline const char *__strace_fstatat_flags(char buf[12], int flags) {
|
|||
/**
|
||||
* Returns information about thing.
|
||||
*
|
||||
* @param dirfd is normally AT_FDCWD but if it's an open directory and
|
||||
* file is a relative path, then file becomes relative to dirfd
|
||||
* On Windows, this implementation always sets `st_uid` and `st_gid` to
|
||||
* `getuid()` and `getgid()`. Anyone who relies upon the information to
|
||||
* secure a multi-tenant personal computer should consider improving it
|
||||
* and further note that the `st_mode` group / other bits will be clear
|
||||
*
|
||||
* @param dirfd is normally `AT_FDCWD` but if it's an open directory and
|
||||
* file is a relative path, then `path` becomes relative to `dirfd`
|
||||
* @param st is where the result is stored
|
||||
* @param flags can have `AT_SYMLINK_NOFOLLOW`
|
||||
* @param st is where result is stored
|
||||
* @param flags can have AT_SYMLINK_NOFOLLOW
|
||||
* @raise EACCES if denied access to component in path prefix
|
||||
* @raise EIO if i/o error occurred while reading from filesystem
|
||||
* @raise ELOOP if a symbolic link loop exists in `path`
|
||||
* @raise ENAMETOOLONG if a component in `path` exceeds `NAME_MAX`
|
||||
* @raise ENOENT on empty string or if component in path doesn't exist
|
||||
* @raise ENOTDIR if a parent component existed that wasn't a directory
|
||||
* @raise ENOTDIR if `path` is relative and `dirfd` isn't an open directory
|
||||
* @raise EOVERFLOW shouldn't be possible on 64-bit systems
|
||||
* @raise ELOOP may ahappen if `SYMLOOP_MAX` symlinks were dereferenced
|
||||
* @raise ENAMETOOLONG may happen if `path` exceeded `PATH_MAX`
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @see S_ISDIR(st.st_mode), S_ISREG()
|
||||
* @asyncsignalsafe
|
||||
|
@ -70,10 +86,12 @@ int fstatat(int dirfd, const char *path, struct stat *st, int flags) {
|
|||
} else {
|
||||
rc = enotsup();
|
||||
}
|
||||
} else if (!IsWindows()) {
|
||||
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) {
|
||||
rc = sys_fstatat(dirfd, path, st, flags);
|
||||
} else {
|
||||
} else if (IsWindows()) {
|
||||
rc = sys_fstatat_nt(dirfd, path, st, flags);
|
||||
} else {
|
||||
rc = enosys();
|
||||
}
|
||||
STRACE("fstatat(%s, %#s, [%s], %s) → %d% m", DescribeDirfd(dirfd), path,
|
||||
DescribeStat(rc, st), __strace_fstatat_flags(alloca(12), flags), rc);
|
||||
|
|
|
@ -17,9 +17,11 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
@ -27,7 +29,7 @@
|
|||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
static uint32_t KnuthMultiplicativeHash32(const void *buf, size_t size) {
|
||||
static uint32_t __kmp32(const void *buf, size_t size) {
|
||||
size_t i;
|
||||
uint32_t h;
|
||||
const uint32_t kPhiPrime = 0x9e3779b1;
|
||||
|
@ -36,18 +38,24 @@ static uint32_t KnuthMultiplicativeHash32(const void *buf, size_t size) {
|
|||
return h;
|
||||
}
|
||||
|
||||
static textwindows dontinline uint32_t GetUserNameHash(void) {
|
||||
textwindows uint32_t __synthesize_uid(void) {
|
||||
char16_t buf[257];
|
||||
uint32_t size = ARRAYLEN(buf);
|
||||
GetUserName(&buf, &size);
|
||||
return KnuthMultiplicativeHash32(buf, size >> 1) & INT_MAX;
|
||||
static atomic_uint uid;
|
||||
uint32_t tmp, size = ARRAYLEN(buf);
|
||||
if (!(tmp = atomic_load_explicit(&uid, memory_order_acquire))) {
|
||||
GetUserName(&buf, &size);
|
||||
tmp = __kmp32(buf, size >> 1) & INT_MAX;
|
||||
if (!tmp) ++tmp;
|
||||
atomic_store_explicit(&uid, tmp, memory_order_release);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns real user id of process.
|
||||
*
|
||||
* This never fails. On Windows, which doesn't really have this concept,
|
||||
* we return a deterministic value that's likely to work.
|
||||
* we return a hash of the username.
|
||||
*
|
||||
* @return user id (always successful)
|
||||
* @asyncsignalsafe
|
||||
|
@ -61,7 +69,7 @@ uint32_t getuid(void) {
|
|||
} else if (!IsWindows()) {
|
||||
rc = sys_getuid();
|
||||
} else {
|
||||
rc = GetUserNameHash();
|
||||
rc = __synthesize_uid();
|
||||
}
|
||||
npassert(rc >= 0);
|
||||
STRACE("%s() → %d", "getuid", rc);
|
||||
|
@ -72,7 +80,7 @@ uint32_t getuid(void) {
|
|||
* Returns real group id of process.
|
||||
*
|
||||
* This never fails. On Windows, which doesn't really have this concept,
|
||||
* we return a deterministic value that's likely to work.
|
||||
* we return a hash of the username.
|
||||
*
|
||||
* @return group id (always successful)
|
||||
* @asyncsignalsafe
|
||||
|
@ -86,7 +94,7 @@ uint32_t getgid(void) {
|
|||
} else if (!IsWindows()) {
|
||||
rc = sys_getgid();
|
||||
} else {
|
||||
rc = GetUserNameHash();
|
||||
rc = __synthesize_uid();
|
||||
}
|
||||
npassert(rc >= 0);
|
||||
STRACE("%s() → %d", "getgid", rc);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/sigval.h"
|
||||
#include "libc/dce.h"
|
||||
|
@ -16,6 +17,7 @@ COSMOPOLITAN_C_START_
|
|||
#define kIoMotion ((const int8_t[3]){1, 0, 0})
|
||||
|
||||
extern struct Fds g_fds;
|
||||
extern atomic_int __umask;
|
||||
extern const struct Fd kEmptyFd;
|
||||
|
||||
int __reservefd(int);
|
||||
|
@ -24,6 +26,7 @@ void __releasefd(int);
|
|||
int __ensurefds(int);
|
||||
int __ensurefds_unlocked(int);
|
||||
void __printfds(void);
|
||||
uint32_t __synthesize_uid(void);
|
||||
|
||||
forceinline int64_t __getfdhandleactual(int fd) {
|
||||
return g_fds.p[fd].handle;
|
||||
|
@ -37,8 +40,8 @@ forceinline bool __isfdkind(int fd, int kind) {
|
|||
return 0 <= fd && fd < g_fds.n && g_fds.p[fd].kind == kind;
|
||||
}
|
||||
|
||||
int _check_interrupts(int);
|
||||
int sys_close_nt(struct Fd *, int);
|
||||
int _check_interrupts(int, struct Fd *);
|
||||
int sys_openat_metal(int, const char *, int, unsigned);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
|
|
|
@ -33,8 +33,8 @@
|
|||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
textwindows int _check_interrupts(int sigops, struct Fd *fd) {
|
||||
int e, rc;
|
||||
textwindows int _check_interrupts(int sigops) {
|
||||
int e, rc, flags = 0;
|
||||
e = errno;
|
||||
if (_weaken(pthread_testcancel_np) &&
|
||||
(rc = _weaken(pthread_testcancel_np)())) {
|
||||
|
@ -42,21 +42,22 @@ textwindows int _check_interrupts(int sigops, struct Fd *fd) {
|
|||
return -1;
|
||||
}
|
||||
if (__tls_enabled) {
|
||||
flags = __get_tls()->tib_flags;
|
||||
__get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
|
||||
}
|
||||
if (_weaken(_check_sigalrm)) {
|
||||
_weaken(_check_sigalrm)();
|
||||
}
|
||||
if (__tls_enabled) {
|
||||
__get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL;
|
||||
__get_tls()->tib_flags = flags;
|
||||
}
|
||||
if (_weaken(_check_sigwinch)) {
|
||||
_weaken(_check_sigwinch)();
|
||||
}
|
||||
if (!__tls_enabled || !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
|
||||
if (!(sigops & kSigOpNochld) && _weaken(_check_sigchld)) {
|
||||
_weaken(_check_sigchld)();
|
||||
}
|
||||
if (fd && _weaken(_check_sigwinch)) {
|
||||
_weaken(_check_sigwinch)(fd);
|
||||
}
|
||||
}
|
||||
if (_weaken(__sig_check) && _weaken(__sig_check)(sigops)) {
|
||||
return eintr();
|
||||
|
|
|
@ -672,9 +672,9 @@ int ioctl(int fd, unsigned long request, ...) {
|
|||
if (request == FIONREAD) {
|
||||
rc = ioctl_fionread(fd, arg);
|
||||
} else if (request == TIOCGWINSZ) {
|
||||
rc = tcgetwinsize(fd, arg);
|
||||
return tcgetwinsize(fd, arg);
|
||||
} else if (request == TIOCSWINSZ) {
|
||||
rc = tcsetwinsize(fd, arg);
|
||||
return tcsetwinsize(fd, arg);
|
||||
} else if (request == SIOCGIFCONF) {
|
||||
rc = ioctl_siocgifconf(fd, arg);
|
||||
} else if (request == SIOCGIFADDR) {
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
int64_t lseek(int fd, int64_t offset, int whence) {
|
||||
int64_t rc;
|
||||
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||
rc = _weaken(__zipos_lseek)(
|
||||
rc = _weaken(__zipos_seek)(
|
||||
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, offset, whence);
|
||||
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd()) {
|
||||
rc = sys_lseek(fd, offset, whence, 0);
|
||||
|
|
|
@ -92,20 +92,24 @@ TryAgain:
|
|||
if (result || flags == F_OK) {
|
||||
rc = 0;
|
||||
} else {
|
||||
NTTRACE("ntaccesscheck finale failed %d %x", result, flags);
|
||||
NTTRACE("ntaccesscheck finale failed: result=%d flags=%x", result,
|
||||
flags);
|
||||
rc = eacces();
|
||||
}
|
||||
} else {
|
||||
rc = __winerr();
|
||||
NTTRACE("%s(%#hs) failed: %m", "AccessCheck", pathname);
|
||||
NTTRACE("%s(%#hs) failed: %s", "AccessCheck", pathname,
|
||||
strerror(errno));
|
||||
}
|
||||
} else {
|
||||
rc = __winerr();
|
||||
NTTRACE("%s(%#hs) failed: %m", "DuplicateToken", pathname);
|
||||
NTTRACE("%s(%#hs) failed: %s", "DuplicateToken", pathname,
|
||||
strerror(errno));
|
||||
}
|
||||
} else {
|
||||
rc = __winerr();
|
||||
NTTRACE("%s(%#hs) failed: %m", "OpenProcessToken", pathname);
|
||||
NTTRACE("%s(%#hs) failed: %s", "OpenProcessToken", pathname,
|
||||
strerror(errno));
|
||||
}
|
||||
} else {
|
||||
e = GetLastError();
|
||||
|
@ -115,11 +119,13 @@ TryAgain:
|
|||
goto TryAgain;
|
||||
} else {
|
||||
rc = enomem();
|
||||
NTTRACE("%s(%#hs) failed: %m", "GetFileSecurity", pathname);
|
||||
NTTRACE("%s(%#hs) failed: %s", "GetFileSecurity", pathname,
|
||||
strerror(errno));
|
||||
}
|
||||
} else {
|
||||
errno = e;
|
||||
NTTRACE("%s(%#hs) failed: %m", "GetFileSecurity", pathname);
|
||||
NTTRACE("%s(%#hs) failed: %s", "GetFileSecurity", pathname,
|
||||
strerror(errno));
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,13 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path,
|
|||
if (__mkntpathat(dirfd, path, flags, path16) == -1) {
|
||||
return kNtInvalidHandleValue;
|
||||
}
|
||||
if (flags & O_NOFOLLOW) {
|
||||
if ((attr = GetFileAttributes(path16)) != -1u && //
|
||||
(attr & kNtFileAttributeReparsePoint)) {
|
||||
return eloop();
|
||||
}
|
||||
flags &= ~O_NOFOLLOW;
|
||||
}
|
||||
if (GetNtOpenFlags(flags, mode, &perm, &share, &disp, &attr) == -1) {
|
||||
return kNtInvalidHandleValue;
|
||||
}
|
||||
|
@ -57,7 +64,9 @@ static textwindows int sys_open_nt_console(int dirfd,
|
|||
// this is an ugly hack that works for observed usage patterns
|
||||
g_fds.p[fd].handle = g_fds.p[STDIN_FILENO].handle;
|
||||
g_fds.p[fd].extra = g_fds.p[STDOUT_FILENO].handle;
|
||||
g_fds.p[fd].dontclose = true; // don't call CloseHandle() upon close()
|
||||
g_fds.p[STDOUT_FILENO].dontclose = true;
|
||||
g_fds.p[STDIN_FILENO].dontclose = true;
|
||||
g_fds.p[fd].dontclose = true;
|
||||
} else if ((g_fds.p[fd].handle = sys_open_nt_impl(
|
||||
dirfd, mp->conin, (flags & ~O_ACCMODE) | O_RDONLY, mode,
|
||||
kNtFileFlagOverlapped)) != -1) {
|
||||
|
|
|
@ -61,6 +61,19 @@
|
|||
* // run `zip program.com hi.txt` beforehand
|
||||
* openat(AT_FDCWD, "/zip/hi.txt", O_RDONLY);
|
||||
*
|
||||
* Cosmopolitan's general approach on Windows to path translation is to
|
||||
*
|
||||
* - replace `/' with `\`
|
||||
* - translate utf-8 into utf-16
|
||||
* - turn `"\X\foo"` into `"\\?\X:\foo"`
|
||||
* - turn `"\X"` into `"\\?\X:\"`
|
||||
* - turn `"X:\foo"` into `"\\?\X:\foo"`
|
||||
*
|
||||
* On Windows, opening files in `/tmp` will open them in GetTempPath(),
|
||||
* which is a secure per-user directory. Opening `/dev/tty` will open a
|
||||
* special console file descriptor holding both `CONIN$` and `CONOUT$`,
|
||||
* which can't be fully closed. Opening `/dev/null` will open up `NUL`.
|
||||
*
|
||||
* @param dirfd is normally `AT_FDCWD` but if it's an open directory and
|
||||
* `file` names a relative path then it's opened relative to `dirfd`
|
||||
* @param file is a UTF-8 string naming filesystem entity, e.g. `foo/bar.txt`,
|
||||
|
@ -81,12 +94,12 @@
|
|||
* - `O_CLOEXEC` automatic close() upon execve()
|
||||
* - `O_EXCL` exclusive access (see below)
|
||||
* - `O_APPEND` open file for appending only
|
||||
* - `O_NOFOLLOW` fail with ELOOP if it's a symlink
|
||||
* - `O_NONBLOCK` asks read/write to fail with `EAGAIN` rather than block
|
||||
* - `O_EXEC` open file for execution only; see fexecve()
|
||||
* - `O_NOCTTY` prevents `file` possibly becoming controlling terminal
|
||||
* - `O_NONBLOCK` asks read/write to fail with `EAGAIN` rather than block
|
||||
* - `O_DIRECT` it's complicated (not supported on Apple and OpenBSD)
|
||||
* - `O_DIRECTORY` useful for stat'ing (hint on UNIX but required on NT)
|
||||
* - `O_NOFOLLOW` fail if it's a symlink (zero on Windows)
|
||||
* - `O_DSYNC` it's complicated (zero on non-Linux/Apple)
|
||||
* - `O_RSYNC` it's complicated (zero on non-Linux/Apple)
|
||||
* - `O_VERIFY` it's complicated (zero on non-FreeBSD)
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
textwindows int sys_pause_nt(void) {
|
||||
for (;;) {
|
||||
|
||||
if (_check_interrupts(0, g_fds.p)) {
|
||||
if (_check_interrupts(0)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
|
|||
if (sigmask) {
|
||||
__sig_mask(SIG_SETMASK, sigmask, &oldmask);
|
||||
}
|
||||
if ((rc = _check_interrupts(0, g_fds.p))) {
|
||||
if ((rc = _check_interrupts(0))) {
|
||||
goto ReturnPath;
|
||||
}
|
||||
|
||||
|
@ -194,7 +194,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
|
|||
}
|
||||
// otherwise loop limitlessly for timeout to elapse while
|
||||
// checking for signal delivery interrupts, along the way
|
||||
if ((rc = _check_interrupts(0, g_fds.p))) {
|
||||
if ((rc = _check_interrupts(0))) {
|
||||
goto ReturnPath;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,10 +27,6 @@
|
|||
#include "libc/thread/tls.h"
|
||||
#include "libc/thread/xnu.internal.h"
|
||||
|
||||
static textwindows inline bool HasWorkingConsole(void) {
|
||||
return !!(__ntconsolemode[0] | __ntconsolemode[1] | __ntconsolemode[2]);
|
||||
}
|
||||
|
||||
static dontubsan void RaiseSigFpe(void) {
|
||||
volatile int x = 0;
|
||||
x = 1 / x;
|
||||
|
|
|
@ -26,9 +26,12 @@
|
|||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/calls/wincrash.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/errors.h"
|
||||
|
@ -48,83 +51,12 @@
|
|||
|
||||
#ifdef __x86_64__
|
||||
|
||||
enum Action {
|
||||
DO_NOTHING,
|
||||
DO_RESTART,
|
||||
DO_EINTR,
|
||||
};
|
||||
|
||||
static textwindows void sys_read_nt_abort(int64_t handle,
|
||||
struct NtOverlapped *overlapped) {
|
||||
unassert(CancelIoEx(handle, overlapped) ||
|
||||
GetLastError() == kNtErrorNotFound);
|
||||
}
|
||||
|
||||
static textwindows int MungeTerminalInput(char *p, uint32_t *n) {
|
||||
size_t i, j;
|
||||
if (!(__ttymagic & kFdTtyNoCr2Nl)) {
|
||||
for (i = 0; i < *n; ++i) {
|
||||
if (p[i] == '\r') {
|
||||
p[i] = '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(__ttymagic & kFdTtyNoIsigs)) {
|
||||
bool delivered = false;
|
||||
bool got_vintr = false;
|
||||
bool got_vquit = false;
|
||||
for (j = i = 0; i < *n; ++i) {
|
||||
if (__vintr != _POSIX_VDISABLE && p[i] == __vintr) {
|
||||
got_vintr = true;
|
||||
} else if (__vquit != _POSIX_VDISABLE && p[i] == __vquit) {
|
||||
got_vquit = true;
|
||||
} else {
|
||||
p[j++] = p[i];
|
||||
}
|
||||
}
|
||||
if (got_vintr) {
|
||||
delivered |= __sig_handle(0, SIGINT, SI_KERNEL, 0);
|
||||
}
|
||||
if (got_vquit) {
|
||||
delivered |= __sig_handle(0, SIGQUIT, SI_KERNEL, 0);
|
||||
}
|
||||
if (*n && !j) {
|
||||
if (delivered) {
|
||||
return DO_EINTR;
|
||||
} else {
|
||||
return DO_RESTART;
|
||||
}
|
||||
}
|
||||
*n = j;
|
||||
}
|
||||
return DO_NOTHING;
|
||||
}
|
||||
|
||||
// Manual CMD.EXE echoing for when !ICANON && ECHO is the case.
|
||||
static textwindows void EchoTerminalInput(struct Fd *fd, char *p, size_t n) {
|
||||
int64_t hOutput;
|
||||
if (fd->kind == kFdConsole) {
|
||||
hOutput = fd->extra;
|
||||
} else {
|
||||
hOutput = g_fds.p[1].handle;
|
||||
}
|
||||
if (__ttymagic & kFdTtyEchoRaw) {
|
||||
WriteFile(hOutput, p, n, 0, 0);
|
||||
} else {
|
||||
size_t i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (isascii(p[i]) && iscntrl(p[i]) && p[i] != '\n' && p[i] != '\t') {
|
||||
char ctl[2];
|
||||
ctl[0] = '^';
|
||||
ctl[1] = p[i] ^ 0100;
|
||||
WriteFile(hOutput, ctl, 2, 0, 0);
|
||||
} else {
|
||||
WriteFile(hOutput, p + i, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
|
||||
size_t size, int64_t offset) {
|
||||
|
||||
|
@ -133,11 +65,38 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
|
|||
uint32_t got;
|
||||
int filetype;
|
||||
int64_t handle;
|
||||
uint32_t remain;
|
||||
uint32_t conmode;
|
||||
char *targetdata;
|
||||
uint32_t targetsize;
|
||||
bool is_console_input;
|
||||
int abort_errno = EAGAIN;
|
||||
StartOver:
|
||||
size = MIN(size, 0x7ffff000);
|
||||
handle = __resolve_stdin_handle(fd->handle);
|
||||
filetype = GetFileType(handle);
|
||||
is_console_input = g_fds.stdin.handle ? fd->handle == g_fds.stdin.handle
|
||||
: fd->handle == g_fds.p[0].handle;
|
||||
|
||||
// the caller might be reading a single byte at a time. but we need to
|
||||
// be able to munge three bytes into just 1 e.g. "\342\220\200" → "\0"
|
||||
if (size && fd->buflen) {
|
||||
ReturnDataFromBuffer:
|
||||
got = MIN(size, fd->buflen);
|
||||
remain = fd->buflen - got;
|
||||
if (got) memcpy(data, fd->buf, got);
|
||||
if (remain) memmove(fd->buf, fd->buf + got, remain);
|
||||
fd->buflen = remain;
|
||||
return got;
|
||||
}
|
||||
if (is_console_input && size && size < 3 && (__ttymagic & kFdTtyMunging)) {
|
||||
targetdata = fd->buf;
|
||||
targetsize = sizeof(fd->buf);
|
||||
} else {
|
||||
targetdata = data;
|
||||
targetsize = size;
|
||||
}
|
||||
|
||||
if (filetype == kNtFileTypeChar || filetype == kNtFileTypePipe) {
|
||||
struct NtOverlapped overlap = {0};
|
||||
if (offset != -1) {
|
||||
|
@ -147,7 +106,7 @@ StartOver:
|
|||
if ((overlap.hEvent = CreateEvent(0, 0, 0, 0))) {
|
||||
// the win32 manual says it's important to *not* put &got here
|
||||
// since for overlapped i/o, we always use GetOverlappedResult
|
||||
ok = ReadFile(handle, data, size, 0, &overlap);
|
||||
ok = ReadFile(handle, targetdata, targetsize, 0, &overlap);
|
||||
if (!ok && GetLastError() == kNtErrorIoPending) {
|
||||
// the i/o operation is in flight; blocking is unavoidable
|
||||
// if we're in a non-blocking mode, then immediately abort
|
||||
|
@ -155,7 +114,7 @@ StartOver:
|
|||
// otherwise wait for i/o periodically checking interrupts
|
||||
if (fd->flags & O_NONBLOCK) {
|
||||
sys_read_nt_abort(handle, &overlap);
|
||||
} else if (_check_interrupts(kSigOpRestartable, g_fds.p)) {
|
||||
} else if (_check_interrupts(kSigOpRestartable)) {
|
||||
Interrupted:
|
||||
abort_errno = errno;
|
||||
sys_read_nt_abort(handle, &overlap);
|
||||
|
@ -164,7 +123,7 @@ StartOver:
|
|||
uint32_t i;
|
||||
i = WaitForSingleObject(overlap.hEvent, __SIG_POLLING_INTERVAL_MS);
|
||||
if (i == kNtWaitTimeout) {
|
||||
if (_check_interrupts(kSigOpRestartable, g_fds.p)) {
|
||||
if (_check_interrupts(kSigOpRestartable)) {
|
||||
goto Interrupted;
|
||||
}
|
||||
} else {
|
||||
|
@ -186,7 +145,7 @@ StartOver:
|
|||
}
|
||||
} else if (offset == -1) {
|
||||
// perform simple blocking file read
|
||||
ok = ReadFile(handle, data, size, &got, 0);
|
||||
ok = ReadFile(handle, targetdata, targetsize, &got, 0);
|
||||
} else {
|
||||
// perform pread()-style file read at particular file offset
|
||||
int64_t position;
|
||||
|
@ -196,17 +155,17 @@ StartOver:
|
|||
}
|
||||
struct NtOverlapped overlap = {0};
|
||||
overlap.Pointer = (void *)(uintptr_t)offset;
|
||||
ok = ReadFile(handle, data, size, 0, &overlap);
|
||||
ok = ReadFile(handle, targetdata, targetsize, 0, &overlap);
|
||||
if (!ok && GetLastError() == kNtErrorIoPending) ok = true;
|
||||
if (ok) ok = GetOverlappedResult(handle, &overlap, &got, true);
|
||||
// restore file pointer which windows clobbers, even on error
|
||||
unassert(SetFilePointerEx(handle, position, 0, SEEK_SET));
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
if (g_fds.stdin.handle ? fd->handle == g_fds.stdin.handle
|
||||
: fd->handle == g_fds.p[0].handle) {
|
||||
if (is_console_input) {
|
||||
if (__ttymagic & kFdTtyMunging) {
|
||||
switch (MungeTerminalInput(data, &got)) {
|
||||
switch (_weaken(__munge_terminal_input)(targetdata, &got)) {
|
||||
case DO_NOTHING:
|
||||
break;
|
||||
case DO_RESTART:
|
||||
|
@ -218,9 +177,13 @@ StartOver:
|
|||
}
|
||||
}
|
||||
if (__ttymagic & kFdTtyEchoing) {
|
||||
EchoTerminalInput(fd, data, got);
|
||||
_weaken(__echo_terminal_input)(fd, targetdata, got);
|
||||
}
|
||||
}
|
||||
if (targetdata != data) {
|
||||
fd->buflen = got;
|
||||
goto ReturnDataFromBuffer;
|
||||
}
|
||||
return got;
|
||||
}
|
||||
|
||||
|
@ -245,7 +208,7 @@ textwindows ssize_t sys_read_nt(struct Fd *fd, const struct iovec *iov,
|
|||
uint32_t size;
|
||||
size_t i, total;
|
||||
if (opt_offset < -1) return einval();
|
||||
if (_check_interrupts(kSigOpRestartable, fd)) return -1;
|
||||
if (_check_interrupts(kSigOpRestartable)) return -1;
|
||||
while (iovlen && !iov[0].iov_len) iov++, iovlen--;
|
||||
if (iovlen) {
|
||||
for (total = i = 0; i < iovlen; ++i) {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "libc/calls/ucontext.h"
|
||||
|
||||
#define __SIG_QUEUE_LENGTH 32
|
||||
#define __SIG_POLLING_INTERVAL_MS 50
|
||||
#define __SIG_POLLING_INTERVAL_MS 20
|
||||
#define __SIG_LOGGING_INTERVAL_MS 1700
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
|
|
|
@ -77,7 +77,7 @@ int sigsuspend(const sigset_t *ignore) {
|
|||
long totoms = 0;
|
||||
#endif
|
||||
do {
|
||||
if ((rc = _check_interrupts(0, g_fds.p))) {
|
||||
if ((rc = _check_interrupts(0))) {
|
||||
break;
|
||||
}
|
||||
if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) {
|
||||
|
|
|
@ -16,48 +16,57 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/winsize.h"
|
||||
#include "libc/calls/struct/winsize.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/struct/consolescreenbufferinfoex.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
static struct winsize __ws;
|
||||
static atomic_uint __win_winsize;
|
||||
|
||||
textwindows void _check_sigwinch(struct Fd *fd) {
|
||||
int e;
|
||||
siginfo_t si;
|
||||
struct winsize ws, old;
|
||||
struct NtConsoleScreenBufferInfoEx sbinfo;
|
||||
if (__tls_enabled && __threaded != gettid()) return;
|
||||
old = __ws;
|
||||
e = errno;
|
||||
if (old.ws_row != 0xffff) {
|
||||
if (tcgetwinsize_nt(fd, &ws) != -1) {
|
||||
if (old.ws_col != ws.ws_col || old.ws_row != ws.ws_row) {
|
||||
__ws = ws;
|
||||
if (old.ws_col | old.ws_row) {
|
||||
__sig_add(0, SIGWINCH, SI_KERNEL);
|
||||
}
|
||||
}
|
||||
static textwindows unsigned __get_console_size(void) {
|
||||
for (int fd = 1; fd < 10; ++fd) {
|
||||
intptr_t hConsoleOutput;
|
||||
if (g_fds.p[fd].kind == kFdConsole) {
|
||||
hConsoleOutput = g_fds.p[fd].extra;
|
||||
} else {
|
||||
if (!old.ws_row && !old.ws_col) {
|
||||
__ws.ws_row = 0xffff;
|
||||
}
|
||||
hConsoleOutput = g_fds.p[fd].handle;
|
||||
}
|
||||
struct NtConsoleScreenBufferInfoEx sr = {.cbSize = sizeof(sr)};
|
||||
if (GetConsoleScreenBufferInfoEx(hConsoleOutput, &sr)) {
|
||||
unsigned short yn = sr.srWindow.Bottom - sr.srWindow.Top + 1;
|
||||
unsigned short xn = sr.srWindow.Right - sr.srWindow.Left + 1;
|
||||
return (unsigned)yn << 16 | xn;
|
||||
}
|
||||
}
|
||||
errno = e;
|
||||
return -1u;
|
||||
}
|
||||
|
||||
textwindows void _check_sigwinch(void) {
|
||||
unsigned old = atomic_load_explicit(&__win_winsize, memory_order_acquire);
|
||||
if (old == -1u) return;
|
||||
unsigned neu = __get_console_size();
|
||||
old = atomic_exchange(&__win_winsize, neu);
|
||||
if (neu != old) {
|
||||
__sig_add(0, SIGWINCH, SI_KERNEL);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((__constructor__)) static void sigwinch_init(void) {
|
||||
if (!IsWindows()) return;
|
||||
unsigned ws = __get_console_size();
|
||||
atomic_store_explicit(&__win_winsize, ws, memory_order_release);
|
||||
STRACE("sigwinch_init() → %08x", ws);
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -20,19 +20,14 @@
|
|||
#include "libc/sysv/consts/at.h"
|
||||
|
||||
/**
|
||||
* Returns information about thing.
|
||||
* Returns information about file.
|
||||
*
|
||||
* @param st is where result is stored
|
||||
* @see S_ISDIR(st.st_mode), S_ISREG(), etc.
|
||||
* @raise EACCES if denied access to component in path prefix
|
||||
* @raise EIO if i/o error occurred while reading from filesystem
|
||||
* @raise ELOOP if a symbolic link loop exists in `path`
|
||||
* @raise ENAMETOOLONG if a component in `path` exceeds `NAME_MAX`
|
||||
* @raise ENOENT on empty string or if component in path doesn't exist
|
||||
* @raise ENOTDIR if a parent component existed that wasn't a directory
|
||||
* @raise EOVERFLOW shouldn't be possible on 64-bit systems
|
||||
* @raise ELOOP may ahappen if `SYMLOOP_MAX` symlinks were dereferenced
|
||||
* @raise ENAMETOOLONG may happen if `path` exceeded `PATH_MAX`
|
||||
* This function is equivalent to:
|
||||
*
|
||||
* struct stat st;
|
||||
* fstatat(AT_FDCWD, path, &st, 0);
|
||||
*
|
||||
* @see fstatat() for further documentation
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int stat(const char *path, struct stat *st) {
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#define DO_NOTHING 0
|
||||
#define DO_RESTART 1
|
||||
#define DO_EINTR 2
|
||||
|
||||
#define kFdEmpty 0
|
||||
#define kFdFile 1
|
||||
#define kFdSocket 2
|
||||
|
@ -23,6 +27,8 @@ struct Fd {
|
|||
char kind;
|
||||
bool zombie;
|
||||
bool dontclose;
|
||||
char buflen;
|
||||
char buf[4];
|
||||
unsigned flags;
|
||||
unsigned mode;
|
||||
int64_t handle;
|
||||
|
@ -44,8 +50,10 @@ struct Fds {
|
|||
struct StdinRelay stdin;
|
||||
};
|
||||
|
||||
int64_t __resolve_stdin_handle(int64_t);
|
||||
void WinMainStdin(void);
|
||||
int64_t __resolve_stdin_handle(int64_t);
|
||||
int __munge_terminal_input(char *, uint32_t *);
|
||||
void __echo_terminal_input(struct Fd *, char *, size_t);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -65,7 +65,7 @@ const char *DescribeSigaction(char[256], int, const struct sigaction *);
|
|||
|
||||
void _init_onntconsoleevent(void);
|
||||
void _init_wincrash(void);
|
||||
void _check_sigwinch();
|
||||
void _check_sigwinch(void);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
int tcgetwinsize_nt(struct Fd *, struct winsize *);
|
||||
int tcgetwinsize_nt(int, struct winsize *);
|
||||
const char *DescribeWinsize(char[64], int, struct winsize *);
|
||||
#define DescribeWinsize(rc, ws) DescribeWinsize(alloca(64), rc, ws)
|
||||
|
||||
|
|
|
@ -33,8 +33,9 @@
|
|||
|
||||
static dontinline textwindows int sys_tcdrain_nt(int fd) {
|
||||
if (!__isfdopen(fd)) return ebadf();
|
||||
if (_check_interrupts(0, g_fds.p)) return -1;
|
||||
if (!FlushFileBuffers(g_fds.p[fd].handle)) return __winerr();
|
||||
if (_check_interrupts(0)) return -1;
|
||||
// Tried FlushFileBuffers but it made Emacs hang when run in cmd.exe
|
||||
// "Console output is not buffered." -Quoth MSDN on FlushFileBuffers
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,67 +17,45 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/winsize.h"
|
||||
#include "libc/calls/struct/winsize.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/startf.h"
|
||||
#include "libc/nt/startupinfo.h"
|
||||
#include "libc/nt/struct/consolescreenbufferinfoex.h"
|
||||
#include "libc/nt/struct/startupinfo.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/fileno.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int tcgetwinsize_nt(struct Fd *fd, struct winsize *ws) {
|
||||
int i, e, rc;
|
||||
uint32_t mode;
|
||||
struct Fd *fds[3];
|
||||
struct NtStartupInfo startinfo;
|
||||
struct NtConsoleScreenBufferInfoEx sbinfo;
|
||||
rc = -1;
|
||||
e = errno;
|
||||
if (ws) {
|
||||
__fds_lock();
|
||||
fds[0] = fd, fds[1] = g_fds.p + 1, fds[2] = g_fds.p + 0;
|
||||
GetStartupInfo(&startinfo);
|
||||
for (i = 0; i < ARRAYLEN(fds); ++i) {
|
||||
if (fds[i]->kind == kFdFile || fds[i]->kind == kFdConsole) {
|
||||
if (GetConsoleMode(__getfdhandleactual(i), &mode)) {
|
||||
bzero(&sbinfo, sizeof(sbinfo));
|
||||
sbinfo.cbSize = sizeof(sbinfo);
|
||||
if (GetConsoleScreenBufferInfoEx(__getfdhandleactual(i), &sbinfo)) {
|
||||
ws->ws_col = sbinfo.srWindow.Right - sbinfo.srWindow.Left + 1;
|
||||
ws->ws_row = sbinfo.srWindow.Bottom - sbinfo.srWindow.Top + 1;
|
||||
ws->ws_xpixel = 0;
|
||||
ws->ws_ypixel = 0;
|
||||
errno = e;
|
||||
rc = 0;
|
||||
break;
|
||||
} else if (startinfo.dwFlags & kNtStartfUsecountchars) {
|
||||
ws->ws_col = startinfo.dwXCountChars;
|
||||
ws->ws_row = startinfo.dwYCountChars;
|
||||
ws->ws_xpixel = 0;
|
||||
ws->ws_ypixel = 0;
|
||||
errno = e;
|
||||
rc = 0;
|
||||
break;
|
||||
} else {
|
||||
__winerr();
|
||||
}
|
||||
} else {
|
||||
enotty();
|
||||
}
|
||||
} else {
|
||||
ebadf();
|
||||
}
|
||||
}
|
||||
__fds_unlock();
|
||||
} else {
|
||||
efault();
|
||||
textwindows int tcgetwinsize_nt(int fd, struct winsize *ws) {
|
||||
|
||||
// The Linux man page doesn't list EBADF as an errno for this.
|
||||
if (!__isfdopen(fd)) {
|
||||
return enotty();
|
||||
}
|
||||
|
||||
// Unlike _check_sigwinch() this is an API so be stricter.
|
||||
intptr_t hConsoleOutput;
|
||||
if (fd == STDIN_FILENO) {
|
||||
uint32_t dwMode;
|
||||
// WIN32 doesn't allow GetConsoleScreenBufferInfoEx(stdin)
|
||||
if (GetConsoleMode(g_fds.p[STDIN_FILENO].handle, &dwMode)) {
|
||||
hConsoleOutput = g_fds.p[STDOUT_FILENO].handle;
|
||||
if (GetConsoleMode(hConsoleOutput, &dwMode)) {
|
||||
hConsoleOutput = g_fds.p[STDERR_FILENO].handle;
|
||||
}
|
||||
} else {
|
||||
return enotty();
|
||||
}
|
||||
} else if (g_fds.p[fd].kind == kFdConsole) {
|
||||
hConsoleOutput = g_fds.p[fd].extra;
|
||||
} else {
|
||||
hConsoleOutput = g_fds.p[fd].handle;
|
||||
}
|
||||
|
||||
// Query the console.
|
||||
struct NtConsoleScreenBufferInfoEx sr = {.cbSize = sizeof(sr)};
|
||||
if (GetConsoleScreenBufferInfoEx(hConsoleOutput, &sr)) {
|
||||
ws->ws_col = sr.srWindow.Right - sr.srWindow.Left + 1;
|
||||
ws->ws_row = sr.srWindow.Bottom - sr.srWindow.Top + 1;
|
||||
return 0;
|
||||
} else {
|
||||
return enotty();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ int tcgetwinsize(int fd, struct winsize *ws) {
|
|||
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||
rc = enotty();
|
||||
} else if (IsWindows()) {
|
||||
rc = tcgetwinsize_nt(g_fds.p + fd, ws);
|
||||
rc = tcgetwinsize_nt(fd, ws);
|
||||
} else {
|
||||
rc = sys_ioctl(fd, TIOCGWINSZ, ws);
|
||||
}
|
||||
|
|
|
@ -17,20 +17,151 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/metatermios.internal.h"
|
||||
#include "libc/calls/termios.internal.h"
|
||||
#include "libc/calls/ttydefaults.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/consolemodeflags.h"
|
||||
#include "libc/nt/enum/version.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/version.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/termios.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
textwindows int __munge_terminal_input(char *p, uint32_t *n) {
|
||||
size_t i, j;
|
||||
if (!(__ttymagic & kFdTtyNoCr2Nl)) {
|
||||
for (i = 0; i < *n; ++i) {
|
||||
if (p[i] == '\r') {
|
||||
p[i] = '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
bool delivered = false;
|
||||
bool got_vintr = false;
|
||||
bool got_vquit = false;
|
||||
for (j = i = 0; i < *n; ++i) {
|
||||
/*
|
||||
It's not possible to type Ctrl+Space (aka ^@) into the CMD.EXE
|
||||
console. Ctrl+Z is also problematic, since the Windows Console
|
||||
processes that keystroke into an EOF event, even when we don't
|
||||
enable input processing. These control codes are important for
|
||||
Emacs users. One solution is to install the "Windows Terminal"
|
||||
application from Microsoft Store, type Ctrl+Shift+, to get its
|
||||
settings.json file opened, and add this code to its "actions":
|
||||
|
||||
"actions": [
|
||||
{
|
||||
"command": {
|
||||
"action": "sendInput",
|
||||
"input": "␀"
|
||||
},
|
||||
"keys": "ctrl+space"
|
||||
},
|
||||
{
|
||||
"command": {
|
||||
"action": "sendInput",
|
||||
"input": "␚"
|
||||
},
|
||||
"keys": "ctrl+z"
|
||||
},
|
||||
{
|
||||
"command": {
|
||||
"action": "sendInput",
|
||||
"input": "\u001f"
|
||||
},
|
||||
"keys": "ctrl+-"
|
||||
}
|
||||
],
|
||||
|
||||
Its not possible to configure Windows Terminal to output our
|
||||
control codes. The workaround is to encode control sequences
|
||||
using the "Control Pictures" UNICODE plane, which we'll then
|
||||
translate back from UTF-8 glyphs, into actual control codes.
|
||||
|
||||
Another option Windows users can consider, particularly when
|
||||
using CMD.EXE is installing Microsoft PowerTools whech makes
|
||||
it possible to remap keys then configure ncurses to use them
|
||||
|
||||
https://github.com/microsoft/terminal/issues/2865
|
||||
*/
|
||||
int c;
|
||||
if (i + 3 <= *n && // control pictures ascii nul ␀
|
||||
(p[i + 0] & 255) == 0xe2 && // becomes ^@ a.k.a. ctrl-space
|
||||
(p[i + 1] & 255) == 0x90 && // map the entire unicode plane
|
||||
((p[i + 2] & 255) >= 0x80 && //
|
||||
(p[i + 2] & 255) <= 0x9F)) {
|
||||
c = (p[i + 2] & 255) - 0x80;
|
||||
i += 2;
|
||||
} else {
|
||||
c = p[i] & 255;
|
||||
}
|
||||
if (!(__ttymagic & kFdTtyNoIsigs) && //
|
||||
__vintr != _POSIX_VDISABLE && //
|
||||
c == __vintr) {
|
||||
got_vintr = true;
|
||||
} else if (!(__ttymagic & kFdTtyNoIsigs) && //
|
||||
__vquit != _POSIX_VDISABLE && //
|
||||
c == __vquit) {
|
||||
got_vquit = true;
|
||||
} else {
|
||||
p[j++] = c;
|
||||
}
|
||||
}
|
||||
if (got_vintr) {
|
||||
delivered |= __sig_handle(0, SIGINT, SI_KERNEL, 0);
|
||||
}
|
||||
if (got_vquit) {
|
||||
delivered |= __sig_handle(0, SIGQUIT, SI_KERNEL, 0);
|
||||
}
|
||||
if (*n && !j) {
|
||||
if (delivered) {
|
||||
return DO_EINTR;
|
||||
} else {
|
||||
return DO_RESTART;
|
||||
}
|
||||
}
|
||||
*n = j;
|
||||
return DO_NOTHING;
|
||||
}
|
||||
|
||||
// Manual CMD.EXE echoing for when !ICANON && ECHO is the case.
|
||||
textwindows void __echo_terminal_input(struct Fd *fd, char *p, size_t n) {
|
||||
int64_t hOutput;
|
||||
if (fd->kind == kFdConsole) {
|
||||
hOutput = fd->extra;
|
||||
} else {
|
||||
hOutput = g_fds.p[1].handle;
|
||||
}
|
||||
if (__ttymagic & kFdTtyEchoRaw) {
|
||||
WriteFile(hOutput, p, n, 0, 0);
|
||||
} else {
|
||||
size_t i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (isascii(p[i]) && iscntrl(p[i]) && p[i] != '\n' && p[i] != '\t') {
|
||||
char ctl[2];
|
||||
ctl[0] = '^';
|
||||
ctl[1] = p[i] ^ 0100;
|
||||
WriteFile(hOutput, ctl, 2, 0, 0);
|
||||
} else {
|
||||
WriteFile(hOutput, p + i, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
|
||||
bool32 ok;
|
||||
int infd;
|
||||
|
@ -122,3 +253,11 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
|
|||
return enotty();
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((__constructor__)) static void tcsetattr_nt_init(void) {
|
||||
if (!getenv("TERM")) {
|
||||
setenv("TERM", "xterm-256color", true);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -17,14 +17,22 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/sysv/consts/nr.h"
|
||||
|
||||
/**
|
||||
* Sets file mode creation mask.
|
||||
*
|
||||
* On Windows, the value of umask() determines how Cosmopolitan goes
|
||||
* about creating synthetic `st_mode` bits. The default umask is 077
|
||||
* which is based on the assumption that Windows is being used for a
|
||||
* single user. Don't assume that Cosmopolitan Libc will help you to
|
||||
* secure a multitenant Windows computer.
|
||||
*
|
||||
* @return previous mask
|
||||
* @note always succeeds
|
||||
*/
|
||||
|
@ -33,8 +41,7 @@ unsigned umask(unsigned newmask) {
|
|||
if (!IsWindows()) {
|
||||
oldmask = sys_umask(newmask);
|
||||
} else {
|
||||
// TODO(jart): what should we do with this?
|
||||
oldmask = newmask;
|
||||
oldmask = atomic_exchange(&__umask, newmask);
|
||||
}
|
||||
STRACE("umask(%#o) → %#o", oldmask);
|
||||
return oldmask;
|
||||
|
|
|
@ -155,7 +155,7 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
|
|||
sigaddset(&mask, SIGCHLD);
|
||||
__sig_mask(SIG_BLOCK, &mask, &oldmask);
|
||||
do {
|
||||
rc = _check_interrupts(kSigOpRestartable | kSigOpNochld, 0);
|
||||
rc = _check_interrupts(kSigOpRestartable | kSigOpNochld);
|
||||
if (rc == -1) break;
|
||||
__fds_lock();
|
||||
rc = sys_wait4_nt_impl(&pid, opt_out_wstatus, options, opt_out_rusage);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue