diff --git a/libc/calls/faccessat-nt.c b/libc/calls/faccessat-nt.c index 96b717084..ef2e7c90a 100644 --- a/libc/calls/faccessat-nt.c +++ b/libc/calls/faccessat-nt.c @@ -16,10 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" +#include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall_support-nt.internal.h" -#include "libc/sysv/consts/at.h" -#include "libc/sysv/errfuns.h" textwindows int sys_faccessat_nt(int dirfd, const char *path, int mode, uint32_t flags) { diff --git a/libc/calls/fstat-nt.c b/libc/calls/fstat-nt.c index f403a5cb1..7edac781d 100644 --- a/libc/calls/fstat-nt.c +++ b/libc/calls/fstat-nt.c @@ -101,7 +101,11 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *out_st) { if (!GetFileInformationByHandle(handle, &wst)) { return __winerr(); } - st.st_mode = 0555 & ~umask; + st.st_mode = 0444 & ~umask; + if ((wst.dwFileAttributes & kNtFileAttributeDirectory) || + IsWindowsExecutable(handle)) { + st.st_mode |= 0111 & ~umask; + } st.st_flags = wst.dwFileAttributes; if (!(wst.dwFileAttributes & kNtFileAttributeReadonly)) { st.st_mode |= 0222 & ~umask; diff --git a/libc/calls/fstatat-nt.c b/libc/calls/fstatat-nt.c index 4de3ca706..11b178ca9 100644 --- a/libc/calls/fstatat-nt.c +++ b/libc/calls/fstatat-nt.c @@ -34,7 +34,7 @@ textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st, uint16_t path16[PATH_MAX]; if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; if ((fh = CreateFile( - path16, kNtFileReadAttributes, + path16, kNtFileGenericRead, kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, 0, kNtOpenExisting, kNtFileAttributeNormal | kNtFileFlagBackupSemantics | diff --git a/libc/calls/getuid-nt.c b/libc/calls/getuid-nt.c index 34c88e768..0de3eba66 100644 --- a/libc/calls/getuid-nt.c +++ b/libc/calls/getuid-nt.c @@ -18,13 +18,10 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/intrin/atomic.h" -#include "libc/limits.h" #include "libc/macros.internal.h" #include "libc/nt/accounting.h" -// asan must be disabled because __proc_worker calls this on win32 stack - -static uint32_t __kmp32(const void *buf, size_t size) { +static textwindows uint32_t __kmp32(const void *buf, size_t size) { size_t i; uint32_t h; const uint32_t kPhiPrime = 0x9e3779b1; @@ -39,7 +36,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) & INT_MAX; + tmp = __kmp32(buf, size >> 1) & 32767; if (!tmp) ++tmp; atomic_store_explicit(&uid, tmp, memory_order_release); } diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 38f2b7e00..59a159c1a 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -26,6 +26,7 @@ int __ensurefds_unlocked(int); void __printfds(void); uint32_t sys_getuid_nt(void); int __pause_thread(uint32_t); +int IsWindowsExecutable(int64_t); int CountConsoleInputBytes(int64_t); int FlushConsoleInputBytes(int64_t); diff --git a/libc/calls/ntaccesscheck.c b/libc/calls/ntaccesscheck.c index 9b900870f..47d63870f 100644 --- a/libc/calls/ntaccesscheck.c +++ b/libc/calls/ntaccesscheck.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/errno.h" @@ -24,7 +25,11 @@ #include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" #include "libc/mem/mem.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/securityimpersonationlevel.h" #include "libc/nt/enum/securityinformation.h" #include "libc/nt/errors.h" @@ -50,18 +55,19 @@ * @see libc/sysv/consts.sh */ textwindows int ntaccesscheck(const char16_t *pathname, uint32_t flags) { - int rc, e; - void *freeme; + int rc; bool32 result; + uint32_t flagmask; struct NtSecurityDescriptor *s; struct NtGenericMapping mapping; struct NtPrivilegeSet privileges; - int64_t hToken, hImpersonatedToken; uint32_t secsize, granted, privsize; + int64_t hToken, hImpersonatedToken, hFile; intptr_t buffer[1024 / sizeof(intptr_t)]; - freeme = 0; + if (flags & X_OK) flags |= R_OK; granted = 0; result = false; + flagmask = flags; s = (void *)buffer; secsize = sizeof(buffer); privsize = sizeof(privileges); @@ -70,9 +76,8 @@ textwindows int ntaccesscheck(const char16_t *pathname, uint32_t flags) { mapping.GenericWrite = kNtFileGenericWrite; mapping.GenericExecute = kNtFileGenericExecute; mapping.GenericAll = kNtFileAllAccess; - MapGenericMask(&flags, &mapping); + MapGenericMask(&flagmask, &mapping); hImpersonatedToken = hToken = -1; -TryAgain: if (GetFileSecurity(pathname, kNtOwnerSecurityInformation | kNtGroupSecurityInformation | @@ -84,13 +89,29 @@ TryAgain: &hToken)) { if (DuplicateToken(hToken, kNtSecurityImpersonation, &hImpersonatedToken)) { - if (flags == kNtGenericExecute) { // X_OK - flags |= kNtGenericRead; // R_OK - } - if (AccessCheck(s, hImpersonatedToken, flags, &mapping, &privileges, + if (AccessCheck(s, hImpersonatedToken, flagmask, &mapping, &privileges, &privsize, &granted, &result)) { if (result || flags == F_OK) { - rc = 0; + if (flags & X_OK) { + if ((hFile = CreateFile( + pathname, kNtFileGenericRead, + kNtFileShareRead | kNtFileShareWrite | + kNtFileShareDelete, + 0, kNtOpenExisting, + kNtFileAttributeNormal | kNtFileFlagBackupSemantics, + 0)) != -1) { + if (IsWindowsExecutable(hFile)) { + rc = 0; + } else { + rc = eacces(); + } + CloseHandle(hFile); + } else { + rc = __winerr(); + } + } else { + rc = 0; + } } else { NTTRACE("ntaccesscheck finale failed: result=%d flags=%x", result, flags); @@ -112,25 +133,15 @@ TryAgain: strerror(errno)); } } else { - e = GetLastError(); - if (!IsTiny() && e == kNtErrorInsufficientBuffer) { - if (!freeme && _weaken(malloc) && (freeme = _weaken(malloc)(secsize))) { - s = freeme; - goto TryAgain; - } else { - rc = enomem(); - NTTRACE("%s(%#hs) failed: %s", "GetFileSecurity", pathname, - strerror(errno)); - } - } else { - errno = e; - NTTRACE("%s(%#hs) failed: %s", "GetFileSecurity", pathname, - strerror(errno)); - rc = -1; - } + rc = __winerr(); + NTTRACE("%s(%#hs) failed: %s", "GetFileSecurity", pathname, + strerror(errno)); + } + if (hImpersonatedToken != -1) { + CloseHandle(hImpersonatedToken); + } + if (hToken != -1) { + CloseHandle(hToken); } - if (freeme && _weaken(free)) _weaken(free)(freeme); - if (hImpersonatedToken != -1) CloseHandle(hImpersonatedToken); - if (hToken != -1) CloseHandle(hToken); return rc; } diff --git a/libc/calls/ttyname_r.c b/libc/calls/ttyname_r.c index dcc93a164..554255b12 100644 --- a/libc/calls/ttyname_r.c +++ b/libc/calls/ttyname_r.c @@ -36,11 +36,9 @@ #define FIODGNAME 0x80106678 // freebsd static textwindows errno_t sys_ttyname_nt(int fd, char *buf, size_t size) { - uint32_t mode; - if (GetConsoleMode(g_fds.p[fd].handle, &mode)) { - if (strlcpy(buf, - (mode & kNtEnableVirtualTerminalInput) ? "CONIN$" : "CONOUT$", - size) < size) { + uint32_t cmode; + if (GetConsoleMode(g_fds.p[fd].handle, &cmode)) { + if (strlcpy(buf, "/dev/tty", size) < size) { return 0; } else { return ERANGE; diff --git a/libc/calls/winexec.c b/libc/calls/winexec.c new file mode 100644 index 000000000..4b5ac6bde --- /dev/null +++ b/libc/calls/winexec.c @@ -0,0 +1,28 @@ +/*-*- 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/nt/runtime.h" + +textwindows int IsWindowsExecutable(int64_t handle) { + char buf[2]; + uint32_t got; + return ReadFile(handle, buf, 2, &got, 0) && got == 2 && + ((buf[0] == 'M' && buf[1] == 'Z') || // + (buf[0] == '#' && buf[1] == '!')); +} diff --git a/libc/isystem/arm_acle.h b/libc/isystem/arm_acle.h new file mode 100644 index 000000000..5e695146a --- /dev/null +++ b/libc/isystem/arm_acle.h @@ -0,0 +1,4 @@ +#ifndef COSMOPOLITAN_LIBC_ISYSTEM_ARM_ACLE_H_ +#define COSMOPOLITAN_LIBC_ISYSTEM_ARM_ACLE_H_ +#include "third_party/aarch64/arm_acle.internal.h" +#endif /* COSMOPOLITAN_LIBC_ISYSTEM_ARM_ACLE_H_ */ diff --git a/libc/isystem/arm_bf16.h b/libc/isystem/arm_bf16.h new file mode 100644 index 000000000..8177f26c0 --- /dev/null +++ b/libc/isystem/arm_bf16.h @@ -0,0 +1,4 @@ +#ifndef COSMOPOLITAN_LIBC_ISYSTEM_ARM_BF16_H_ +#define COSMOPOLITAN_LIBC_ISYSTEM_ARM_BF16_H_ +#include "third_party/aarch64/arm_bf16.internal.h" +#endif /* COSMOPOLITAN_LIBC_ISYSTEM_ARM_BF16_H_ */ diff --git a/libc/isystem/arm_fp16.h b/libc/isystem/arm_fp16.h new file mode 100644 index 000000000..2df9b91a2 --- /dev/null +++ b/libc/isystem/arm_fp16.h @@ -0,0 +1,4 @@ +#ifndef COSMOPOLITAN_LIBC_ISYSTEM_ARM_FP16_H_ +#define COSMOPOLITAN_LIBC_ISYSTEM_ARM_FP16_H_ +#include "third_party/aarch64/arm_fp16.internal.h" +#endif /* COSMOPOLITAN_LIBC_ISYSTEM_ARM_FP16_H_ */ diff --git a/libc/isystem/arm_neon.h b/libc/isystem/arm_neon.h new file mode 100644 index 000000000..c59b01ae6 --- /dev/null +++ b/libc/isystem/arm_neon.h @@ -0,0 +1,4 @@ +#ifndef COSMOPOLITAN_LIBC_ISYSTEM_ARM_NEON_H_ +#define COSMOPOLITAN_LIBC_ISYSTEM_ARM_NEON_H_ +#include "third_party/aarch64/arm_neon.internal.h" +#endif /* COSMOPOLITAN_LIBC_ISYSTEM_ARM_NEON_H_ */ diff --git a/libc/sysv/dos2errno.sh b/libc/sysv/dos2errno.sh index 76f43441a..f319c1297 100755 --- a/libc/sysv/dos2errno.sh +++ b/libc/sysv/dos2errno.sh @@ -47,7 +47,7 @@ dos kNtErrorGenFailure EACCES dos kNtErrorGracefulDisconnect EPIPE dos kNtErrorHostDown EHOSTUNREACH dos kNtErrorHostUnreachable EHOSTUNREACH -dos kNtErrorInsufficientBuffer EFAULT +dos kNtErrorInsufficientBuffer ENOBUFS dos kNtErrorNoaccess EFAULT dos kNtErrorInvalidAddress EADDRNOTAVAIL dos kNtErrorNotAReparsePoint EINVAL diff --git a/libc/sysv/dos2errno/EFAULT.S b/libc/sysv/dos2errno/EFAULT.S index 04d391aed..009d25737 100644 --- a/libc/sysv/dos2errno/EFAULT.S +++ b/libc/sysv/dos2errno/EFAULT.S @@ -11,6 +11,5 @@ .globl kDos2Errno.EFAULT .type kDos2Errno.EFAULT,@object kDos2Errno.EFAULT: - .e kNtErrorInsufficientBuffer,EFAULT .e kNtErrorNoaccess,EFAULT .e WSAEFAULT,EFAULT diff --git a/libc/sysv/dos2errno/ENOBUFS.S b/libc/sysv/dos2errno/ENOBUFS.S new file mode 100644 index 000000000..31610cc06 --- /dev/null +++ b/libc/sysv/dos2errno/ENOBUFS.S @@ -0,0 +1,14 @@ +// generated by libc/sysv/dos2errno.sh +#include "libc/nt/errors.h" +#ifndef __x86_64__ + .end +#endif + .macro .e doscode systemv + .short \doscode + .long \systemv + .endm + .section .sort.rodata.dos2errno.2,"a",@progbits + .globl kDos2Errno.ENOBUFS + .type kDos2Errno.ENOBUFS,@object +kDos2Errno.ENOBUFS: + .e kNtErrorInsufficientBuffer,ENOBUFS diff --git a/test/libc/calls/access_test.c b/test/libc/calls/access_test.c index 0e9d1cf60..c7e758da8 100644 --- a/test/libc/calls/access_test.c +++ b/test/libc/calls/access_test.c @@ -61,7 +61,7 @@ TEST(access, test) { } TEST(access, testRequestWriteOnReadOnly_returnsEaccess) { - return; /* TODO(jart): maybe we need root to help? */ + if (1) return; // TODO(jart): maybe we need root to help? ASSERT_SYS(ENOENT, -1, access("file", F_OK)); ASSERT_SYS(0, 0, close(creat("file", 0444))); ASSERT_SYS(0, 0, access("file", F_OK)); @@ -76,3 +76,8 @@ TEST(access, testRequestWriteOnReadOnly_returnsEaccess) { TEST(access, runThisExecutable) { ASSERT_SYS(0, 0, access(GetProgramExecutableName(), R_OK | X_OK)); } + +TEST(access, textFileIsntExecutable) { + ASSERT_SYS(0, 0, touch("foo.txt", 0644)); + ASSERT_SYS(EACCES, -1, access("foo.txt", R_OK | X_OK)); +} diff --git a/test/libc/calls/stat_test.c b/test/libc/calls/stat_test.c index d21c644c2..e505cc288 100644 --- a/test/libc/calls/stat_test.c +++ b/test/libc/calls/stat_test.c @@ -33,7 +33,6 @@ #include "libc/sysv/consts/nr.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" -#include "libc/x/x.h" __static_yoink("zipos"); @@ -61,6 +60,31 @@ TEST(stat, enotdir) { ASSERT_SYS(ENOTDIR, -1, stat("yo/there", &st)); } +TEST(stat, textFileIsntExecutable) { + struct stat st; + ASSERT_SYS(0, 0, touch("foo.txt", 0644)); + ASSERT_SYS(0, 0, stat("foo.txt", &st)); + ASSERT_FALSE(st.st_mode & 0111); +} + +TEST(stat, shebangIsExecutable) { + struct stat st; + ASSERT_SYS(0, 3, creat("foo.sh", 0777)); + ASSERT_SYS(0, 2, write(3, "#!", 2)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 0, stat("foo.sh", &st)); + ASSERT_TRUE(!!(st.st_mode & 0111)); +} + +TEST(stat, portableExecutableIsExecutable) { + struct stat st; + ASSERT_SYS(0, 3, creat("foo.exe", 0777)); + ASSERT_SYS(0, 2, write(3, "MZ", 2)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 0, stat("foo.exe", &st)); + ASSERT_TRUE(!!(st.st_mode & 0111)); +} + TEST(stat, zipos) { struct stat st; EXPECT_SYS(0, 0,