mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-27 21:10:29 +00:00
Make improvements
- Polyfill readlink("foo/") dir check on Windows - Support asynchronous signal delivery on Windows - Restore Windows Console from execve() daisy chain - Work around bug in AARCH64 Optimized Routines memcmp() - Disable unbourne.com shell completion on Windows for now - Don't always set virtual terminal input state on console - Remove Musl Libc's unusual preservation of realpath("//") - Make realpath() strongly link malloc() to pass configure test - Delete cosh.com shell, now that unbourne.com works on Windows!
This commit is contained in:
parent
f9c9a323fe
commit
425c055116
40 changed files with 581 additions and 706 deletions
|
@ -73,7 +73,7 @@ typedef int sig_atomic_t;
|
|||
|
||||
bool32 isatty(int);
|
||||
char *getcwd(char *, size_t);
|
||||
char *realpath(const char *, char *);
|
||||
char *realpath(const char *, char *) __wur;
|
||||
char *ttyname(int);
|
||||
int access(const char *, int) dontthrow;
|
||||
int chdir(const char *);
|
||||
|
|
|
@ -65,6 +65,13 @@ $(LIBC_CALLS_A).pkg: \
|
|||
$(LIBC_CALLS_A_OBJS) \
|
||||
$(foreach x,$(LIBC_CALLS_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
# we can't use sanitizers because:
|
||||
# we're on a stack owned by win32 without tls
|
||||
o/$(MODE)/libc/calls/onntconsoleevent.o: private \
|
||||
COPTS += \
|
||||
-ffreestanding \
|
||||
-fno-sanitize=all
|
||||
|
||||
# we can't use asan because:
|
||||
# siginfo_t memory is owned by kernels
|
||||
o/$(MODE)/libc/calls/siginfo2cosmo.o: private \
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
|
@ -56,10 +57,10 @@
|
|||
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;
|
||||
__msabi extern typeof(UnmapViewOfFile) *const __imp_UnmapViewOfFile;
|
||||
__msabi extern typeof(TerminateThread) *const __imp_TerminateThread;
|
||||
__msabi extern typeof(UnmapViewOfFile) *const __imp_UnmapViewOfFile;
|
||||
__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject;
|
||||
|
||||
wontreturn void __switch_stacks(intptr_t, long, long, long,
|
||||
void (*)(intptr_t, intptr_t, long, long),
|
||||
|
@ -70,9 +71,9 @@ __msabi static keywords bool32 sys_execve_nt_event(uint32_t dwCtrlType) {
|
|||
}
|
||||
|
||||
static keywords void PurgeHandle(intptr_t h) {
|
||||
if (h && h != -1) {
|
||||
__imp_CloseHandle(h);
|
||||
}
|
||||
if (!h) return;
|
||||
if (h == -1) return;
|
||||
__imp_CloseHandle(h);
|
||||
}
|
||||
|
||||
static keywords void PurgeThread(intptr_t h) {
|
||||
|
|
|
@ -17,15 +17,14 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows bool32 sys_isatty_nt(int fd) {
|
||||
if (__isfdopen(fd)) {
|
||||
if (__isfdkind(fd, kFdConsole) ||
|
||||
(__isfdkind(fd, kFdFile) &&
|
||||
GetFileType(g_fds.p[fd].handle) == kNtFileTypeChar)) {
|
||||
uint32_t mode;
|
||||
if (GetConsoleMode(g_fds.p[fd].handle, &mode)) {
|
||||
return true;
|
||||
} else {
|
||||
enotty();
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/systeminfo.h"
|
||||
|
|
|
@ -17,49 +17,143 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/nexgen32e/nt2sysv.h"
|
||||
#include "libc/nt/enum/context.h"
|
||||
#include "libc/nt/enum/ctrlevent.h"
|
||||
#include "libc/nt/enum/threadaccess.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/context.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/thread/tls2.internal.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) {
|
||||
struct CosmoTib tib;
|
||||
// WIN32 doesn't have the System V red-zone. Microsoft says they reserve
|
||||
// the right to trample all over it. so we technically don't need to use
|
||||
// this value. it's just not clear how common it is for WIN32 to clobber
|
||||
// the red zone, which means broken code could seem to mostly work which
|
||||
// means it's better that we're not the ones responsible for breaking it
|
||||
#define kRedzoneSize 128
|
||||
|
||||
// win32 spawns a thread on its own just to deliver sigint
|
||||
// TODO(jart): make signal code lockless so we can delete!
|
||||
if (__tls_enabled && !__get_tls_win32()) {
|
||||
bzero(&tib, sizeof(tib));
|
||||
tib.tib_self = &tib;
|
||||
tib.tib_self2 = &tib;
|
||||
atomic_store_explicit(&tib.tib_tid, GetCurrentThreadId(),
|
||||
memory_order_relaxed);
|
||||
__set_tls_win32(&tib);
|
||||
}
|
||||
// Both Microsoft and the Fifth Bell System agree on this one.
|
||||
#define kStackAlign 16
|
||||
|
||||
STRACE("__onntconsoleevent(%u)", dwCtrlType);
|
||||
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
|
||||
__msabi extern typeof(GetLastError) *const __imp_GetLastError;
|
||||
__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle;
|
||||
__msabi extern typeof(GetThreadContext) *const __imp_GetThreadContext;
|
||||
__msabi extern typeof(OpenThread) *const __imp_OpenThread;
|
||||
__msabi extern typeof(ResumeThread) *const __imp_ResumeThread;
|
||||
__msabi extern typeof(SuspendThread) *const __imp_SuspendThread;
|
||||
__msabi extern typeof(WriteFile) *const __imp_WriteFile;
|
||||
|
||||
int WinThreadLaunch(int, int, int (*)(int, int), intptr_t);
|
||||
|
||||
static unsigned long StrLen(const char *s) {
|
||||
unsigned long n = 0;
|
||||
while (*s++) ++n;
|
||||
return n;
|
||||
}
|
||||
|
||||
static void Log(const char *s) {
|
||||
#ifndef NDEBUG
|
||||
__imp_WriteFile(__imp_GetStdHandle(kNtStdErrorHandle), s, StrLen(s), 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int GetSig(uint32_t dwCtrlType) {
|
||||
switch (dwCtrlType) {
|
||||
case kNtCtrlCEvent:
|
||||
__sig_add(0, SIGINT, SI_KERNEL);
|
||||
return true;
|
||||
return SIGINT;
|
||||
case kNtCtrlBreakEvent:
|
||||
__sig_add(0, SIGQUIT, SI_KERNEL);
|
||||
return true;
|
||||
return SIGQUIT;
|
||||
case kNtCtrlCloseEvent:
|
||||
case kNtCtrlLogoffEvent: // only received by services
|
||||
case kNtCtrlShutdownEvent: // only received by services
|
||||
__sig_add(0, SIGHUP, SI_KERNEL);
|
||||
return true;
|
||||
return SIGHUP;
|
||||
default:
|
||||
return false;
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
__msabi textwindows dontinstrument dontasan dontubsan bool32
|
||||
__onntconsoleevent(uint32_t dwCtrlType) {
|
||||
|
||||
// the signal to be delivered
|
||||
int sig = GetSig(dwCtrlType);
|
||||
int sic = SI_KERNEL;
|
||||
|
||||
// if we don't have tls, then we can't hijack a safe stack from a
|
||||
// thread so just try our luck punting the signal to the next i/o
|
||||
if (!__tls_enabled) {
|
||||
goto PuntSignal;
|
||||
}
|
||||
|
||||
// we're on a stack that's owned by win32. to make matters worse,
|
||||
// win32 spawns a totally new thread just to invoke this handler.
|
||||
// that means most of the cosmo runtime is broken right now which
|
||||
// means we can't call the user signal handler safely. what we'll
|
||||
// do instead is pick a posix thread at random to hijack, pretend
|
||||
// to be that thread, use its stack, and then deliver this signal
|
||||
// asynchronously if it isn't blocked. hopefully it won't longjmp
|
||||
// because once the handler returns, we'll restore the old thread
|
||||
bool gotsome = false;
|
||||
pthread_spin_lock(&_pthread_lock);
|
||||
for (struct Dll *e = dll_first(_pthread_list); e && !gotsome;
|
||||
e = dll_next(_pthread_list, e)) {
|
||||
struct PosixThread *pt = POSIXTHREAD_CONTAINER(e);
|
||||
int tid = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire);
|
||||
if (tid <= 0) continue; // -1 means spawning, 0 means terminated
|
||||
if (pt->tib->tib_sigmask & (1ull << (sig - 1))) continue; // blocked
|
||||
intptr_t th;
|
||||
if ((th = __imp_OpenThread(kNtThreadSuspendResume | kNtThreadGetContext,
|
||||
false, tid))) {
|
||||
uint32_t old_suspend_count = __imp_SuspendThread(th);
|
||||
if (old_suspend_count != -1u) {
|
||||
if (!old_suspend_count &&
|
||||
atomic_load_explicit(&pt->status, memory_order_acquire) <
|
||||
kPosixThreadTerminated) {
|
||||
struct NtContext ctx;
|
||||
__repstosb(&ctx, 0, sizeof(ctx));
|
||||
ctx.ContextFlags = kNtContextControl;
|
||||
if (__imp_GetThreadContext(th, &ctx)) {
|
||||
gotsome = true;
|
||||
pthread_spin_unlock(&_pthread_lock);
|
||||
__set_tls_win32(pt->tib);
|
||||
WinThreadLaunch(sig, sic, __sig_raise,
|
||||
ROUNDDOWN(ctx.Rsp - kRedzoneSize, kStackAlign) - 8);
|
||||
} else {
|
||||
Log("GetThreadContext failed\n");
|
||||
}
|
||||
}
|
||||
__imp_ResumeThread(th);
|
||||
} else {
|
||||
Log("SuspendThread failed\n");
|
||||
}
|
||||
__imp_CloseHandle(th);
|
||||
} else {
|
||||
Log("OpenThread failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!gotsome) {
|
||||
pthread_spin_unlock(&_pthread_lock);
|
||||
PuntSignal:
|
||||
__sig_add(0, sig, sic);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -18,17 +18,11 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/macros.internal.h"
|
||||
.text.windows
|
||||
|
||||
__onntconsoleevent_nt:
|
||||
ezlea __onntconsoleevent,ax
|
||||
jmp __nt2sysv
|
||||
.endfn __onntconsoleevent_nt,globl,hidden
|
||||
|
||||
.init.start 300,_init_onntconsoleevent
|
||||
testb IsWindows()
|
||||
jz 1f
|
||||
ezlea __onntconsoleevent_nt,cx
|
||||
ezlea __onntconsoleevent,cx
|
||||
pushpop 1,%rdx
|
||||
ntcall __imp_SetConsoleCtrlHandler
|
||||
1: .init.end 300,_init_onntconsoleevent,globl,hidden
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#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"
|
||||
|
@ -107,6 +106,7 @@ StartOver:
|
|||
// since for overlapped i/o, we always use GetOverlappedResult
|
||||
ok = ReadFile(handle, targetdata, targetsize, 0, &overlap);
|
||||
if (!ok && GetLastError() == kNtErrorIoPending) {
|
||||
TryAgain:
|
||||
// the i/o operation is in flight; blocking is unavoidable
|
||||
// if we're in a non-blocking mode, then immediately abort
|
||||
// if an interrupt is pending then we abort before waiting
|
||||
|
@ -137,6 +137,9 @@ StartOver:
|
|||
// overlapped is allocated on stack, so it's important we wait
|
||||
// for windows to acknowledge that it's done using that memory
|
||||
ok = GetOverlappedResult(handle, &overlap, &got, true);
|
||||
if (!ok && GetLastError() == kNtErrorIoIncomplete) {
|
||||
goto TryAgain;
|
||||
}
|
||||
}
|
||||
CloseHandle(overlap.hEvent);
|
||||
} else {
|
||||
|
|
|
@ -20,8 +20,11 @@
|
|||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/accessmask.h"
|
||||
#include "libc/nt/enum/creationdisposition.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/enum/filesharemode.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/enum/fsctl.h"
|
||||
#include "libc/nt/enum/io.h"
|
||||
#include "libc/nt/files.h"
|
||||
|
@ -34,28 +37,45 @@
|
|||
|
||||
textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf,
|
||||
size_t bufsiz) {
|
||||
|
||||
char16_t path16[PATH_MAX];
|
||||
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
|
||||
size_t len = strlen16(path16);
|
||||
bool must_be_directory = len > 1 && path16[len - 1] == '\\';
|
||||
if (must_be_directory) path16[--len] = 0;
|
||||
|
||||
int64_t h;
|
||||
ssize_t rc;
|
||||
uint64_t w;
|
||||
wint_t x, y;
|
||||
volatile char *memory;
|
||||
uint32_t i, j, n, mem;
|
||||
char16_t path16[PATH_MAX], *p;
|
||||
struct NtReparseDataBuffer *rdb;
|
||||
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
|
||||
mem = 16384;
|
||||
memory = alloca(mem);
|
||||
uint32_t mem = 16384;
|
||||
volatile char *memory = alloca(mem);
|
||||
CheckLargeStackAllocation((char *)memory, mem);
|
||||
rdb = (struct NtReparseDataBuffer *)memory;
|
||||
if ((h = CreateFile(path16, 0, 0, 0, kNtOpenExisting,
|
||||
struct NtReparseDataBuffer *rdb = (struct NtReparseDataBuffer *)memory;
|
||||
if ((h = CreateFile(path16, kNtFileReadAttributes,
|
||||
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete,
|
||||
0, kNtOpenExisting,
|
||||
kNtFileFlagOpenReparsePoint | kNtFileFlagBackupSemantics,
|
||||
0)) != -1) {
|
||||
if (DeviceIoControl(h, kNtFsctlGetReparsePoint, 0, 0, rdb, mem, &n, 0)) {
|
||||
|
||||
// if path had trailing slash, assert last component is directory
|
||||
if (must_be_directory) {
|
||||
struct NtByHandleFileInformation wst;
|
||||
if (GetFileType(h) != kNtFileTypeDisk ||
|
||||
(GetFileInformationByHandle(h, &wst) &&
|
||||
!(wst.dwFileAttributes & kNtFileAttributeDirectory))) {
|
||||
return enotdir();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t bc;
|
||||
if (DeviceIoControl(h, kNtFsctlGetReparsePoint, 0, 0, rdb, mem, &bc, 0)) {
|
||||
if (rdb->ReparseTag == kNtIoReparseTagSymlink) {
|
||||
i = 0;
|
||||
j = 0;
|
||||
n = rdb->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(char16_t);
|
||||
p = (char16_t *)((char *)rdb->SymbolicLinkReparseBuffer.PathBuffer +
|
||||
|
||||
uint32_t i = 0;
|
||||
uint32_t j = 0;
|
||||
uint32_t n =
|
||||
rdb->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(char16_t);
|
||||
char16_t *p =
|
||||
(char16_t *)((char *)rdb->SymbolicLinkReparseBuffer.PathBuffer +
|
||||
rdb->SymbolicLinkReparseBuffer.PrintNameOffset);
|
||||
if (n >= 3 && isalpha(p[0]) && p[1] == ':' && p[2] == '\\') {
|
||||
p[1] = p[0];
|
||||
|
@ -63,15 +83,18 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf,
|
|||
p[2] = '/';
|
||||
}
|
||||
while (i < n) {
|
||||
x = p[i++] & 0xffff;
|
||||
|
||||
wint_t x = p[i++] & 0xffff;
|
||||
if (!IsUcs2(x)) {
|
||||
if (i < n) {
|
||||
y = p[i++] & 0xffff;
|
||||
wint_t y = p[i++] & 0xffff;
|
||||
x = MergeUtf16(x, y);
|
||||
} else {
|
||||
x = 0xfffd;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t w;
|
||||
if (x < 0200) {
|
||||
if (x == '\\') {
|
||||
x = '/';
|
||||
|
@ -95,6 +118,7 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf,
|
|||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
CloseHandle(h);
|
||||
} else {
|
||||
rc = __fix_enotdir(-1, path16);
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Reads symbolic link.
|
||||
|
@ -40,12 +40,20 @@
|
|||
* and this buffer will *not* be nul-terminated
|
||||
* @return number of bytes written to buf, or -1 w/ errno; if the
|
||||
* return is equal to bufsiz then truncation may have occurred
|
||||
* @error EINVAL if path isn't a symbolic link
|
||||
* @raise EINVAL if path isn't a symbolic link
|
||||
* @raise ENOENT if `path` didn't exist
|
||||
* @raise ENOTDIR if parent component existed that's not a directory
|
||||
* @raise ENOTDIR if base component ends with slash and is not a dir
|
||||
* @raise ENAMETOOLONG if symlink-resolved `path` length exceeds `PATH_MAX`
|
||||
* @raise ENAMETOOLONG if component in `path` exists longer than `NAME_MAX`
|
||||
* @raise EBADF on relative `path` when `dirfd` isn't open or `AT_FDCWD`
|
||||
* @raise ELOOP if a loop was detected resolving parent components
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) {
|
||||
ssize_t bytes;
|
||||
if ((IsAsan() && !__asan_is_valid(buf, bufsiz)) || (bufsiz && !buf)) {
|
||||
if ((bufsiz && !buf) || (IsAsan() && (!__asan_is_valid_str(path) ||
|
||||
!__asan_is_valid(buf, bufsiz)))) {
|
||||
bytes = efault();
|
||||
} else if (_weaken(__zipos_notat) &&
|
||||
(bytes = __zipos_notat(dirfd, path)) == -1) {
|
||||
|
|
|
@ -1,235 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╚──────────────────────────────────────────────────────────────────────────────╝
|
||||
│ │
|
||||
│ Musl Libc │
|
||||
│ Copyright © 2005-2014 Rich Felker, et al. │
|
||||
│ │
|
||||
│ Permission is hereby granted, free of charge, to any person obtaining │
|
||||
│ a copy of this software and associated documentation files (the │
|
||||
│ "Software"), to deal in the Software without restriction, including │
|
||||
│ without limitation the rights to use, copy, modify, merge, publish, │
|
||||
│ distribute, sublicense, and/or sell copies of the Software, and to │
|
||||
│ permit persons to whom the Software is furnished to do so, subject to │
|
||||
│ the following conditions: │
|
||||
│ │
|
||||
│ The above copyright notice and this permission notice shall be │
|
||||
│ included in all copies or substantial portions of the Software. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │
|
||||
│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │
|
||||
│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │
|
||||
│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
|
||||
│ │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
#define SYMLOOP_MAX 40
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
Musl libc (MIT License)\\n\
|
||||
Copyright 2005-2014 Rich Felker, et. al.\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
/* clang-format off */
|
||||
|
||||
static inline bool IsSlash(char c)
|
||||
{
|
||||
return c == '/' || c == '\\';
|
||||
}
|
||||
|
||||
static size_t GetSlashLen(const char *s)
|
||||
{
|
||||
const char *s0 = s;
|
||||
while (IsSlash(*s)) s++;
|
||||
return s-s0;
|
||||
}
|
||||
|
||||
static char *ResolvePath(char *d, const char *s, size_t n)
|
||||
{
|
||||
if (d || (_weaken(malloc) && (d = _weaken(malloc)(n+1)))) {
|
||||
return memmove(d, s, n+1);
|
||||
} else {
|
||||
enomem();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns absolute pathname.
|
||||
*
|
||||
* This function removes `/./` and `/../` components. IF the path is a
|
||||
* symbolic link then it's resolved.
|
||||
*
|
||||
* @param resolved needs PATH_MAX bytes or NULL to use malloc()
|
||||
* @return resolved or NULL w/ errno
|
||||
*/
|
||||
char *realpath(const char *filename, char *resolved)
|
||||
{
|
||||
ssize_t rc;
|
||||
int e, up, check_dir=0;
|
||||
size_t k, p, q, l, l0, cnt=0, nup=0;
|
||||
char output[PATH_MAX], stack[PATH_MAX+1], *z;
|
||||
|
||||
/* STRACE("realpath(%#s, %#s)", filename, resolved); */
|
||||
|
||||
if (!filename) {
|
||||
einval();
|
||||
return 0;
|
||||
}
|
||||
l = strnlen(filename, sizeof stack);
|
||||
if (!l) {
|
||||
enoent();
|
||||
return 0;
|
||||
}
|
||||
if (l >= PATH_MAX) goto toolong;
|
||||
if (l >= 4 && READ32LE(filename) == READ32LE("/zip") &&
|
||||
(!filename[4] || filename[4] == '/')) {
|
||||
return ResolvePath(resolved, filename, l);
|
||||
}
|
||||
p = sizeof stack - l - 1;
|
||||
q = 0;
|
||||
memcpy(stack+p, filename, l+1);
|
||||
|
||||
/* Main loop. Each iteration pops the next part from stack of
|
||||
* remaining path components and consumes any slashes that follow.
|
||||
* If not a link, it's moved to output; if a link, contents are
|
||||
* pushed to the stack. */
|
||||
restart:
|
||||
for (; ; p+=GetSlashLen(stack+p)) {
|
||||
/* If stack starts with /, the whole component is / or //
|
||||
* and the output state must be reset. */
|
||||
if (IsSlash(stack[p])) {
|
||||
check_dir=0;
|
||||
nup=0;
|
||||
q=0;
|
||||
output[q++] = '/';
|
||||
p++;
|
||||
/* Initial // is special. */
|
||||
if (IsSlash(stack[p]) && !IsSlash(stack[p+1]))
|
||||
output[q++] = '/';
|
||||
continue;
|
||||
}
|
||||
|
||||
z = (char *)min((intptr_t)strchrnul(stack+p, '/'),
|
||||
(intptr_t)strchrnul(stack+p, '\\'));
|
||||
l0 = l = z-(stack+p);
|
||||
|
||||
if (!l && !check_dir) break;
|
||||
|
||||
/* Skip any . component but preserve check_dir status. */
|
||||
if (l==1 && stack[p]=='.') {
|
||||
p += l;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Copy next component onto output at least temporarily, to
|
||||
* call readlink, but wait to advance output position until
|
||||
* determining it's not a link. */
|
||||
if (q && !IsSlash(output[q-1])) {
|
||||
if (!p) goto toolong;
|
||||
stack[--p] = '/';
|
||||
l++;
|
||||
}
|
||||
if (q+l >= PATH_MAX) goto toolong;
|
||||
if (l) memcpy(output+q, stack+p, l);
|
||||
output[q+l] = 0;
|
||||
p += l;
|
||||
|
||||
up = 0;
|
||||
if (l0==2 && stack[p-2]=='.' && stack[p-1]=='.') {
|
||||
up = 1;
|
||||
/* Any non-.. path components we could cancel start
|
||||
* after nup repetitions of the 3-byte string "../";
|
||||
* if there are none, accumulate .. components to
|
||||
* later apply to cwd, if needed. */
|
||||
if (q <= 3*nup) {
|
||||
nup++;
|
||||
q += l;
|
||||
continue;
|
||||
}
|
||||
/* When previous components are already known to be
|
||||
* directories, processing .. can skip readlink. */
|
||||
if (!check_dir) goto skip_readlink;
|
||||
}
|
||||
e = errno;
|
||||
if ((rc = readlink(output, stack, p)) == -1) {
|
||||
if (errno != EINVAL) return 0;
|
||||
errno = e; /* [jart] undirty errno if not a symlink */
|
||||
skip_readlink:
|
||||
check_dir = 0;
|
||||
if (up) {
|
||||
while(q && !IsSlash(output[q-1])) q--;
|
||||
if (q>1 && (q>2 || !IsSlash(output[0]))) q--;
|
||||
continue;
|
||||
}
|
||||
if (l0) q += l;
|
||||
check_dir = stack[p];
|
||||
continue;
|
||||
}
|
||||
k = rc;
|
||||
npassert(k <= p);
|
||||
if (k==p)
|
||||
goto toolong;
|
||||
if (!k) {
|
||||
errno = ENOENT;
|
||||
return 0;
|
||||
}
|
||||
if (++cnt == SYMLOOP_MAX) {
|
||||
errno = ELOOP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If link contents end in /, strip any slashes already on
|
||||
* stack to avoid /->// or //->/// or spurious toolong. */
|
||||
if (IsSlash(stack[k-1])) {
|
||||
while (IsSlash(stack[p]))
|
||||
p++;
|
||||
}
|
||||
p -= k;
|
||||
memmove(stack+p, stack, k);
|
||||
|
||||
/* Skip the stack advancement in case we have a new
|
||||
* absolute base path. */
|
||||
goto restart;
|
||||
}
|
||||
|
||||
output[q] = 0;
|
||||
|
||||
if (!IsSlash(output[0])) {
|
||||
if (!getcwd(stack, sizeof(stack))) return 0;
|
||||
l = strlen(stack);
|
||||
/* Cancel any initial .. components. */
|
||||
p = 0;
|
||||
while (nup--) {
|
||||
while(l>1 && !IsSlash(stack[l-1])) l--;
|
||||
if (l>1) l--;
|
||||
p += 2;
|
||||
if (p<q) p++;
|
||||
}
|
||||
if (q-p && !IsSlash(stack[l-1])) stack[l++] = '/';
|
||||
if (l + (q-p) + 1 >= PATH_MAX) goto toolong;
|
||||
memmove(output + l, output + p, q - p + 1);
|
||||
if (l) memcpy(output, stack, l);
|
||||
q = l + q-p;
|
||||
}
|
||||
|
||||
return ResolvePath(resolved, output, q);
|
||||
|
||||
toolong:
|
||||
enametoolong();
|
||||
return 0;
|
||||
}
|
|
@ -24,7 +24,6 @@
|
|||
#include "libc/calls/struct/winsize.internal.h"
|
||||
#include "libc/dce.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/sysv/consts/sicode.h"
|
||||
|
@ -66,7 +65,6 @@ __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__ */
|
||||
|
|
|
@ -185,18 +185,23 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
|
|||
if (opt == TCSAFLUSH) {
|
||||
FlushConsoleInputBuffer(hInput);
|
||||
}
|
||||
inmode &=
|
||||
~(kNtEnableLineInput | kNtEnableEchoInput | kNtEnableProcessedInput);
|
||||
inmode &= ~(kNtEnableLineInput | kNtEnableEchoInput |
|
||||
kNtEnableProcessedInput | kNtEnableVirtualTerminalInput);
|
||||
inmode |= kNtEnableWindowInput;
|
||||
__ttymagic = 0;
|
||||
if (tio->c_lflag & ICANON) {
|
||||
inmode |= kNtEnableLineInput;
|
||||
inmode |= kNtEnableLineInput | kNtEnableProcessedInput;
|
||||
} else {
|
||||
__ttymagic |= kFdTtyMunging;
|
||||
if (tio->c_cc[VMIN] != 1) {
|
||||
STRACE("tcsetattr c_cc[VMIN] must be 1 on Windows");
|
||||
return einval();
|
||||
}
|
||||
if (IsAtLeastWindows10()) {
|
||||
// - keys like f1, up, etc. get turned into \e ansi codes
|
||||
// - totally destroys default console gui (e.g. up arrow)
|
||||
inmode |= kNtEnableVirtualTerminalInput;
|
||||
}
|
||||
}
|
||||
if (!(tio->c_iflag & ICRNL)) {
|
||||
__ttymagic |= kFdTtyNoCr2Nl;
|
||||
|
@ -220,13 +225,11 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
|
|||
if (!(tio->c_lflag & ISIG)) {
|
||||
__ttymagic |= kFdTtyNoIsigs;
|
||||
}
|
||||
if (IsAtLeastWindows10()) {
|
||||
inmode |= kNtEnableVirtualTerminalInput;
|
||||
}
|
||||
__vintr = tio->c_cc[VINTR];
|
||||
__vquit = tio->c_cc[VQUIT];
|
||||
if ((tio->c_lflag & ISIG) && //
|
||||
tio->c_cc[VINTR] == CTRL('C')) {
|
||||
// allows ctrl-c to be delivered asynchronously via win32
|
||||
inmode |= kNtEnableProcessedInput;
|
||||
}
|
||||
ok = SetConsoleMode(hInput, inmode);
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/accessmask.h"
|
||||
#include "libc/nt/enum/creationdisposition.h"
|
||||
|
@ -30,6 +32,8 @@
|
|||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/thread/tls2.internal.h"
|
||||
|
||||
/**
|
||||
* @fileoverview makes windows stdin handle capable of being poll'd
|
||||
|
@ -56,11 +60,24 @@ __msabi extern typeof(CreateFile) *const __imp_CreateFileW;
|
|||
__msabi extern typeof(CreateNamedPipe) *const __imp_CreateNamedPipeW;
|
||||
__msabi extern typeof(CreateSemaphore) *const __imp_CreateSemaphoreW;
|
||||
__msabi extern typeof(CreateThread) *const __imp_CreateThread;
|
||||
__msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId;
|
||||
__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle;
|
||||
__msabi extern typeof(ReadFile) *const __imp_ReadFile;
|
||||
__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject;
|
||||
__msabi extern typeof(WriteFile) *const __imp_WriteFile;
|
||||
|
||||
static unsigned long StrLen(const char *s) {
|
||||
unsigned long n = 0;
|
||||
while (*s++) ++n;
|
||||
return n;
|
||||
}
|
||||
|
||||
static void Log(const char *s) {
|
||||
#if 0
|
||||
__imp_WriteFile(__imp_GetStdHandle(kNtStdErrorHandle), s, StrLen(s), 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
__msabi static dontasan dontubsan dontinstrument textwindows uint32_t
|
||||
WinStdinThread(void *lpParameter) {
|
||||
char buf[4096];
|
||||
|
@ -75,19 +92,19 @@ WinStdinThread(void *lpParameter) {
|
|||
__imp_CloseHandle(g_fds.stdin.inisem);
|
||||
|
||||
// relay stdin to process
|
||||
NTTRACE("<stdin> activated");
|
||||
Log("<stdin> activated\n");
|
||||
for (;;) {
|
||||
if (!__imp_ReadFile(g_fds.stdin.handle, buf, sizeof(buf), &got, 0)) {
|
||||
NTTRACE("<stdin> read failed");
|
||||
Log("<stdin> read failed\n");
|
||||
goto Finish;
|
||||
}
|
||||
if (!got) {
|
||||
NTTRACE("<stdin> end of file");
|
||||
Log("<stdin> end of file\n");
|
||||
goto Finish;
|
||||
}
|
||||
for (i = 0; i < got; i += wrote) {
|
||||
if (!__imp_WriteFile(g_fds.stdin.writer, buf + i, got - i, &wrote, 0)) {
|
||||
NTTRACE("<stdin> failed to write to pipe");
|
||||
Log("<stdin> failed to write to pipe\n");
|
||||
goto Finish;
|
||||
}
|
||||
}
|
||||
|
@ -106,13 +123,13 @@ dontasan dontubsan dontinstrument textwindows void WinMainStdin(void) {
|
|||
if (!SupportsWindows()) return;
|
||||
hStdin = __imp_GetStdHandle(kNtStdInputHandle);
|
||||
if (hStdin == kNtInvalidHandleValue) {
|
||||
NTTRACE("<stdin> GetStdHandle failed");
|
||||
Log("<stdin> GetStdHandle failed\n");
|
||||
return;
|
||||
}
|
||||
// create non-inherited semaphore with initial value of 0
|
||||
hSemaphore = __imp_CreateSemaphoreW(0, 0, 1, 0);
|
||||
if (!hSemaphore) {
|
||||
NTTRACE("<stdin> CreateSemaphore failed");
|
||||
Log("<stdin> CreateSemaphore failed\n");
|
||||
return;
|
||||
}
|
||||
__create_pipe_name(pipename);
|
||||
|
@ -120,18 +137,18 @@ dontasan dontubsan dontinstrument textwindows void WinMainStdin(void) {
|
|||
pipename, kNtPipeAccessInbound | kNtFileFlagOverlapped,
|
||||
kNtPipeTypeByte | kNtPipeReadmodeByte, 1, 4096, 4096, 0, 0);
|
||||
if (hReader == kNtInvalidHandleValue) {
|
||||
NTTRACE("<stdin> CreateNamedPipe failed");
|
||||
Log("<stdin> CreateNamedPipe failed\n");
|
||||
return;
|
||||
}
|
||||
hWriter = __imp_CreateFileW(pipename, kNtGenericWrite, 0, 0, kNtOpenExisting,
|
||||
kNtFileFlagOverlapped, 0);
|
||||
if (hWriter == kNtInvalidHandleValue) {
|
||||
NTTRACE("<stdin> CreateFile failed");
|
||||
Log("<stdin> CreateFile failed\n");
|
||||
return;
|
||||
}
|
||||
hThread = __imp_CreateThread(0, 65536, WinStdinThread, 0, 0, 0);
|
||||
if (!hThread) {
|
||||
NTTRACE("<stdin> CreateThread failed");
|
||||
Log("<stdin> CreateThread failed\n");
|
||||
return;
|
||||
}
|
||||
g_fds.stdin.handle = hStdin;
|
||||
|
|
47
libc/calls/winthreadlaunch.S
Normal file
47
libc/calls/winthreadlaunch.S
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 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/macros.internal.h"
|
||||
.text.windows
|
||||
|
||||
// Used by clone() on Windows to launch thread.
|
||||
//
|
||||
// Windows owns the stack memory when we initially enter threads.
|
||||
// This function switches us over, so that we can start using the
|
||||
// runtime facilities.
|
||||
//
|
||||
// @param %rdi is arg1
|
||||
// @param %rsi is arg2
|
||||
// @param %rdx is func
|
||||
// @param %rcx is stack
|
||||
// @return %rax is res
|
||||
// @see clone()
|
||||
WinThreadLaunch:
|
||||
push %rbx
|
||||
push %r15
|
||||
mov %rbp,%r15
|
||||
mov %rsp,%rbx
|
||||
mov %rcx,%rsp
|
||||
xor %rbp,%rbp
|
||||
call *%rdx
|
||||
mov %r15,%rbp
|
||||
mov %rbx,%rsp
|
||||
pop %r15
|
||||
pop %rbx
|
||||
ret
|
||||
.endfn WinThreadLaunch,globl,hidden
|
Loading…
Add table
Add a link
Reference in a new issue