mirror of
https://github.com/jart/cosmopolitan.git
synced 2026-11-07 10:36:04 -10:00
Make improvements
- Open flags (e.g. O_CREAT) are now constants - Error numbers (e.g. EINVAL) are now constants - Signal numbers (e.g. SIGINT) are now constants - fcntl() file locks now work properly on Windows - Significantly improve Windows path handling code - Implement true vfork() on Windows and make fork() faster - Implement scanf %[charset] support and improve conformance - Introduce $COSMOSDRIVE so you can use Windows 11 dev drives - Fix stdio seek/tell bug and make stdio locks faster and safer - Change TLS ABI to use tlscc wrapper that inserts function calls - Make malloc() faster on all platforms using new cosmo_shard() api - Make malloc() fast like tcmalloc on Linux using restartable sequences - Improve fsync() and fdatasync() polyfills so they abstract robustness - Fix CLOCK_BOOTTIME and CLOCK_MONOTONIC on XNU so it reflects uptime - Fix bugs in Windows overlapped code that could possibly cause crash - Fix bugs in Windows signal code that could possibly cause crash - Make symlinks with multiple components work right on Windows - Add FreeBSD ELF note to remove SIGBUS issue and other quirks - Support symlinks on Windows under non-administrator accounts - Make kisdangerous() scalable by adding radix trie to mmap() - Make large realloc() operations zero-cost on all platforms - Fix bugs in readdir() on Windows where d_reclen wasn't set - Make all system call wrappers memory safe (raises EFAULT) - Fix bugs in poll() and select() on Windows with 64+ fds - Fix bug in madvise() on Windows that could cause crash - Support WUNTRACED, SIGSTOP, and SIGCONT on Windows - Increase x86 image base from 0x400000 to 0x4200000 - Introduce -msysv and -mtinylinux flags to cosmocc - Fix issues with siglongjmp() on XNU and NetBSD - Make pthread_barrier_wait() work correctly - Make clock_gettime() faster on FreeBSD ARM - Support file mode bits better on Windows - Improve performance of string libraries - Raise EILSEQ when creating evil paths - Fix std::terminate to not use malloc - Modernize more c11 and c23 features - Make pthread_once() exception safe - Support RLIMIT_CPU on Windows - Introduce HACL* cryptography - Introduce cosmopolitan make - Introduce fasterzip program - Introduce open_memstream() - Introduce dedupe program - Introduce arc4random() - Introduce f2fs support
This commit is contained in:
2593 files changed
+205176
-32550
No files matched your search
@@ -1,6 +1,7 @@
|
||||
# -*- conf -*-
|
||||
|
||||
/o
|
||||
/core
|
||||
/cosmocc
|
||||
/.cosmocc
|
||||
|
||||
|
||||
@@ -110,11 +110,11 @@ ECHO = $(BOOTSTRAP)/echo.ape
|
||||
CHMOD = $(BOOTSTRAP)/chmod.ape
|
||||
TOUCH = $(BOOTSTRAP)/touch.ape
|
||||
PKG = $(BOOTSTRAP)/package.ape
|
||||
MKDEPS = $(BOOTSTRAP)/mkdeps
|
||||
MKDEPS = build/bootstrap/mkdeps
|
||||
ZIPOBJ = $(BOOTSTRAP)/zipobj
|
||||
ZIPCOPY = $(BOOTSTRAP)/zipcopy
|
||||
PECHECK = $(BOOTSTRAP)/pecheck
|
||||
FIXUPOBJ = $(BOOTSTRAP)/fixupobj
|
||||
FIXUPOBJ = build/bootstrap/fixupobj
|
||||
OBJBINCOPY = $(BOOTSTRAP)/objbincopy
|
||||
MKDIR = $(BOOTSTRAP)/mkdir.ape -p
|
||||
COMPILE = $(BOOTSTRAP)/compile.ape -V9 -M2048m -P8192 $(QUOTA)
|
||||
@@ -136,7 +136,7 @@ ARCH = aarch64
|
||||
HOSTS ?= pi pi5 studio freebsdarm
|
||||
else
|
||||
ARCH = x86_64
|
||||
HOSTS ?= freebsd rhel7 xnu openbsd netbsd win10 luna
|
||||
HOSTS ?= freebsd xnu win10 luna netbsd
|
||||
endif
|
||||
|
||||
ZIPOBJ_FLAGS += -a$(ARCH)
|
||||
@@ -155,9 +155,13 @@ DOWNLOAD := $(shell build/download-cosmocc.sh $(COSMOCC) 3.9.2 f4ff13af65fcd309f
|
||||
|
||||
IGNORE := $(shell $(MKDIR) $(TMPDIR))
|
||||
|
||||
ifeq ($(ARCH), x86_64)
|
||||
TLSCC = build/bootstrap/tlscc
|
||||
endif
|
||||
|
||||
AS = $(TOOLCHAIN)as
|
||||
CC = $(TOOLCHAIN)gcc
|
||||
CXX = $(TOOLCHAIN)g++
|
||||
CC = $(TLSCC) $(TOOLCHAIN)gcc
|
||||
CXX = $(TLSCC) $(TOOLCHAIN)g++
|
||||
CXXFILT = $(TOOLCHAIN)c++filt
|
||||
LD = $(TOOLCHAIN)ld.bfd
|
||||
NM = $(TOOLCHAIN)nm
|
||||
@@ -190,39 +194,41 @@ o/$(MODE): \
|
||||
o/$(MODE)/: o/$(MODE)
|
||||
o/$(MODE)/.: o/$(MODE)
|
||||
|
||||
# check if we're using o//third_party/make/make
|
||||
# we added sandboxing to guarantee cosmo's makefile is hermetic
|
||||
# it also shaves away 200ms of startup latency with native $(uniq)
|
||||
ifneq ($(LANDLOCKMAKE_VERSION),)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
ifeq ($(wildcard /usr/bin/ape),)
|
||||
$(warning please run ape/apeinstall.sh if you intend to use landlock make)
|
||||
$(shell sleep .5)
|
||||
endif
|
||||
endif
|
||||
ifneq ($(TOOLCHAIN),)
|
||||
.STRICT = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
.PLEDGE += stdio rpath wpath cpath fattr proc
|
||||
.UNVEIL += \
|
||||
libc/integral \
|
||||
libc/stdbool.h \
|
||||
rwc:/dev/shm \
|
||||
# If you're using Cosmopolitan Make as your `make` command (you can
|
||||
# build it by saying `make o//third_party/make/make`) then these
|
||||
# variables will activate its pledge() and unveil() sandboxing
|
||||
# functionality when running on Linux. When cross-compiling, it's
|
||||
# important that your qemu binaries be statically compiled. See
|
||||
# third_party/qemu/ for our prebuilt qemu-aarch64 binary. Yes
|
||||
# binfmt_misc has to be configured to use it on foreign bins.
|
||||
.SANDBOXED = 1
|
||||
.PLEDGE := stdio rpath wpath cpath fattr proc
|
||||
.UNVEIL := \
|
||||
rwcx:o \
|
||||
rwcx:/tmp \
|
||||
rwcx:$(TMPDIR) \
|
||||
rx:/usr/bin/ape \
|
||||
rx:.cosmocc \
|
||||
rx:build/bootstrap \
|
||||
r:build/portcosmo.h \
|
||||
libc/integral \
|
||||
rwc:/dev/shm \
|
||||
/proc/stat \
|
||||
rw:/dev/null \
|
||||
rw:/dev/full \
|
||||
w:o/stack.log \
|
||||
/etc/hosts \
|
||||
/etc/ape.trust \
|
||||
/etc/ape.revoke \
|
||||
/etc/ssh/ssh_host_ed25519_key.pub \
|
||||
~/.runit.psk \
|
||||
~/.ssh/authorized_keys \
|
||||
~/.ssh/id_ed25519.pub \
|
||||
~/.ape.trust \
|
||||
~/.ape.revoke \
|
||||
/proc/self/status \
|
||||
rx:/usr/bin/qemu-aarch64 \
|
||||
rx:o/third_party/qemu/qemu-aarch64 \
|
||||
/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
|
||||
rx:/usr/bin/qemu-x86_64
|
||||
endif
|
||||
|
||||
PKGS =
|
||||
|
||||
@@ -257,8 +263,10 @@ include third_party/gdtoa/BUILD.mk # ├──DYNAMIC RUNTIME
|
||||
include third_party/nsync/mem/BUILD.mk # │ You can now use stdio
|
||||
include libc/proc/BUILD.mk # │ You can now use threads
|
||||
include libc/dlopen/BUILD.mk # │ You can now use processes
|
||||
include libc/thread/BUILD.mk # │ You can finally call malloc()
|
||||
include third_party/libunwind/BUILD.mk # | You can finally call malloc()
|
||||
include libc/thread/BUILD.mk # │
|
||||
include third_party/zlib/BUILD.mk # │
|
||||
include third_party/haclstar/BUILD.mk # │
|
||||
include libc/stdio/BUILD.mk # │
|
||||
include tool/hello/BUILD.mk # │
|
||||
include third_party/tz/BUILD.mk # │
|
||||
@@ -284,15 +292,15 @@ include dsp/scale/BUILD.mk # │
|
||||
include dsp/mpeg/BUILD.mk # │
|
||||
include dsp/tty/BUILD.mk # │
|
||||
include dsp/audio/BUILD.mk # │
|
||||
include dsp/prog/BUILD.mk # │
|
||||
include dsp/BUILD.mk # │
|
||||
include third_party/stb/BUILD.mk # │
|
||||
include third_party/mbedtls/BUILD.mk # │
|
||||
include third_party/ncurses/BUILD.mk # │
|
||||
include third_party/readline/BUILD.mk # │
|
||||
include third_party/libunwind/BUILD.mk # |
|
||||
include third_party/libcxxabi/BUILD.mk # |
|
||||
include third_party/miniaudio/BUILD.mk # |
|
||||
include third_party/double-conversion/BUILD.mk # │
|
||||
include dsp/prog/BUILD.mk # │
|
||||
include ctl/BUILD.mk # │
|
||||
include third_party/libcxx/BUILD.mk # │
|
||||
include third_party/openmp/BUILD.mk # │
|
||||
@@ -305,6 +313,7 @@ include third_party/nsync/testing/BUILD.mk
|
||||
include libc/testlib/BUILD.mk
|
||||
include tool/viz/lib/BUILD.mk
|
||||
include tool/args/BUILD.mk
|
||||
include tool/chat/BUILD.mk
|
||||
include test/math/BUILD.mk
|
||||
include test/posix/BUILD.mk
|
||||
include test/ctl/BUILD.mk
|
||||
@@ -314,6 +323,8 @@ include third_party/linenoise/BUILD.mk
|
||||
include third_party/maxmind/BUILD.mk
|
||||
include net/finger/BUILD.mk
|
||||
include third_party/double-conversion/test/BUILD.mk
|
||||
include third_party/dash/BUILD.mk
|
||||
include third_party/netcat/BUILD.mk
|
||||
include third_party/lua/BUILD.mk
|
||||
include third_party/tree/BUILD.mk
|
||||
include third_party/zstd/BUILD.mk
|
||||
@@ -414,24 +425,24 @@ o/$(MODE)/hdrs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS) $(
|
||||
o/$(MODE)/incs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(INCS) $(INCS),$(dir $(x)))) $(INCS) $(INCS)
|
||||
$(file >$@,$(INCS))
|
||||
o/$(MODE)/depend: o/$(MODE)/.x o/$(MODE)/srcs.txt o/$(MODE)/hdrs.txt o/$(MODE)/incs.txt $(SRCS) $(HDRS) $(INCS)
|
||||
$(COMPILE) -AMKDEPS -L320 $(MKDEPS) -o $@ -s -r o/$(MODE)/ @o/$(MODE)/srcs.txt @o/$(MODE)/hdrs.txt @o/$(MODE)/incs.txt
|
||||
$(COMPILE) -AMKDEPS -L320 $(MKDEPS) -o $@ -s -r o/$(MODE)/ -S c++:third_party/libcxx/ -S libc/isystem/ @o/$(MODE)/srcs.txt @o/$(MODE)/hdrs.txt @o/$(MODE)/incs.txt
|
||||
|
||||
o/$(MODE)/srcs-old.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(SRCS),$(dir $(x))))
|
||||
$(file >$@) $(foreach x,$(SRCS),$(file >>$@,$(x)))
|
||||
o/$(MODE)/hdrs-old.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS) $(INCS),$(dir $(x))))
|
||||
$(file >$@) $(foreach x,$(HDRS) $(INCS),$(file >>$@,$(x)))
|
||||
|
||||
TAGS: private .UNSANDBOXED = 1
|
||||
TAGS: private .SANDBOXED = 0
|
||||
TAGS: o/$(MODE)/srcs-old.txt $(SRCS) #o/$(MODE)/third_party/ctags/ctags
|
||||
@$(RM) $@
|
||||
@o/$(MODE)/third_party/ctags/ctags $(TAGSFLAGS) -L $< -o $@
|
||||
|
||||
HTAGS: private .UNSANDBOXED = 1
|
||||
HTAGS: private .SANDBOXED = 0
|
||||
HTAGS: o/$(MODE)/hdrs-old.txt $(filter-out third_party/libcxx/%,$(HDRS)) #o/$(MODE)/third_party/ctags/ctags
|
||||
@$(RM) $@
|
||||
@build/htags o/$(MODE)/third_party/ctags/ctags -L $< -o $@
|
||||
|
||||
loc: private .UNSANDBOXED = 1
|
||||
loc: private .SANDBOXED = 0
|
||||
loc: o/$(MODE)/tool/build/summy
|
||||
find -name \*.h -or -name \*.hpp -or -name \*.c -or -name \*.cc -or -name \*.cpp -or -name \*.S -or -name \*.mk | \
|
||||
$(XARGS) wc -l | grep total | awk '{print $$1}' | $<
|
||||
@@ -462,6 +473,7 @@ COSMOPOLITAN = \
|
||||
LIBC_NT_SHELL32 \
|
||||
LIBC_NT_SYNCHRONIZATION \
|
||||
LIBC_NT_USER32 \
|
||||
LIBC_NT_WINMM \
|
||||
LIBC_NT_WS2_32 \
|
||||
LIBC_PROC \
|
||||
LIBC_RUNTIME \
|
||||
@@ -555,7 +567,7 @@ COSMOPOLITAN_H_ROOT_HDRS = \
|
||||
libc/integral/normalize.inc \
|
||||
$(foreach x,$(COSMOPOLITAN_H_PKGS),$($(x)_HDRS))
|
||||
|
||||
o/cosmopolitan.html: private .UNSANDBOXED = 1
|
||||
o/cosmopolitan.html: private .SANDBOXED = 0
|
||||
o/cosmopolitan.html: \
|
||||
o/$(MODE)/third_party/chibicc/chibicc.dbg \
|
||||
$(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS))) \
|
||||
@@ -578,6 +590,7 @@ ifeq ($(ARCH), x86_64)
|
||||
TOOLCHAIN_ARTIFACTS = \
|
||||
o/$(MODE)/ape/ape.lds \
|
||||
o/$(MODE)/libc/crt/crt.o \
|
||||
o/$(MODE)/libc/crt/crtfastmath.o \
|
||||
o/$(MODE)/ape/ape.elf \
|
||||
o/$(MODE)/ape/ape.o \
|
||||
o/$(MODE)/ape/ape-copy-self.o \
|
||||
@@ -596,6 +609,7 @@ TOOLCHAIN_ARTIFACTS = \
|
||||
o/$(MODE)/ape/ape.elf \
|
||||
o/$(MODE)/ape/aarch64.lds \
|
||||
o/$(MODE)/libc/crt/crt.o \
|
||||
o/$(MODE)/libc/crt/crtfastmath.o \
|
||||
o/$(MODE)/cosmopolitan.a \
|
||||
o/$(MODE)/third_party/libcxx/libcxx.a \
|
||||
o/$(MODE)/tool/build/march-native \
|
||||
@@ -611,7 +625,7 @@ clean_toolchain:
|
||||
$(RM) $(TOOLCHAIN_ARTIFACTS)
|
||||
|
||||
aarch64: private .INTERNET = true
|
||||
aarch64: private .UNSANDBOXED = true
|
||||
aarch64: private .SANDBOXED = 0
|
||||
aarch64:
|
||||
$(MAKE) m=aarch64
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ o/$(MODE)/ape/aarch64.lds: \
|
||||
libc/thread/tls.h \
|
||||
libc/calls/struct/timespec.h \
|
||||
libc/macros.h \
|
||||
libc/sysv/consts/prot.h \
|
||||
libc/dce.h \
|
||||
libc/str/str.h
|
||||
|
||||
APE_LOADER_LDFLAGS = \
|
||||
|
||||
+13
-2
@@ -4,6 +4,8 @@
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#define CACHELINE 64
|
||||
|
||||
ENTRY(_start)
|
||||
OUTPUT_ARCH(aarch64)
|
||||
OUTPUT_FORMAT("elf64-littleaarch64",
|
||||
@@ -73,13 +75,16 @@ SECTIONS {
|
||||
KEEP(*(SORT_NONE(.fini)))
|
||||
} =0x1f2003d5
|
||||
|
||||
. += CONSTANT(MAXPAGESIZE);
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
__privileged_start = .;
|
||||
|
||||
.privileged : {
|
||||
__privileged_start = ABSOLUTE(.) & -CONSTANT(MAXPAGESIZE);
|
||||
*(.privileged*)
|
||||
} =0x1f2003d6
|
||||
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
_rodata = .;
|
||||
|
||||
.rodata : {
|
||||
KEEP(*(.rodata.pytab.0));
|
||||
KEEP(*(.rodata.pytab.1));
|
||||
@@ -200,6 +205,9 @@ SECTIONS {
|
||||
.data : {
|
||||
__data_start = .;
|
||||
KEEP(*(SORT_BY_NAME(.piro.data.sort.*)))
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.data.rarechange)
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.data .data.* .gnu.linkonce.d.*)
|
||||
KEEP(*(SORT_BY_NAME(.sort.data.*)))
|
||||
SORT(CONSTRUCTORS)
|
||||
@@ -214,6 +222,9 @@ SECTIONS {
|
||||
|
||||
.bss : {
|
||||
*(.dynbss)
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.bss.rarechange)
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.bss .bss.* .gnu.linkonce.b.*)
|
||||
KEEP(*(SORT_BY_NAME(.piro.bss.sort.*)))
|
||||
*(COMMON)
|
||||
|
||||
@@ -789,6 +789,33 @@ ape.ident:
|
||||
.type ape.ident,@object
|
||||
.previous
|
||||
|
||||
#if SupportsFreebsd()
|
||||
//
|
||||
// This PT_NOTE enables the following kernel behaviors:
|
||||
//
|
||||
// - P_OSREL_SIGSEGV ensures SIGEGV signals aren't misdelivered as
|
||||
// as SIGBUS with invalid siginfo fields.
|
||||
//
|
||||
// - P_OSREL_SHUTDOWN_ENOTCONN is probably a good thing
|
||||
//
|
||||
// - P_OSREL_SIGWAIT prevents sigwait() from raising EINTR
|
||||
//
|
||||
// - P_OSREL_MAP_FSTRICT (basically MAP_SHARED_VALIDATE?)
|
||||
//
|
||||
.section .note.freebsd.ident,"a",@progbits
|
||||
.balign 4
|
||||
freebsd.ident:
|
||||
.long 2f-1f
|
||||
.long 4f-3f
|
||||
.long 1 // NT_FREEBSD_ABI_TAG
|
||||
1: .asciz "FreeBSD"
|
||||
2: .balign 4
|
||||
3: .long 1300000 // v13.0+ c. 2020
|
||||
4: .size freebsd.ident,.-freebsd.ident
|
||||
.type freebsd.ident,@object
|
||||
.previous
|
||||
#endif /* SupportsFreebsd() */
|
||||
|
||||
#if SupportsOpenbsd()
|
||||
.section .note.openbsd.ident,"a",@progbits
|
||||
.balign 4
|
||||
@@ -1086,6 +1113,19 @@ ape_pe: .ascin "PE",4
|
||||
.long PERDAT // Flags
|
||||
.previous
|
||||
|
||||
.section .pe.sections,"a",@progbits
|
||||
.ascin ".idata",8 // Section Name
|
||||
.long ape_idt_memsz // Virtual Size or Physical Address
|
||||
.long ape_idt_rva // Relative Virtual Address
|
||||
.long ape_idt_filesz // Physical Size
|
||||
.long ape_idt_offset // Physical Offset
|
||||
.long 0 // Relocation Table Offset
|
||||
.long 0 // Line Number Table Offset
|
||||
.short 0 // Relocation Count
|
||||
.short 0 // Line Number Count
|
||||
.long PEIMPS // Flags
|
||||
.previous
|
||||
|
||||
.section .pe.sections,"a",@progbits
|
||||
.ascin ".data",8 // Section Name
|
||||
.long ape_ram_memsz // Virtual Size or Physical Address
|
||||
@@ -1711,6 +1751,7 @@ kernel: movabs $ape_stack_vaddr,%rsp
|
||||
mov $0x79000,%esi
|
||||
mov $0x7f000,%edx
|
||||
call __reclaim_boot_pages
|
||||
movl $1,__pib(%rip)
|
||||
push $_HOSTMETAL // sets __hostos in crt.S
|
||||
pop %rcx
|
||||
pushq .Lenv0(%rip) // envp[0][0]
|
||||
@@ -1828,7 +1869,7 @@ __bss_end:
|
||||
.section .notice,"aR",@progbits
|
||||
.asciz "\n\n\
|
||||
Cosmopolitan\n\
|
||||
Copyright 2024 Justine Alexandra Roberts Tunney\n\
|
||||
Copyright 2025 Justine Alexandra Roberts Tunney\n\
|
||||
\n\
|
||||
Permission to use, copy, modify, and/or distribute this software for\n\
|
||||
any purpose with or without fee is hereby granted, provided that the\n\
|
||||
@@ -1844,4 +1885,4 @@ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n\
|
||||
PERFORMANCE OF THIS SOFTWARE."
|
||||
|
||||
.end
|
||||
|
||||
|
||||
+41
-16
@@ -186,6 +186,8 @@
|
||||
/* uncomment if .com.dbg won't execute on your kernel (will break .com file) */
|
||||
/* #define APE_FIX_COM_DBG */
|
||||
|
||||
#define CACHELINE 64
|
||||
|
||||
#ifdef __x86__
|
||||
#define CODE_GRANULE 1
|
||||
#else
|
||||
@@ -199,7 +201,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef IMAGE_BASE_VIRTUAL
|
||||
#define IMAGE_BASE_VIRTUAL 0x400000
|
||||
#define IMAGE_BASE_VIRTUAL 0x4200000
|
||||
#endif
|
||||
|
||||
#if IMAGE_BASE_VIRTUAL > 0xffffffff
|
||||
@@ -237,11 +239,10 @@ SECTIONS {
|
||||
KEEP(*(.elf.phdrs))
|
||||
ape_phdrs_end = .;
|
||||
|
||||
/* OpenBSD */
|
||||
/* ELF PT_NOTE data structures */
|
||||
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
|
||||
ape_note = .;
|
||||
KEEP(*(.note.openbsd.ident))
|
||||
KEEP(*(.note.netbsd.ident))
|
||||
KEEP(*(.note.*))
|
||||
ape_note_end = .;
|
||||
|
||||
/* Portable Executable */
|
||||
@@ -321,6 +322,7 @@ SECTIONS {
|
||||
/*BEGIN: Read Only Data */
|
||||
|
||||
.rodata ALIGN(CONSTANT(COMMONPAGESIZE)) : {
|
||||
_rodata = .;
|
||||
KEEP(*(.rodata.pytab.0));
|
||||
KEEP(*(.rodata.pytab.1));
|
||||
KEEP(*(.rodata.pytab.2));
|
||||
@@ -342,12 +344,6 @@ SECTIONS {
|
||||
|
||||
/*BEGIN: read-only data that's only needed for initialization */
|
||||
|
||||
#if SupportsWindows()
|
||||
/* Windows DLL Import Directory */
|
||||
KEEP(*(.idata.ro));
|
||||
KEEP(*(SORT_BY_NAME(.idata.ro.*)))
|
||||
#endif
|
||||
|
||||
/* Encoded Data Structures w/ Linear Initialization Order */
|
||||
KEEP(*(.initroprologue))
|
||||
KEEP(*(SORT_BY_NAME(.initro.*)))
|
||||
@@ -386,6 +382,17 @@ SECTIONS {
|
||||
_tbss_end = .;
|
||||
} :Tls
|
||||
|
||||
.idata : {
|
||||
#if SupportsWindows()
|
||||
KEEP(*(.dllstr));
|
||||
KEEP(*(.idata.ro));
|
||||
KEEP(*(SORT_BY_NAME(.idata.ro.*)));
|
||||
KEEP(*(SORT_BY_NAME(.piro.data.sort.iat.*)));
|
||||
KEEP(*(.data.nt.*));
|
||||
#endif
|
||||
. = ALIGN(. != 0 ? CONSTANT(COMMONPAGESIZE) : 0);
|
||||
} :Ram
|
||||
|
||||
.eh_frame : {
|
||||
__eh_frame_start = .;
|
||||
KEEP(*(.eh_frame))
|
||||
@@ -395,11 +402,14 @@ SECTIONS {
|
||||
|
||||
.data . : {
|
||||
/*BEGIN: Read/Write Data */
|
||||
#if SupportsWindows()
|
||||
KEEP(*(SORT_BY_NAME(.piro.data.sort.iat.*)))
|
||||
#endif
|
||||
/*BEGIN: NT FORK COPYING */
|
||||
KEEP(*(.dataprologue))
|
||||
|
||||
/* Precious Runtime Constants */
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.data.rarechange)
|
||||
. = ALIGN(CACHELINE);
|
||||
|
||||
*(.data .data.*)
|
||||
*(.gnu_extab)
|
||||
*(.gcc_except_table .gcc_except_table.*)
|
||||
@@ -464,6 +474,11 @@ SECTIONS {
|
||||
. += . > 0 ? CODE_GRANULE : 0;
|
||||
/*END: Post-Initialization Read-Only */
|
||||
|
||||
/* Precious Runtime Constants */
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.bss.rarechange)
|
||||
. = ALIGN(CACHELINE);
|
||||
|
||||
/* Statically Allocated Empty Space */
|
||||
*(SORT_BY_ALIGNMENT(.bss))
|
||||
*(SORT_BY_ALIGNMENT(.bss.*))
|
||||
@@ -549,7 +564,7 @@ SECTIONS {
|
||||
}
|
||||
}
|
||||
|
||||
ape_pe_filealignment = 512;
|
||||
ape_pe_filealignment = 4096;
|
||||
ape_pe_sectionalignment = 4096;
|
||||
ape_pe_sizeofheaders = SIZEOF(.head);
|
||||
ape_pe_sizeofimage = ROUNDUP(_end - __executable_start, ape_pe_sectionalignment);
|
||||
@@ -586,6 +601,14 @@ ape_rom_memsz = ape_rom_filesz;
|
||||
ape_rom_align = CONSTANT(COMMONPAGESIZE);
|
||||
ape_rom_rva = RVA(ape_rom_vaddr);
|
||||
|
||||
ape_idt_vaddr = ADDR(.idata);
|
||||
ape_idt_offset = ape_idt_vaddr - __executable_start;
|
||||
ape_idt_paddr = LOADADDR(.idata);
|
||||
ape_idt_filesz = ADDR(.eh_frame) - ADDR(.idata);
|
||||
ape_idt_memsz = ape_idt_filesz;
|
||||
ape_idt_align = CONSTANT(COMMONPAGESIZE);
|
||||
ape_idt_rva = RVA(ape_idt_vaddr);
|
||||
|
||||
ape_ram_vaddr = ADDR(.eh_frame);
|
||||
ape_ram_offset = ape_ram_vaddr - __executable_start;
|
||||
ape_ram_paddr = LOADADDR(.eh_frame);
|
||||
@@ -600,7 +623,7 @@ ape_stack_offset = 0;
|
||||
ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000;
|
||||
ape_stack_paddr = ape_ram_paddr + ape_ram_filesz;
|
||||
ape_stack_filesz = 0;
|
||||
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 4 * 1024 * 1024;
|
||||
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 8 * 1024 * 1024; /* for gnu make */
|
||||
|
||||
ape_note_offset = ape_cod_offset + (ape_note - ape_cod_vaddr);
|
||||
ape_note_filesz = ape_note_end - ape_note;
|
||||
@@ -714,8 +737,10 @@ CHURN(ape_pe_sections);
|
||||
CHURN(ape_pe_sections_end);
|
||||
CHURN(ape_pe_shnum);
|
||||
CHURN(ape_phdrs_end);
|
||||
CHURN(WinMain);
|
||||
#endif /* SupportsWindows() */
|
||||
#if SupportsWindows()
|
||||
CHURN(WinMain);
|
||||
#endif
|
||||
#endif /* SupportsXnu() */
|
||||
|
||||
#if SupportsWindows() || SupportsMetal()
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
*/.section ".piro.data.sort.iat.2.\name\().3","awG",@progbits,"\name",comdat
|
||||
.quad 0
|
||||
.previous
|
||||
.section .rodata.str1.1,"aSM",@progbits,1
|
||||
.section .dllstr,"aSM",@progbits,1
|
||||
".Lidata.str.\name":
|
||||
.asciz "\name\().dll"
|
||||
.previous
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
#define IMAGE_BASE_REAL 0x2000
|
||||
|
||||
#ifndef IMAGE_BASE_VIRTUAL
|
||||
#define IMAGE_BASE_VIRTUAL 0x400000
|
||||
#define IMAGE_BASE_VIRTUAL 0x4200000
|
||||
#endif
|
||||
|
||||
#ifndef IMAGE_BASE_PHYSICAL
|
||||
|
||||
@@ -6,6 +6,7 @@ extern const char __notices[] __attribute__((__weak__));
|
||||
extern unsigned char __executable_start[] __attribute__((__weak__));
|
||||
extern unsigned char __privileged_start[] __attribute__((__weak__));
|
||||
extern unsigned char _ehead[] __attribute__((__weak__));
|
||||
extern unsigned char _rodata[] __attribute__((__weak__));
|
||||
extern unsigned char _etext[] __attribute__((__weak__));
|
||||
extern unsigned char _edata[] __attribute__((__weak__));
|
||||
extern unsigned char _ezip[] __attribute__((__weak__));
|
||||
@@ -28,5 +29,8 @@ extern unsigned long __got_start[] __attribute__((__weak__));
|
||||
extern unsigned long __got_end[] __attribute__((__weak__));
|
||||
extern unsigned char ape_phdrs[] __attribute__((__weak__));
|
||||
|
||||
#define __rarechange _Section(".bss.rarechange")
|
||||
#define __data_rarechange _Section(".data.rarechange")
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_APE_SECTIONS_INTERNAL_H_ */
|
||||
+48
-72
@@ -361,13 +361,13 @@ Here's the TLS memory layout on x86_64:
|
||||
```
|
||||
__get_tls()
|
||||
│
|
||||
%fs OpenBSD/NetBSD
|
||||
%fs Linux/FreeBSD/NetBSD/OpenBSD
|
||||
_Thread_local │
|
||||
┌───┬──────────┬──────────┼───┐
|
||||
│pad│ .tdata │ .tbss │tib│
|
||||
└───┴──────────┴──────────┼───┘
|
||||
│
|
||||
Linux/FreeBSD/Windows/Mac %gs
|
||||
Windows/Mac %gs
|
||||
```
|
||||
|
||||
Quite possibly the greatest challenge in Actually Portable Executable
|
||||
@@ -401,80 +401,56 @@ arises in the fact that our Linux-flavored GCC and Clang toolchains
|
||||
(which are used to produce cross-OS binaries) are also only capable of
|
||||
producing TLS instructions that use the %fs convention.
|
||||
|
||||
To solve these challenges, the `cosmocc` compiler will rewrite binary
|
||||
objects after they've been compiled by GCC, so that the `%gs` register
|
||||
is used, rather than `%fs`. Morphing x86-64 binaries after they've been
|
||||
compiled is normally difficult, due to the complexity of the machine
|
||||
instruction language. However GCC provides `-mno-tls-direct-seg-refs`
|
||||
which greatly reduces the complexity of this task. This flag forgoes
|
||||
some optimizations to make the generated code simpler. Rather than doing
|
||||
clever arithmetic with `%fs` prefixes, the compiler will always generate
|
||||
the thread information block address load as a separate instruction.
|
||||
To solve these challenges, the `cosmocc` compiler rewrites GCC generated
|
||||
assembly so that the `%fs` referencing instructions created by GCC when
|
||||
using the `-mno-tls-direct-seg-refs` flag are replaced by calls to the
|
||||
following functions:
|
||||
|
||||
```c
|
||||
// Change AMD code to use %gs:0x30 instead of %fs:0
|
||||
// We assume -mno-tls-direct-seg-refs has been used
|
||||
static void ChangeTlsFsToGs(unsigned char *p, size_t n) {
|
||||
unsigned char *e = p + n - 9;
|
||||
while (p <= e) {
|
||||
// we're checking for the following expression:
|
||||
// 0144 == p[0] && // %fs
|
||||
// 0110 == (p[1] & 0373) && // rex.w (and ignore rex.r)
|
||||
// (0213 == p[2] || // mov reg/mem → reg (word-sized)
|
||||
// 0003 == p[2]) && // add reg/mem → reg (word-sized)
|
||||
// 0004 == (p[3] & 0307) && // mod/rm (4,reg,0) means sib → reg
|
||||
// 0045 == p[4] && // sib (5,4,0) → (rbp,rsp,0) → disp32
|
||||
// 0000 == p[5] && // displacement (von Neumann endian)
|
||||
// 0000 == p[6] && // displacement
|
||||
// 0000 == p[7] && // displacement
|
||||
// 0000 == p[8] // displacement
|
||||
uint64_t w = READ64LE(p) & READ64LE("\377\373\377\307\377\377\377\377");
|
||||
if ((w == READ64LE("\144\110\213\004\045\000\000\000") ||
|
||||
w == READ64LE("\144\110\003\004\045\000\000\000")) &&
|
||||
!p[8]) {
|
||||
p[0] = 0145; // change %fs to %gs
|
||||
p[5] = 0x30; // change 0 to 0x30
|
||||
p += 9;
|
||||
} else {
|
||||
++p;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
- `__get_tls_rax`
|
||||
- `__get_tls_rbx`
|
||||
- `__get_tls_rcx`
|
||||
- `__get_tls_rdx`
|
||||
- `__get_tls_rdi`
|
||||
- `__get_tls_rsi`
|
||||
- `__get_tls_rbp`
|
||||
- `__get_tls_r8`
|
||||
- `__get_tls_r9`
|
||||
- `__get_tls_r10`
|
||||
- `__get_tls_r11`
|
||||
- `__get_tls_r12`
|
||||
- `__get_tls_r13`
|
||||
- `__get_tls_r14`
|
||||
- `__get_tls_r15`
|
||||
- `__add_tls_rax`
|
||||
- `__add_tls_rbx`
|
||||
- `__add_tls_rcx`
|
||||
- `__add_tls_rdx`
|
||||
- `__add_tls_rdi`
|
||||
- `__add_tls_rsi`
|
||||
- `__add_tls_rbp`
|
||||
- `__add_tls_r8`
|
||||
- `__add_tls_r9`
|
||||
- `__add_tls_r10`
|
||||
- `__add_tls_r11`
|
||||
- `__add_tls_r12`
|
||||
- `__add_tls_r13`
|
||||
- `__add_tls_r14`
|
||||
- `__add_tls_r15`
|
||||
|
||||
By favoring `%gs` we've now ensured friction-free compatibility for the
|
||||
APE runtime on MacOS, Linux, and FreeBSD which are all able to conform
|
||||
easily to this convention. However additional work needs to be done at
|
||||
runtime when an APE program is started on Windows, OpenBSD, and NetBSD.
|
||||
On these platforms, all executable pages must be faulted and morphed to
|
||||
fixup the TLS instructions.
|
||||
The `cosmocc` script internally uses a tool named `tlscc` which wraps a
|
||||
gcc or clang compilation so it generates intermediate assembly code that
|
||||
it can modify to call the above functions. In the process, it also adds
|
||||
the `-mno-tls-direct-seg-refs` and `-mno-red-zone` flags to the compile
|
||||
command to ensure the compiler only generates TLS assembly instructions
|
||||
that are understood by the tool.
|
||||
|
||||
On OpenBSD and NetBSD, this is as simple as undoing the example
|
||||
operation above. Earlier at compile-time we turned `%fs` into `%gs`.
|
||||
Now, at runtime, `%gs` must be turned back into `%fs`. Since the
|
||||
executable is morphing itself, this is easier said than done.
|
||||
The above functions do not clobber any registers. The function name
|
||||
specifies the register to which the result is stored or added. The only
|
||||
above function that can be called by C code is `__get_tls_rax`.
|
||||
|
||||
OpenBSD for example enforces a `W^X` invariant. Code that's executing
|
||||
can't modify itself at the same time. The way Cosmopolitan solves this
|
||||
is by defining a special part of the binary called `.text.privileged`.
|
||||
This section is aligned to page boundaries. A GNU ld linker script is
|
||||
used to ensure that code which morphs code is placed into this section,
|
||||
through the use of a header-defined cosmo-specific keyword `privileged`.
|
||||
Additionally, the `fixupobj` program is used by the Cosmo build system
|
||||
to ensure that compiled objects don't contain privileged functions that
|
||||
call non-privileged functions. Needless to say, `mprotect()` needs to be
|
||||
a privileged function, so that it can be used to disable the execute bit
|
||||
on all other parts of the executable except for the privileged section,
|
||||
thereby making it writable. Once this has been done, code can change.
|
||||
|
||||
On Windows the displacement bytes of the TLS instruction are changed to
|
||||
use the `%gs:0x1480+i*8` ABI where `i` is a number assigned by the WIN32
|
||||
`TlsAlloc()` API. This avoids the need to call `TlsGetValue()` which is
|
||||
implemented this exact same way under the hood. Even though 0x1480 isn't
|
||||
explicitly documented by MSDN, this ABI is believed to be stable because
|
||||
MSVC generates binaries that use this offset directly. The only caveat
|
||||
is that `TlsAlloc()` must be called as early in the runtime init as
|
||||
possible, to ensure an index less than 64 is returned.
|
||||
When Windows and MacOS aren't in the support vector, then the above
|
||||
assembly rewriting pass can be avoided entirely, and the binary can
|
||||
simply use the TLS opcodes that GCC and Clang generate for Linux.
|
||||
|
||||
### Thread Information Block (TIB)
|
||||
|
||||
|
||||
+10
@@ -61,6 +61,16 @@ netbsd.ident:
|
||||
4: .size netbsd.ident,.-netbsd.ident
|
||||
.type netbsd.ident,@object
|
||||
.balign 4
|
||||
freebsd.ident:
|
||||
.long 2f-1f
|
||||
.long 4f-3f
|
||||
.long 1 // NT_FREEBSD_ABI_TAG
|
||||
1: .asciz "FreeBSD"
|
||||
2: .balign 4
|
||||
3: .long 1300000 // v13.0+ c. 2020
|
||||
4: .size freebsd.ident,.-freebsd.ident
|
||||
.type freebsd.ident,@object
|
||||
.balign 4
|
||||
ape.ident:
|
||||
.long 2f-1f
|
||||
.long 4f-3f
|
||||
|
||||
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -5,6 +5,7 @@
|
||||
-Wa,-msse2avx
|
||||
-Werror=maybe-uninitialized
|
||||
-Wno-literal-suffix
|
||||
-Wno-prio-ctor-dtor
|
||||
-Wno-unused-but-set-variable
|
||||
-Wunsafe-loop-optimizations
|
||||
-fbranch-target-load-optimize
|
||||
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
+55
-24
@@ -96,6 +96,7 @@ endif
|
||||
# - Turns off support for other operating systems
|
||||
#
|
||||
ifeq ($(MODE), optlinux)
|
||||
TLSCC =
|
||||
CONFIG_OFLAGS ?= -g -ggdb
|
||||
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
|
||||
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
|
||||
@@ -103,17 +104,14 @@ CONFIG_COPTS += -mred-zone
|
||||
CONFIG_TARGET_ARCH ?= -march=native
|
||||
endif
|
||||
ifeq ($(MODE), x86_64-optlinux)
|
||||
CONFIG_OFLAGS ?= -g -ggdb
|
||||
TLSCC =
|
||||
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
|
||||
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
|
||||
CONFIG_COPTS += -mred-zone
|
||||
CONFIG_TARGET_ARCH ?= -march=native
|
||||
CONFIG_TARGET_ARCH ?= -mavx
|
||||
endif
|
||||
ifeq ($(MODE), aarch64-optlinux)
|
||||
CONFIG_OFLAGS ?= -g -ggdb
|
||||
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
|
||||
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
|
||||
CONFIG_COPTS += -mred-zone
|
||||
endif
|
||||
|
||||
# Release Mode
|
||||
@@ -153,7 +151,7 @@ OVERRIDE_CFLAGS += -O0
|
||||
OVERRIDE_CXXFLAGS += -O0
|
||||
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
|
||||
CONFIG_COPTS += -fsanitize=undefined
|
||||
CONFIG_COPTS += -fsanitize=undefined -fno-data-sections
|
||||
OVERRIDE_CCFLAGS += -fno-pie
|
||||
QUOTA ?= -C64 -L300
|
||||
endif
|
||||
@@ -164,7 +162,7 @@ OVERRIDE_CFLAGS += -O0
|
||||
OVERRIDE_CXXFLAGS += -O0
|
||||
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
|
||||
CONFIG_COPTS += -fsanitize=undefined
|
||||
CONFIG_COPTS += -fsanitize=undefined -fno-data-sections
|
||||
OVERRIDE_CCFLAGS += -fno-pie
|
||||
QUOTA ?= -C64 -L300
|
||||
endif
|
||||
@@ -175,7 +173,7 @@ OVERRIDE_CFLAGS += -O0 -fdce
|
||||
OVERRIDE_CXXFLAGS += -O0 -fdce
|
||||
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
|
||||
CONFIG_COPTS += -fsanitize=undefined
|
||||
CONFIG_COPTS += -fsanitize=undefined -fno-data-sections
|
||||
QUOTA ?= -C64 -L300
|
||||
endif
|
||||
|
||||
@@ -191,7 +189,16 @@ endif
|
||||
#
|
||||
ifeq ($(MODE), sysv)
|
||||
ENABLE_FTRACE = 1
|
||||
CONFIG_OFLAGS ?= -g -ggdb
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -O2
|
||||
CONFIG_CPPFLAGS += -DSYSDEBUG -DSUPPORT_VECTOR=121
|
||||
endif
|
||||
ifeq ($(MODE), x86_64-sysv)
|
||||
ENABLE_FTRACE = 1
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -O2
|
||||
CONFIG_CPPFLAGS += -DSYSDEBUG -DSUPPORT_VECTOR=121
|
||||
endif
|
||||
ifeq ($(MODE), aarch64-sysv)
|
||||
ENABLE_FTRACE = 1
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -O2
|
||||
CONFIG_CPPFLAGS += -DSYSDEBUG -DSUPPORT_VECTOR=121
|
||||
endif
|
||||
@@ -211,8 +218,7 @@ endif
|
||||
ifeq ($(MODE), tiny)
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY
|
||||
-DNDEBUG
|
||||
CONFIG_CCFLAGS += \
|
||||
-Os \
|
||||
-fno-align-functions \
|
||||
@@ -230,8 +236,7 @@ endif
|
||||
ifeq ($(MODE), x86_64-tiny)
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY
|
||||
-DNDEBUG
|
||||
CONFIG_CCFLAGS += \
|
||||
-Os \
|
||||
-fno-align-functions \
|
||||
@@ -250,8 +255,7 @@ ifeq ($(MODE), aarch64-tiny)
|
||||
# TODO(jart): -mcmodel=tiny
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY
|
||||
-DNDEBUG
|
||||
CONFIG_CCFLAGS += \
|
||||
-Os \
|
||||
-fno-align-functions \
|
||||
@@ -281,10 +285,37 @@ endif
|
||||
# - YOLO
|
||||
#
|
||||
ifeq ($(MODE), tinylinux)
|
||||
TLSCC =
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DSUPPORT_VECTOR=1 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
-Os \
|
||||
-fno-align-functions \
|
||||
-fno-align-jumps \
|
||||
-fno-align-labels \
|
||||
-fno-align-loops
|
||||
endif
|
||||
ifeq ($(MODE), x86_64-tinylinux)
|
||||
TLSCC =
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DSUPPORT_VECTOR=1 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
-Os \
|
||||
-fno-align-functions \
|
||||
-fno-align-jumps \
|
||||
-fno-align-labels \
|
||||
-fno-align-loops
|
||||
endif
|
||||
ifeq ($(MODE), aarch64-tinylinux)
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY \
|
||||
-DSUPPORT_VECTOR=1 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
@@ -310,10 +341,10 @@ endif
|
||||
# - YOLO
|
||||
#
|
||||
ifeq ($(MODE), tinylinuxbsd)
|
||||
TLSCC =
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY \
|
||||
-DSUPPORT_VECTOR=113 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
@@ -341,7 +372,6 @@ ifeq ($(MODE), tinysysv)
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY \
|
||||
-DSUPPORT_VECTOR=121 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
@@ -369,7 +399,6 @@ ifeq ($(MODE), tinynowin)
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY \
|
||||
-DSUPPORT_VECTOR=251 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
@@ -410,7 +439,7 @@ endif
|
||||
# The rest of the monorepo may not work with llvm.
|
||||
#
|
||||
ifeq ($(MODE), llvm)
|
||||
.STRICT = 0
|
||||
.SANDBOXED = 0
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG -O2
|
||||
AS = clang
|
||||
CC = clang
|
||||
@@ -450,9 +479,9 @@ endif
|
||||
# like objconv to convert them to COFF or MachO. Then use ANSI mode to
|
||||
# rollup one header file that'll enable linkage with minimal issues.
|
||||
ifeq ($(MODE), ansi)
|
||||
CONFIG_CFLAGS += -std=c11
|
||||
CONFIG_CFLAGS += -std=c23
|
||||
#CONFIG_CPPFLAGS += -ansi
|
||||
CONFIG_CXXFLAGS += -std=c++11
|
||||
CONFIG_CXXFLAGS += -std=c++23
|
||||
endif
|
||||
|
||||
ifneq ($(ENABLE_FTRACE),)
|
||||
@@ -490,7 +519,7 @@ endif
|
||||
ifeq ($(ARCH), aarch64)
|
||||
# this flag causes gcc to generate functions like this
|
||||
#
|
||||
# nop nop nop nop nop nop
|
||||
# nop nop nop nop nop nop nop nop
|
||||
# func:
|
||||
# nop
|
||||
# ...
|
||||
@@ -507,7 +536,9 @@ ifeq ($(ARCH), aarch64)
|
||||
# b -5
|
||||
# ...
|
||||
#
|
||||
CONFIG_CCFLAGS += -fpatchable-function-entry=7,6
|
||||
# we only need -fpatchable-function-entry=7,6 but we round up to 32
|
||||
# bytes so we don't break function alignment.
|
||||
CONFIG_CCFLAGS += -fpatchable-function-entry=9,8
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
@@ -53,11 +53,7 @@
|
||||
# OVERRIDE_FOO set ~/.cosmo.mk and target-specific (use rarely)
|
||||
#
|
||||
|
||||
ifeq ($(LANDLOCKMAKE_VERSION),)
|
||||
TMPSAFE = $(join $(TMPDIR)/,$(subst /,_,$@)).tmp
|
||||
else
|
||||
TMPSAFE = $(TMPDIR)/
|
||||
endif
|
||||
|
||||
BACKTRACES = \
|
||||
-fno-schedule-insns2 \
|
||||
@@ -94,6 +90,7 @@ DEFAULT_COPTS ?= \
|
||||
-fstrict-overflow \
|
||||
-fno-semantic-interposition
|
||||
|
||||
ifneq ($(TLSCC),)
|
||||
ifeq ($(ARCH), x86_64)
|
||||
# Microsoft says "[a]ny memory below the stack beyond the red zone
|
||||
# [note: Windows defines the x64 red zone size as 0] is considered
|
||||
@@ -103,6 +100,7 @@ DEFAULT_COPTS += \
|
||||
-mno-red-zone \
|
||||
-mno-tls-direct-seg-refs
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH), aarch64)
|
||||
#
|
||||
|
||||
+1
-2
@@ -23,8 +23,7 @@
|
||||
# - tool/build/runitd.c
|
||||
|
||||
.PRECIOUS: o/$(MODE)/%.ok
|
||||
o/$(MODE)/%.ok: private .PLEDGE = stdio rpath wpath cpath proc fattr inet dns
|
||||
o/$(MODE)/%.ok: private .UNVEIL += r:/etc/resolv.conf
|
||||
o/$(MODE)/%.ok: private .PLEDGE = fattr inet dns
|
||||
o/$(MODE)/%.ok: \
|
||||
o/$(MODE)/tool/build/runit \
|
||||
o/$(MODE)/tool/build/runitd \
|
||||
|
||||
+10
-19
@@ -145,16 +145,7 @@ o/%.zip.o: o/%
|
||||
o/$(MODE)/%.zip.o: %
|
||||
@$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
|
||||
|
||||
o/$(MODE)/%.zip.o: %
|
||||
@$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
|
||||
|
||||
# an issue with sandboxing arises when creating directory entries in the
|
||||
# zip file. we need the trailing slash (e.g. o//foo/.zip.o) but Landlock
|
||||
# Make avoids sandboxing directory names that have a trailing slash (so
|
||||
# they can be used to watch for deleted files, without creating overly
|
||||
# broad unveiling). such rules need to be written more explicitly.
|
||||
o/$(MODE)%/.zip.o: %
|
||||
@$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
|
||||
o/$(MODE)/%.zip.o: private .SANDBOXED = 0
|
||||
|
||||
################################################################################
|
||||
# STRICT HEADER CHECKING
|
||||
@@ -163,43 +154,43 @@ o/$(MODE)%/.zip.o: %
|
||||
# and it would be too costly in terms of make latency to have every
|
||||
# header file depend on $(HDRS) and $(INCS).
|
||||
|
||||
o/%.h.ok: private .UNSANDBOXED = 1
|
||||
o/%.h.ok: private .SANDBOXED = 0
|
||||
o/%.h.ok: %.h
|
||||
@$(COMPILE) -ACHECK.h $(COMPILE.c) -xc -g0 -o $@ $<
|
||||
|
||||
o/$(MODE)/%.h.ok: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%.h.ok: private .SANDBOXED = 0
|
||||
o/$(MODE)/%.h.ok: %.h
|
||||
@$(COMPILE) -ACHECK.h $(COMPILE.c) -xc -g0 -o $@ $<
|
||||
|
||||
o/$(MODE)/%.hh.ok: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%.hh.ok: private .SANDBOXED = 0
|
||||
o/$(MODE)/%.hh.ok: %.hh
|
||||
@$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
|
||||
|
||||
o/%.okk: .UNSANDBOXED = 1
|
||||
o/%.okk: .SANDBOXED = 0
|
||||
o/%.okk: %
|
||||
@$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
|
||||
|
||||
o/$(MODE)/%.okk: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%.okk: private .SANDBOXED = 0
|
||||
o/$(MODE)/%.okk: %
|
||||
@$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
|
||||
|
||||
################################################################################
|
||||
# EMACS ASSEMBLY GENERATION
|
||||
|
||||
o/$(MODE)/%-gcc.asm: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%-gcc.asm: private .SANDBOXED = 0
|
||||
o/$(MODE)/%-gcc.asm: %.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -S -g0 $(OUTPUT_OPTION) $<
|
||||
|
||||
o/$(MODE)/%-gcc.asm: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%-gcc.asm: private .SANDBOXED = 0
|
||||
o/$(MODE)/%-gcc.asm: %.cc
|
||||
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.cxx) -S -g0 $(OUTPUT_OPTION) $<
|
||||
|
||||
o/$(MODE)/%-clang.asm: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%-clang.asm: private .SANDBOXED = 0
|
||||
o/$(MODE)/%-clang.asm: %.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -S -g0 $(OUTPUT_OPTION) $<
|
||||
|
||||
# TODO(jart): Make intrinsics support Clang.
|
||||
o/$(MODE)/%-clang.asm: CC = $(CLANG)
|
||||
o/$(MODE)/%-clang.asm: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%-clang.asm: private .SANDBOXED = 0
|
||||
o/$(MODE)/%-clang.asm: %.cc
|
||||
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.cxx) -S -g0 $(OUTPUT_OPTION) $<
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace ctl {
|
||||
|
||||
struct string;
|
||||
class string;
|
||||
|
||||
class istream : public ios
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#define COSMOPOLITAN_CTL_NEW_H_
|
||||
|
||||
#ifndef TINY
|
||||
__static_yoink("__demangle");
|
||||
__static_yoink("cosmo_demangle");
|
||||
#endif
|
||||
|
||||
namespace ctl {
|
||||
|
||||
@@ -3833,7 +3833,7 @@ typedef ma_uint16 wchar_t;
|
||||
|
||||
|
||||
/* Platform/backend detection. */
|
||||
#if defined(_WIN32) || defined(__COSMOPOLITAN__)
|
||||
#if defined(_WIN32)
|
||||
#define MA_WIN32
|
||||
#if defined(MA_FORCE_UWP) || (defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)))
|
||||
#define MA_WIN32_UWP
|
||||
@@ -78883,6 +78883,7 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p
|
||||
}
|
||||
if (isProcessingMetadata) {
|
||||
ma_uint64 metadataBytesRead;
|
||||
(void)metadataBytesRead;
|
||||
metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown);
|
||||
MA_DR_WAV_ASSERT(metadataBytesRead <= header.sizeInBytes);
|
||||
if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) {
|
||||
|
||||
@@ -167,6 +167,7 @@ See below for detailed the API documentation.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <cosmotime.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ DSP_PROG_BINS = $(DSP_PROG_COMS) $(DSP_PROG_COMS:%=%.dbg)
|
||||
DSP_PROG_DIRECTDEPS = \
|
||||
DSP_AUDIO \
|
||||
LIBC_CALLS \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_RUNTIME \
|
||||
@@ -20,6 +21,8 @@ DSP_PROG_DIRECTDEPS = \
|
||||
LIBC_STDIO \
|
||||
LIBC_SYSV \
|
||||
LIBC_TINYMATH \
|
||||
THIRD_PARTY_GETOPT \
|
||||
THIRD_PARTY_MINIAUDIO \
|
||||
THIRD_PARTY_MUSL \
|
||||
|
||||
DSP_PROG_DEPS := \
|
||||
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2025 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 "dsp/audio/cosmoaudio/cosmoaudio.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "third_party/getopt/getopt.internal.h"
|
||||
#include "third_party/miniaudio/miniaudio.h"
|
||||
|
||||
/**
|
||||
* @fileoverview audio file player
|
||||
*
|
||||
* This program provides a command line interface for playing .wav,
|
||||
* .mp3, .flac, and .ogg on the system speaker.
|
||||
*/
|
||||
|
||||
volatile int gotsig;
|
||||
|
||||
void onsig(int sig) {
|
||||
gotsig = sig;
|
||||
}
|
||||
|
||||
void usage(FILE *f) {
|
||||
fprintf(f, "usage: %s [-hrv] [-s 44100] [-c 2] FILE...\n",
|
||||
program_invocation_short_name);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
// parse flags
|
||||
int opt;
|
||||
int flag_repeat = 0;
|
||||
int flag_verbose = 0;
|
||||
int flag_channels = 2;
|
||||
int flag_samprate = 44100;
|
||||
while ((opt = getopt(argc, argv, "hrvc:s:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'c':
|
||||
flag_channels = atoi(optarg);
|
||||
if (flag_channels < 1) {
|
||||
fprintf(stderr, "error: invalid channel count\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
flag_samprate = atoi(optarg);
|
||||
if (flag_samprate < 1) {
|
||||
fprintf(stderr, "error: invalid sampling rate\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
++flag_verbose;
|
||||
break;
|
||||
case 'r':
|
||||
++flag_repeat;
|
||||
break;
|
||||
case 'h':
|
||||
usage(stdout);
|
||||
exit(0);
|
||||
default:
|
||||
usage(stderr);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// open speaker
|
||||
int status;
|
||||
struct CosmoAudio *ca;
|
||||
struct CosmoAudioOpenOptions cao = {0};
|
||||
cao.sizeofThis = sizeof(struct CosmoAudioOpenOptions);
|
||||
cao.deviceType = kCosmoAudioDeviceTypePlayback;
|
||||
cao.sampleRate = flag_samprate;
|
||||
cao.debugLog = flag_verbose > 1;
|
||||
cao.channels = flag_channels;
|
||||
status = cosmoaudio_open(&ca, &cao);
|
||||
if (status != COSMOAUDIO_SUCCESS) {
|
||||
fprintf(stderr, "failed to open speaker: %d\n", status);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// intercept ctrl-c
|
||||
signal(SIGINT, onsig);
|
||||
|
||||
// play files
|
||||
int exit_code = 0;
|
||||
do {
|
||||
for (int arg = optind; arg < argc; ++arg) {
|
||||
const char *fname = argv[arg];
|
||||
|
||||
if (flag_verbose)
|
||||
fprintf(stderr, "playing %s\n", fname);
|
||||
|
||||
// create decoder
|
||||
ma_decoder_config decoderConfig =
|
||||
ma_decoder_config_init(ma_format_f32, flag_channels, flag_samprate);
|
||||
decoderConfig.resampling.algorithm = ma_resample_algorithm_linear;
|
||||
decoderConfig.resampling.linear.lpfOrder = 8;
|
||||
|
||||
// open input file
|
||||
ma_decoder decoder;
|
||||
ma_result rc = ma_decoder_init_file(fname, &decoderConfig, &decoder);
|
||||
if (rc != MA_SUCCESS) {
|
||||
fprintf(stderr,
|
||||
"%s: failed to open audio file: %s (we support "
|
||||
".wav, .mp3, .flac, and .ogg)\n",
|
||||
fname, ma_result_description(rc));
|
||||
exit_code = 1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// decode audio and transfer to speaker
|
||||
while (!gotsig) {
|
||||
float buf[512];
|
||||
ma_uint64 got = 0;
|
||||
ma_uint64 max = sizeof(buf) / sizeof(*buf) / flag_channels;
|
||||
ma_decoder_read_pcm_frames(&decoder, buf, max, &got);
|
||||
if (!got)
|
||||
break;
|
||||
int can = got;
|
||||
cosmoaudio_poll(ca, NULL, &can);
|
||||
cosmoaudio_write(ca, buf, got);
|
||||
}
|
||||
|
||||
// cleanup
|
||||
ma_decoder_uninit(&decoder);
|
||||
}
|
||||
} while (!gotsig && flag_repeat);
|
||||
|
||||
// we're done
|
||||
exit:
|
||||
cosmoaudio_flush(ca);
|
||||
cosmoaudio_close(ca);
|
||||
exit(exit_code);
|
||||
}
|
||||
+2
-1
@@ -56,7 +56,6 @@ EXAMPLES_DIRECTDEPS = \
|
||||
LIBC_NT_IPHLPAPI \
|
||||
LIBC_NT_KERNEL32 \
|
||||
LIBC_NT_MEMORY \
|
||||
LIBC_NT_NTDLL \
|
||||
LIBC_NT_USER32 \
|
||||
LIBC_NT_WS2_32 \
|
||||
LIBC_PROC \
|
||||
@@ -80,6 +79,7 @@ EXAMPLES_DIRECTDEPS = \
|
||||
THIRD_PARTY_DOUBLECONVERSION \
|
||||
THIRD_PARTY_GDTOA \
|
||||
THIRD_PARTY_GETOPT \
|
||||
THIRD_PARTY_HACLSTAR \
|
||||
THIRD_PARTY_HIREDIS \
|
||||
THIRD_PARTY_LIBCXX \
|
||||
THIRD_PARTY_LIBCXXABI \
|
||||
@@ -87,6 +87,7 @@ EXAMPLES_DIRECTDEPS = \
|
||||
THIRD_PARTY_LINENOISE \
|
||||
THIRD_PARTY_LUA \
|
||||
THIRD_PARTY_MBEDTLS \
|
||||
THIRD_PARTY_MINIAUDIO \
|
||||
THIRD_PARTY_MUSL \
|
||||
THIRD_PARTY_NSYNC \
|
||||
THIRD_PARTY_NSYNC_MEM \
|
||||
|
||||
+3
-3
@@ -60,10 +60,10 @@ struct List {
|
||||
int count;
|
||||
};
|
||||
|
||||
atomic_uintptr_t list;
|
||||
_Atomic(struct List*) list;
|
||||
|
||||
void push(struct List* elem) {
|
||||
uintptr_t tip;
|
||||
struct List* tip;
|
||||
assert(!TAG(elem));
|
||||
for (tip = atomic_load_explicit(&list, memory_order_relaxed);;) {
|
||||
elem->next = (struct List*)PTR(tip);
|
||||
@@ -76,7 +76,7 @@ void push(struct List* elem) {
|
||||
}
|
||||
|
||||
struct List* pop(void) {
|
||||
uintptr_t tip;
|
||||
struct List* tip;
|
||||
struct List* elem;
|
||||
tip = atomic_load_explicit(&list, memory_order_relaxed);
|
||||
while ((elem = (struct List*)PTR(tip))) {
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
// clang-format off
|
||||
//
|
||||
// this program implements ancient roman cryptography
|
||||
//
|
||||
// example usage:
|
||||
//
|
||||
// make -j o//examples/caesar # compile
|
||||
// o//examples/caesar 23 <file.txt | # encrypt
|
||||
// o//examples/caesar # decrypt
|
||||
//
|
||||
|
||||
/*
|
||||
* Copyright (c) 1989, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Rick Adams.
|
||||
*
|
||||
* Authors:
|
||||
* Stan King, John Eldridge, based on algorithm suggested by
|
||||
* Bob Morris
|
||||
* 29-Sep-82
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define LINELENGTH 2048
|
||||
#define ROTATE(ch, perm) \
|
||||
isascii(ch) ? ( \
|
||||
isupper(ch) ? ('A' + (ch - 'A' + perm) % 26) : \
|
||||
islower(ch) ? ('a' + (ch - 'a' + perm) % 26) : ch) : ch
|
||||
|
||||
/*
|
||||
* letter frequencies (taken from some unix(tm) documentation)
|
||||
* (unix is a trademark of Bell Laboratories)
|
||||
*/
|
||||
static double stdf[26] = {
|
||||
7.97, 1.35, 3.61, 4.78, 12.37, 2.01, 1.46, 4.49, 6.39, 0.04,
|
||||
0.42, 3.81, 2.69, 5.92, 6.96, 2.91, 0.08, 6.63, 8.77, 9.68,
|
||||
2.62, 0.81, 1.88, 0.23, 2.07, 0.06,
|
||||
};
|
||||
|
||||
static void
|
||||
printit(char *arg)
|
||||
{
|
||||
int ch, rot;
|
||||
|
||||
if ((rot = atoi(arg)) < 0) {
|
||||
fprintf(stderr, "caesar: bad rotation value.\n");
|
||||
exit(1);
|
||||
}
|
||||
while ((ch = getchar()) != EOF)
|
||||
putchar(ROTATE(ch, rot));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int ch, dot, i, nread, winnerdot = 0;
|
||||
unsigned char inbuf[LINELENGTH];
|
||||
int obs[26], try, winner;
|
||||
|
||||
if (argc > 1)
|
||||
printit(argv[1]);
|
||||
|
||||
/* adjust frequency table to weight low probs REAL low */
|
||||
for (i = 0; i < 26; ++i)
|
||||
stdf[i] = log(stdf[i]) + log(26.0 / 100.0);
|
||||
|
||||
/* zero out observation table */
|
||||
bzero(obs, 26 * sizeof(int));
|
||||
|
||||
if ((nread = read(STDIN_FILENO, inbuf, LINELENGTH)) < 0) {
|
||||
perror("caesar");
|
||||
exit(1);
|
||||
}
|
||||
for (i = nread; i--;) {
|
||||
ch = inbuf[i];
|
||||
if (isascii(ch)) {
|
||||
if (islower(ch))
|
||||
++obs[ch - 'a'];
|
||||
else if (isupper(ch))
|
||||
++obs[ch - 'A'];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* now "dot" the freqs with the observed letter freqs
|
||||
* and keep track of best fit
|
||||
*/
|
||||
for (try = winner = 0; try < 26; ++try) { /* += 13) { */
|
||||
dot = 0;
|
||||
for (i = 0; i < 26; i++)
|
||||
dot += obs[i] * stdf[(i + try) % 26];
|
||||
/* initialize winning score */
|
||||
if (try == 0)
|
||||
winnerdot = dot;
|
||||
if (dot > winnerdot) {
|
||||
/* got a new winner! */
|
||||
winner = try;
|
||||
winnerdot = dot;
|
||||
}
|
||||
}
|
||||
|
||||
while (nread) {
|
||||
for (i = 0; i < nread; ++i) {
|
||||
ch = inbuf[i];
|
||||
putchar(ROTATE(ch, winner));
|
||||
}
|
||||
if ((nread = read(STDIN_FILENO, inbuf, LINELENGTH)) < 0) {
|
||||
perror("caesar");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -89,7 +89,7 @@ int main(int argc, char *argv[]) {
|
||||
// not the case when you use sigprocmask() to block signals which is
|
||||
// useful for kicking the can down the road.
|
||||
WRITE("doing cpu task...\n");
|
||||
for (volatile int i = 0; i < INT_MAX / 3; ++i) {
|
||||
for (volatile int i = 0; i < (int)(INT_MAX * .4); ++i) {
|
||||
if (gotsig) {
|
||||
WRITE("\rgot ctrl+c asynchronously\n");
|
||||
exit(0);
|
||||
|
||||
@@ -259,7 +259,7 @@ int main(int argc, char *argv[]) {
|
||||
unassert(!sigaction(SIGTERM, &sa, 0));
|
||||
|
||||
// you can pass the number of threads you want as the first command arg
|
||||
threads = argc > 1 ? atoi(argv[1]) : __get_cpu_count();
|
||||
threads = argc > 1 ? atoi(argv[1]) : cosmo_cpu_count();
|
||||
if (!(1 <= threads && threads <= 100000)) {
|
||||
tinyprint(2, "error: invalid number of threads\n", NULL);
|
||||
exit(1);
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
#/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// POSIX kill command
|
||||
//
|
||||
// - kill -l
|
||||
// - kill -9 123 456
|
||||
// - kill -KILL 123 456
|
||||
// - kill -SIGKILL 123 456
|
||||
// - kill -s SIGKILL 123 456
|
||||
//
|
||||
|
||||
const char kSignals[] = {
|
||||
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS,
|
||||
SIGFPE, SIGKILL, SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM,
|
||||
SIGTERM, SIGCHLD, SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU,
|
||||
SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGPROF, SIGWINCH, SIGSYS,
|
||||
};
|
||||
|
||||
const char kSignalNames[][8] = {
|
||||
"HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "BUS",
|
||||
"FPE", "KILL", "USR1", "SEGV", "USR2", "PIPE", "ALRM",
|
||||
"TERM", "CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU",
|
||||
"URG", "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "SYS",
|
||||
};
|
||||
|
||||
void list_signals(void) {
|
||||
for (int i = 0; i < sizeof(kSignals); i += 4) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
if (i + j < sizeof(kSignals)) {
|
||||
int sig = kSignals[i + j];
|
||||
printf("%2d) SIG%-7s", sig, kSignalNames[i + j]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
const char *prog = argv[0];
|
||||
if (!prog)
|
||||
prog = "kill";
|
||||
|
||||
int arg = 1;
|
||||
const char *sigstr = 0;
|
||||
if (arg < argc) {
|
||||
if (argv[arg][0] == '-') {
|
||||
if (argv[arg][1] == 's' && !argv[arg][2]) {
|
||||
if (++arg < argc)
|
||||
sigstr = argv[arg++];
|
||||
} else if (argv[arg][1] == 'l' && !argv[arg][2]) {
|
||||
list_signals();
|
||||
exit(0);
|
||||
} else {
|
||||
sigstr = argv[arg++] + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (arg == argc) {
|
||||
fprintf(stderr, "%s: missing operand\n", prog);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int sig;
|
||||
if (!sigstr) {
|
||||
sig = SIGTERM;
|
||||
} else {
|
||||
sig = atoi(sigstr);
|
||||
if (sig) {
|
||||
if (!(1 <= sig && sig <= NSIG)) {
|
||||
fprintf(stderr, "%s: bad signal number: %d\n", prog, sig);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if (toupper(sigstr[0]) == 'S' && //
|
||||
toupper(sigstr[1]) == 'I' && //
|
||||
toupper(sigstr[2]) == 'G')
|
||||
sigstr += 3;
|
||||
for (int i = 0; i < sizeof(kSignals); ++i)
|
||||
if (!strcasecmp(sigstr, kSignalNames[i]))
|
||||
sig = kSignals[i];
|
||||
if (!sig) {
|
||||
fprintf(stderr, "%s: bad signal name: %s\n", prog, sigstr);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int rc = 0;
|
||||
for (int i = arg; i < argc; ++i) {
|
||||
int pid = atoi(argv[i]);
|
||||
if (kill(pid, sig)) {
|
||||
fprintf(stderr, "%s: %d: %s\n", prog, pid, strerror(errno));
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
exit(rc);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include <cosmo.h>
|
||||
#include <malloc.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// cosmo bulitin memory leak detector demo
|
||||
//
|
||||
// you can use cosmo's memory leak detector to track backtraces for your
|
||||
// memory allocations and then print error messages using addr2line. you
|
||||
// generally want to have your app override malloc with your own malloc.
|
||||
// you can use libc's builtin dlmalloc implementation like this as well.
|
||||
// using this leak detector makes malloc() go 10x slower and allocations
|
||||
// will each use 200 bytes of additional memory. if you don't care about
|
||||
// backtraces, then you can create a leak detector for free by using the
|
||||
// CheckForMemoryLeaks() function at the end of main().
|
||||
//
|
||||
// $ make -j o//examples/memleak_backtrace
|
||||
// $ o//examples/memleak_backtrace
|
||||
// let's demo the builtin memory leak detector
|
||||
// error: memory leak detected 0x778dd0979fc0
|
||||
// 42175f4 in malloc at /home/jart/cosmo/examples/memleak_backtrace.c:34
|
||||
// 42049b7 in main at /home/jart/cosmo/examples/memleak_backtrace.c:67
|
||||
// 4204db4 in cosmo at /home/jart/cosmo/libc/runtime/cosmo.S:103
|
||||
// 42045d9 in _start at /home/jart/cosmo/libc/crt/crt.S:128
|
||||
// error: memory leak detected 0x778dd0979fe0
|
||||
// 42175f4 in malloc at /home/jart/cosmo/examples/memleak_backtrace.c:34
|
||||
// 42049aa in main at /home/jart/cosmo/examples/memleak_backtrace.c:66
|
||||
// 4204db4 in cosmo at /home/jart/cosmo/libc/runtime/cosmo.S:103
|
||||
// 42045d9 in _start at /home/jart/cosmo/libc/crt/crt.S:128
|
||||
//
|
||||
|
||||
// wrap cosmopolitan's memory allocator to track allocations
|
||||
static pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER;
|
||||
void *malloc(size_t n) {
|
||||
pthread_mutex_lock(&mu);
|
||||
void *r = cosmo_leak_track(__dlmalloc(n));
|
||||
pthread_mutex_unlock(&mu);
|
||||
return r;
|
||||
}
|
||||
void *calloc(size_t n, size_t z) {
|
||||
pthread_mutex_lock(&mu);
|
||||
void *r = cosmo_leak_track(__dlcalloc(n, z));
|
||||
pthread_mutex_unlock(&mu);
|
||||
return r;
|
||||
}
|
||||
void *realloc(void *p, size_t n) {
|
||||
pthread_mutex_lock(&mu);
|
||||
void *t = cosmo_leak_track(__dlrealloc(p, n));
|
||||
if (t && t != p)
|
||||
cosmo_leak_untrack(p);
|
||||
pthread_mutex_unlock(&mu);
|
||||
return t;
|
||||
}
|
||||
void *memalign(size_t a, size_t n) {
|
||||
pthread_mutex_lock(&mu);
|
||||
void *r = cosmo_leak_track(__dlmemalign(a, n));
|
||||
pthread_mutex_unlock(&mu);
|
||||
return r;
|
||||
}
|
||||
void free(void *p) {
|
||||
pthread_mutex_lock(&mu);
|
||||
__dlfree(cosmo_leak_untrack(p));
|
||||
pthread_mutex_unlock(&mu);
|
||||
}
|
||||
|
||||
// the c standard says this function can't be optimized away
|
||||
void *identity(void *arg) {
|
||||
return arg;
|
||||
}
|
||||
void *(*pIdentity)(void *) = identity;
|
||||
|
||||
int main(void) {
|
||||
kprintf("let's demo the builtin memory leak detector\n");
|
||||
|
||||
// leak two allocations, one small, one large
|
||||
pIdentity(malloc(1));
|
||||
pIdentity(malloc(257));
|
||||
|
||||
// wait for any detached posix threads to exit
|
||||
while (!pthread_orphan_np())
|
||||
pthread_decimate_np();
|
||||
|
||||
// this runs atexit functions and thread/process finalizers
|
||||
cosmo_leak_finalize();
|
||||
|
||||
// print information about memory leaks with backtraces
|
||||
if (cosmo_leak_print(0) > 0)
|
||||
_Exit(73);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#include <cosmo.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// zero-overhead memory leak detector demo
|
||||
//
|
||||
// $ make -j o//examples/memleak_zerocost
|
||||
// $ o//examples/memleak_zerocost
|
||||
// error: leaked 128 byte allocation at 0x7a1483eabf50
|
||||
// error: leaked 16 byte allocation at 0x7a1483eacfe0
|
||||
// error: leaked 1'008 byte allocation at 0x42671e0
|
||||
// you forgot to call free 3 times
|
||||
//
|
||||
|
||||
void NoOp(void *p) {
|
||||
}
|
||||
|
||||
void (*pNoOp)(void *) = NoOp;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
// run program, which leaks memory
|
||||
pNoOp(malloc(10));
|
||||
pNoOp(malloc(100));
|
||||
pNoOp(malloc(1000));
|
||||
|
||||
// check for memory leaks. this has zero overhead, because it crawls
|
||||
// the data structures used by cosmopolitan's malloc implementation.
|
||||
// this is great thing to always use so you know when there's a leak
|
||||
// and if you have trouble tracking down where it came from then you
|
||||
// can swap this out with the code in examples/memleak_backtrace.c
|
||||
CheckForMemoryLeaks();
|
||||
}
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include <cosmo.h>
|
||||
#include <getopt.h>
|
||||
#include <netdb.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
// clang-format off
|
||||
|
||||
/**
|
||||
* @fileoverview netcat clone
|
||||
*
|
||||
* Implemented because BusyBox's netcat doesn't detect remote close and
|
||||
* lingers in the CLOSE_WAIT wait possibly due to file descriptor leaks
|
||||
*
|
||||
* Here's an example usage:
|
||||
*
|
||||
* make -j8 o//examples/nc.com
|
||||
* printf 'GET /\r\nHost: justine.lol\r\n\r\n' | o//examples/nc.com justine.lol 80
|
||||
*
|
||||
* Once upon time we called this command basically "telnet"
|
||||
*/
|
||||
|
||||
#define ARRAYLEN(A) \
|
||||
((sizeof(A) / sizeof(*(A))) / ((unsigned)!(sizeof(A) % sizeof(*(A)))))
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
ssize_t rc;
|
||||
size_t i, got;
|
||||
int opt, sock;
|
||||
char buf[1500];
|
||||
bool halfclose = true;
|
||||
const char *host, *port;
|
||||
struct addrinfo *ai = NULL;
|
||||
struct linger linger = {true, 1};
|
||||
struct pollfd fds[2] = {{-1, POLLIN}, {-1, POLLIN}};
|
||||
struct addrinfo hint = {AI_NUMERICSERV, AF_INET, SOCK_STREAM, IPPROTO_TCP};
|
||||
|
||||
while ((opt = getopt(argc, argv, "hH")) != -1) {
|
||||
switch (opt) {
|
||||
case 'H':
|
||||
halfclose = false;
|
||||
break;
|
||||
case 'h':
|
||||
tinyprint(1, "Usage: ", argv[0], " [-hH] IP PORT\n", NULL);
|
||||
exit(0);
|
||||
default:
|
||||
fprintf(stderr, "bad option %d\n", opt);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (argc - optind != 2) {
|
||||
fputs("missing args\n", stderr);
|
||||
exit(1);
|
||||
}
|
||||
host = argv[optind + 0];
|
||||
port = argv[optind + 1];
|
||||
|
||||
if ((rc = getaddrinfo(host, port, &hint, &ai))) {
|
||||
tinyprint(2, host, ": ", gai_strerror(rc), "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) {
|
||||
perror("socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)) == -1) {
|
||||
perror("SO_LINGER");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
|
||||
perror(host);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fds[0].fd = 0;
|
||||
fds[1].fd = sock;
|
||||
for (;;) {
|
||||
fds[0].revents = 0;
|
||||
fds[1].revents = 0;
|
||||
if (poll(fds, ARRAYLEN(fds), -1) == -1) {
|
||||
perror("poll");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (fds[0].revents & (POLLIN | POLLERR | POLLHUP)) {
|
||||
if ((rc = read(0, buf, 1400)) == -1) {
|
||||
perror("read(stdin)");
|
||||
exit(1);
|
||||
}
|
||||
if (!(got = rc)) {
|
||||
if (halfclose) {
|
||||
shutdown(sock, SHUT_WR);
|
||||
}
|
||||
fds[0].fd = -1;
|
||||
}
|
||||
for (i = 0; i < got; i += rc) {
|
||||
if ((rc = write(sock, buf + i, got - i)) == -1) {
|
||||
perror("write(sock)");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fds[1].revents & (POLLIN | POLLERR | POLLHUP)) {
|
||||
if ((rc = read(sock, buf, 1500)) == -1) {
|
||||
perror("read(sock)");
|
||||
exit(1);
|
||||
}
|
||||
if (!(got = rc)) {
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < got; i += rc) {
|
||||
if ((rc = write(1, buf + i, got - i)) == -1) {
|
||||
perror("write(stdout)");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (close(sock) == -1) {
|
||||
perror("close");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
freeaddrinfo(ai);
|
||||
return 0;
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/winsize.h"
|
||||
#include "libc/calls/termios.h"
|
||||
#include "libc/cosmotime.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
|
||||
+1
-1
@@ -11,5 +11,5 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
printf("%d\n", __get_cpu_count());
|
||||
printf("%d\n", cosmo_cpu_count());
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/cosmotime.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/magnumstrs.internal.h"
|
||||
|
||||
+4
-2
@@ -13,7 +13,9 @@
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/cosmotime.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/appendresourcereport.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/append.h"
|
||||
@@ -27,7 +29,7 @@ int main(int argc, char *argv[]) {
|
||||
prog = "rusage";
|
||||
|
||||
if (argc < 2) {
|
||||
tinyprint(2, prog, ": missing command\n", NULL);
|
||||
fprintf(stderr, "%s: missing command\n", prog);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -42,7 +44,7 @@ int main(int argc, char *argv[]) {
|
||||
struct timespec started = timespec_real();
|
||||
|
||||
// launch subprocess
|
||||
int child = fork();
|
||||
int child = vfork();
|
||||
if (child == -1) {
|
||||
perror(prog);
|
||||
exit(1);
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define PHILOSOPHERS 9 // number of hungry philosophers
|
||||
#define THOUGHTS 3 // number of breaks for thinking
|
||||
#define SEATS 3 // number of seats at the tables
|
||||
|
||||
atomic_int occupied;
|
||||
sem_t sem;
|
||||
|
||||
void *philosopher(void *arg) {
|
||||
int tid = (long)arg;
|
||||
|
||||
printf("philosopher#%d arrives\n", tid);
|
||||
|
||||
for (int i = 0; i < THOUGHTS; ++i) {
|
||||
|
||||
sem_wait(&sem);
|
||||
printf("philosopher#%d sat down to eat (%d occupied)\n", tid, ++occupied);
|
||||
usleep(rand() % 1000000);
|
||||
|
||||
printf("philosopher#%d gets up to think (%d occupied)\n", tid, --occupied);
|
||||
sem_post(&sem);
|
||||
usleep(rand() % 1000000);
|
||||
}
|
||||
|
||||
printf("philosopher#%d leaves\n", tid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
printf("inviting %d philosophers to %d person dinner\n", PHILOSOPHERS, SEATS);
|
||||
sem_init(&sem, 0, SEATS);
|
||||
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
for (long i = 0; i < PHILOSOPHERS; ++i) {
|
||||
pthread_t th;
|
||||
pthread_create(&th, &attr, philosopher, (void *)i);
|
||||
}
|
||||
|
||||
pthread_exit(0);
|
||||
}
|
||||
@@ -9,7 +9,9 @@
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* @fileoverview swapcontext() and makecontext() example
|
||||
@@ -20,7 +22,7 @@ static ucontext_t uctx_main;
|
||||
static ucontext_t uctx_func1;
|
||||
static ucontext_t uctx_func2;
|
||||
|
||||
#define say(s) write(1, s, strlen(s))
|
||||
#define say(s) (void)!write(1, s, strlen(s))
|
||||
#define handle_error(msg) \
|
||||
do { \
|
||||
perror(msg); \
|
||||
|
||||
+13
-10
@@ -12,6 +12,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <time.h>
|
||||
|
||||
/**
|
||||
@@ -62,7 +63,6 @@ const char *DescribeFileType(unsigned mode) {
|
||||
|
||||
void PrintFileMetadata(const char *pathname, struct stat *st) {
|
||||
int fd;
|
||||
printf("\n%s:", pathname);
|
||||
if (numeric) {
|
||||
fd = atoi(pathname);
|
||||
if (fstat(fd, st)) {
|
||||
@@ -70,15 +70,16 @@ void PrintFileMetadata(const char *pathname, struct stat *st) {
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if (stat(pathname, st)) {
|
||||
if (lstat(pathname, st)) {
|
||||
perror(pathname);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
printf("\n"
|
||||
"%-32s%,ld\n"
|
||||
"%-32s%,ld\n"
|
||||
"%-32s%#lx\n"
|
||||
"%s:\n"
|
||||
"%-32s%,ld\n" // bytes in file
|
||||
"%-32s%,ld\n" // physical bytes
|
||||
"%-32s%u,%u\n" // device id
|
||||
"%-32s%#lx\n"
|
||||
"%-32s%ld\n"
|
||||
"%-32s%#o (%s)\n"
|
||||
@@ -92,11 +93,13 @@ void PrintFileMetadata(const char *pathname, struct stat *st) {
|
||||
"%-32s%s\n"
|
||||
"%-32s%s\n"
|
||||
"%-32s%s\n",
|
||||
"bytes in file:", st->st_size, "physical bytes:", st->st_blocks * 512,
|
||||
"device id w/ file:", st->st_dev, "inode:", st->st_ino,
|
||||
"hard link count:", st->st_nlink, "mode / permissions:", st->st_mode,
|
||||
DescribeFileType(st->st_mode), "owner id:", st->st_uid,
|
||||
"group id:", st->st_gid, "flags:", st->st_flags, "gen:", st->st_gen,
|
||||
pathname, "bytes in file:", st->st_size,
|
||||
"physical bytes:", st->st_blocks * 512,
|
||||
"device id w/ file:", major(st->st_dev), minor(st->st_dev),
|
||||
"inode:", st->st_ino, "hard link count:", st->st_nlink,
|
||||
"mode / permissions:", st->st_mode, DescribeFileType(st->st_mode),
|
||||
"owner id:", st->st_uid, "group id:", st->st_gid,
|
||||
"flags:", st->st_flags, "gen:", st->st_gen,
|
||||
"device id (if special):", st->st_rdev, "block size:", st->st_blksize,
|
||||
"access time:", gc(xiso8601(st->st_atim)),
|
||||
"modified time:", gc(xiso8601(st->st_mtim)),
|
||||
|
||||
@@ -45,4 +45,7 @@ int main(int argc, char *argv[]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
int main(int argc, char *argv[]) {
|
||||
}
|
||||
#endif /* __x86_64__ */
|
||||
@@ -61,4 +61,7 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
int main(int argc, char *argv[]) {
|
||||
}
|
||||
#endif /* __x86_64__ */
|
||||
@@ -43,4 +43,7 @@ int main(int argc, char *argv[]) {
|
||||
;
|
||||
}
|
||||
|
||||
#else
|
||||
int main(int argc, char *argv[]) {
|
||||
}
|
||||
#endif /* __x86_64__ */
|
||||
@@ -0,0 +1,22 @@
|
||||
#/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void onsig(int sig) {
|
||||
fprintf(stderr, "got %s\n", strsignal(sig));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
fprintf(stderr, "my pid is %d\n", getpid());
|
||||
for (int sig = 1; sig <= NSIG; ++sig)
|
||||
signal(sig, onsig);
|
||||
pause();
|
||||
}
|
||||
+10
-1
@@ -13,12 +13,17 @@ libc/isystem/arm_fp16.h \
|
||||
libc/isystem/arm_neon.h \
|
||||
libc/isystem/arpa/inet.h \
|
||||
libc/isystem/arpa/nameser.h \
|
||||
libc/isystem/arpa/telnet.h \
|
||||
libc/isystem/assert.h \
|
||||
libc/isystem/bsd/readpassphrase.h \
|
||||
libc/isystem/bsd/stdlib.h \
|
||||
libc/isystem/bsd/string.h \
|
||||
libc/isystem/byteswap.h \
|
||||
libc/isystem/clzerointrin.h \
|
||||
libc/isystem/complex.h \
|
||||
libc/isystem/cosmo.h \
|
||||
libc/isystem/cosmoaudio.h \
|
||||
libc/isystem/cosmotime.h \
|
||||
libc/isystem/cpio.h \
|
||||
libc/isystem/cpuid.h \
|
||||
libc/isystem/crypt.h \
|
||||
@@ -49,9 +54,12 @@ libc/isystem/langinfo.h \
|
||||
libc/isystem/libgen.h \
|
||||
libc/isystem/limits.h \
|
||||
libc/isystem/link.h \
|
||||
libc/isystem/linux/f2fs.h \
|
||||
libc/isystem/linux/futex.h \
|
||||
libc/isystem/linux/ioctl.h \
|
||||
libc/isystem/linux/limits.h \
|
||||
libc/isystem/linux/param.h \
|
||||
libc/isystem/linux/rseq.h \
|
||||
libc/isystem/linux/types.h \
|
||||
libc/isystem/locale.h \
|
||||
libc/isystem/malloc.h \
|
||||
@@ -68,6 +76,7 @@ libc/isystem/net/if.h \
|
||||
libc/isystem/net/if_arp.h \
|
||||
libc/isystem/netdb.h \
|
||||
libc/isystem/netinet/in.h \
|
||||
libc/isystem/netinet/in_systm.h \
|
||||
libc/isystem/netinet/ip.h \
|
||||
libc/isystem/netinet/tcp.h \
|
||||
libc/isystem/netinet/udp.h \
|
||||
@@ -94,6 +103,7 @@ libc/isystem/popcntintrin.h \
|
||||
libc/isystem/pthread.h \
|
||||
libc/isystem/pty.h \
|
||||
libc/isystem/pwd.h \
|
||||
libc/isystem/readpassphrase.h \
|
||||
libc/isystem/regex.h \
|
||||
libc/isystem/resolv.h \
|
||||
libc/isystem/sched.h \
|
||||
@@ -135,7 +145,6 @@ libc/isystem/sys/param.h \
|
||||
libc/isystem/sys/poll.h \
|
||||
libc/isystem/sys/prctl.h \
|
||||
libc/isystem/sys/procfs.h \
|
||||
libc/isystem/sys/ptrace.h \
|
||||
libc/isystem/sys/random.h \
|
||||
libc/isystem/sys/reboot.h \
|
||||
libc/isystem/sys/resource.h \
|
||||
|
||||
+2
-1
@@ -23,7 +23,8 @@ void unassert(const char *, const char *, int) libcesque;
|
||||
#define assert(x) ((void)((x) || (__assert_fail(#x, __FILE__, __LINE__), 0)))
|
||||
#endif
|
||||
|
||||
#if __STDC_VERSION__ >= 201112L && !defined(__cplusplus)
|
||||
#if !defined(__cplusplus) && defined(__STDC_VERSION__) && \
|
||||
__STDC_VERSION__ >= 201112L && __STDC_VERSION__ <= 201710L
|
||||
#undef static_assert
|
||||
#define static_assert _Static_assert
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef COSMOPOLITAN_LIBC_BSDSTDLIB_H_
|
||||
#define COSMOPOLITAN_LIBC_BSDSTDLIB_H_
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
uint32_t arc4random(void);
|
||||
void arc4random_buf(void *, size_t);
|
||||
uint32_t arc4random_uniform(uint32_t);
|
||||
|
||||
void *reallocarray(void *, size_t, size_t);
|
||||
long long strtonum(const char *, long long, long long, const char **);
|
||||
int heapsort(void *, size_t, size_t, int (*)(const void *, const void *));
|
||||
int mergesort(void *, size_t, size_t, int (*)(const void *, const void *));
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_LIBC_BSDSTDLIB_H_ */
|
||||
+7
-2
@@ -50,6 +50,7 @@ LIBC_CALLS_A_DIRECTDEPS = \
|
||||
LIBC_NT_PSAPI \
|
||||
LIBC_NT_REALTIME \
|
||||
LIBC_NT_SYNCHRONIZATION \
|
||||
LIBC_NT_WINMM \
|
||||
LIBC_NT_WS2_32 \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
@@ -111,7 +112,7 @@ o//libc/calls/prctl.o: \
|
||||
|
||||
# we always want -Os because:
|
||||
# it's early runtime mandatory and quite huge without it
|
||||
o//libc/calls/getcwd.greg.o \
|
||||
o//libc/calls/getcwd.o \
|
||||
o//libc/calls/statfs2cosmo.o: private \
|
||||
CFLAGS += \
|
||||
-Os
|
||||
@@ -174,7 +175,6 @@ o//libc/calls/gettimeofday.o \
|
||||
o//libc/calls/ioctl.o \
|
||||
o//libc/calls/lutimes.o \
|
||||
o//libc/calls/metaflock.o \
|
||||
o//libc/calls/ntaccesscheck.o \
|
||||
o//libc/calls/ntspawn.o \
|
||||
o//libc/calls/open-nt.o \
|
||||
o//libc/calls/pledge-linux.o \
|
||||
@@ -215,6 +215,11 @@ o//libc/calls/writev.o: private \
|
||||
COPTS += \
|
||||
-mgeneral-regs-only
|
||||
|
||||
# it's too early to use memmove() due to __vex and WinMain
|
||||
o/$(MODE)/libc/calls/mkunixpath.o: private \
|
||||
CFLAGS += \
|
||||
-ffreestanding
|
||||
|
||||
# these assembly files are safe to build on aarch64
|
||||
o/$(MODE)/libc/calls/stackjump.o: libc/calls/stackjump.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
|
||||
+42
-6
@@ -17,10 +17,20 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
alignas(64) static struct {
|
||||
pthread_mutex_t lock;
|
||||
int phase;
|
||||
} __abort = {
|
||||
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
|
||||
};
|
||||
|
||||
/**
|
||||
* Terminates program abnormally.
|
||||
@@ -33,10 +43,36 @@
|
||||
* @noreturn
|
||||
*/
|
||||
wontreturn void abort(void) {
|
||||
sigset_t m = 1ull << (SIGABRT - 1);
|
||||
sigprocmask(SIG_UNBLOCK, &m, 0);
|
||||
raise(SIGABRT);
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
raise(SIGABRT);
|
||||
_Exit(128 + SIGABRT);
|
||||
sigset_t mask;
|
||||
if (!__spawned)
|
||||
pthread_mutex_lock(&__abort.lock);
|
||||
switch (__abort.phase) {
|
||||
case 0:
|
||||
mask = 1ull << (SIGABRT - 1);
|
||||
sigprocmask(SIG_UNBLOCK, &mask, 0);
|
||||
__abort.phase = 1;
|
||||
// fallthrough
|
||||
case 1:
|
||||
__abort.phase = 0;
|
||||
if (!__spawned)
|
||||
pthread_mutex_unlock(&__abort.lock);
|
||||
raise(SIGABRT);
|
||||
if (!__spawned)
|
||||
pthread_mutex_lock(&__abort.lock);
|
||||
__abort.phase = 2;
|
||||
// fallthrough
|
||||
case 2:
|
||||
__abort.phase = 3;
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
// fallthrough
|
||||
case 3:
|
||||
__abort.phase = 4;
|
||||
raise(SIGABRT);
|
||||
// fallthrough
|
||||
case 4:
|
||||
__abort.phase = 5;
|
||||
_Exit(128 + SIGABRT);
|
||||
}
|
||||
for (;;)
|
||||
notpossible;
|
||||
}
|
||||
+4
-2
@@ -20,12 +20,14 @@
|
||||
#include "libc/sysv/consts/at.h"
|
||||
|
||||
/**
|
||||
* Checks if effective user can access path in particular ways.
|
||||
* Checks if file can be accessed in particular way.
|
||||
*
|
||||
* This is equivalent to saying:
|
||||
* This function is equivalent to:
|
||||
*
|
||||
* faccessat(AT_FDCWD, path, mode, 0);
|
||||
*
|
||||
* Please see faccessat() for further documentation.
|
||||
*
|
||||
* @param path is a filename or directory
|
||||
* @param mode can be R_OK, W_OK, X_OK, F_OK
|
||||
* @return 0 if ok, or -1 w/ errno
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright (c) 1996, David Mazieres <[email protected]> │
|
||||
│ Copyright (c) 2008, Damien Miller <[email protected]> │
|
||||
│ Copyright (c) 2013, Markus Friedl <[email protected]> │
|
||||
│ Copyright (c) 2014, Theo de Raadt <[email protected]> │
|
||||
│ Copyright (c) Robert Clausecker <[email protected]> │
|
||||
│ │
|
||||
│ 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/state.internal.h"
|
||||
#include "libc/calls/typedef/u.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
// clang-format off
|
||||
|
||||
#define FAIR_SHARE 1024
|
||||
#define USE_CURRENT_CPU 1
|
||||
#define USE_THREAD_COUNT 1
|
||||
|
||||
__notice(arc4random_notice, "arc4random (ISC)\n\
|
||||
Copyright (c) 1996, David Mazieres <[email protected]>\n\
|
||||
Copyright (c) 2008, Damien Miller <[email protected]>\n\
|
||||
Copyright (c) 2013, Markus Friedl <[email protected]>\n\
|
||||
Copyright (c) 2014, Theo de Raadt <[email protected]>\n\
|
||||
Copyright (c) 2024, Robert Clausecker <[email protected]>\n\
|
||||
Copyright (c) 2025, Justine Tunney <[email protected]>\n\
|
||||
Based upon D. J. Bernstein's public domain ChaCha code\n\
|
||||
Based upon Daniel Lemire's arc4random_uniform research");
|
||||
|
||||
#define KEYSTREAM_ONLY
|
||||
#include "libc/str/chacha.inc"
|
||||
|
||||
#define minimum(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#define KEYSZ 32
|
||||
#define IVSZ 8
|
||||
#define BLOCKSZ 64
|
||||
#define RSBUFSZ (16*BLOCKSZ)
|
||||
|
||||
#define REKEY_BASE (1024*1024) /* NB. should be a power of 2 */
|
||||
|
||||
/* This is zero'd out in forked children. */
|
||||
struct _rs {
|
||||
size_t rs_have; /* valid bytes at end of rs_buf */
|
||||
size_t rs_count; /* bytes till reseed */
|
||||
};
|
||||
|
||||
/* Optional to preserve in forked children. */
|
||||
struct _rsx {
|
||||
chacha_ctx rs_chacha; /* chacha context for random keystream */
|
||||
u_char rs_buf[RSBUFSZ]; /* keystream blocks */
|
||||
};
|
||||
|
||||
struct _rng {
|
||||
alignas(64) pthread_mutex_t mtx;
|
||||
bool once;
|
||||
struct _rs rs;
|
||||
struct _rsx rsx;
|
||||
};
|
||||
|
||||
static struct _rng _rs_state[COSMO_SHARDS];
|
||||
|
||||
void
|
||||
__arc4random_fork_prepare(void)
|
||||
{
|
||||
if (__isthreaded < 2)
|
||||
return;
|
||||
for (int i = 0; i < COSMO_SHARDS; ++i)
|
||||
if (_rs_state[i].once)
|
||||
pthread_mutex_lock(&_rs_state[i].mtx);
|
||||
}
|
||||
|
||||
void
|
||||
__arc4random_fork_parent(void)
|
||||
{
|
||||
if (__isthreaded < 2)
|
||||
return;
|
||||
for (int i = COSMO_SHARDS; i--;)
|
||||
if (_rs_state[i].once)
|
||||
pthread_mutex_unlock(&_rs_state[i].mtx);
|
||||
}
|
||||
|
||||
void
|
||||
__arc4random_fork_child(void)
|
||||
{
|
||||
for (int i = 0; i < COSMO_SHARDS; ++i) {
|
||||
if (!_rs_state[i].once)
|
||||
continue;
|
||||
pthread_mutex_wipe_np(&_rs_state[i].mtx);
|
||||
memset(&_rs_state[i].rs, 0, sizeof(_rs_state[i].rs));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
_rs_init(struct _rng *rng, u_char *buf, size_t n)
|
||||
{
|
||||
if (n < KEYSZ + IVSZ)
|
||||
return;
|
||||
chacha_keysetup(&rng->rsx.rs_chacha, buf, KEYSZ * 8);
|
||||
chacha_ivsetup(&rng->rsx.rs_chacha, buf + KEYSZ);
|
||||
rng->once = true;
|
||||
}
|
||||
|
||||
static inline void
|
||||
_rs_rekey(struct _rng *rng, u_char *dat, size_t datlen)
|
||||
{
|
||||
#ifndef KEYSTREAM_ONLY
|
||||
memset(rng->rsx.rs_buf, 0, sizeof(rng->rsx.rs_buf));
|
||||
#endif
|
||||
/* fill rs_buf with the keystream */
|
||||
chacha_encrypt_bytes(&rng->rsx.rs_chacha, rng->rsx.rs_buf,
|
||||
rng->rsx.rs_buf, sizeof(rng->rsx.rs_buf));
|
||||
/* mix in optional user provided data */
|
||||
if (dat) {
|
||||
size_t i, m;
|
||||
|
||||
m = minimum(datlen, KEYSZ + IVSZ);
|
||||
for (i = 0; i < m; i++)
|
||||
rng->rsx.rs_buf[i] ^= dat[i];
|
||||
}
|
||||
/* immediately reinit for backtracking resistance */
|
||||
_rs_init(rng, rng->rsx.rs_buf, KEYSZ + IVSZ);
|
||||
memset(rng->rsx.rs_buf, 0, KEYSZ + IVSZ);
|
||||
rng->rs.rs_have = sizeof(rng->rsx.rs_buf) - KEYSZ - IVSZ;
|
||||
}
|
||||
|
||||
static void
|
||||
_rs_stir(struct _rng *rng)
|
||||
{
|
||||
u_char rnd[KEYSZ + IVSZ];
|
||||
uint32_t rekey_fuzz = 0;
|
||||
|
||||
if (getentropy(rnd, sizeof rnd) == -1)
|
||||
notpossible;
|
||||
|
||||
if (!rng->once) {
|
||||
_rs_init(rng, rnd, sizeof(rnd));
|
||||
} else {
|
||||
_rs_rekey(rng, rnd, sizeof(rnd));
|
||||
}
|
||||
explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
|
||||
|
||||
/* invalidate rs_buf */
|
||||
rng->rs.rs_have = 0;
|
||||
memset(rng->rsx.rs_buf, 0, sizeof(rng->rsx.rs_buf));
|
||||
|
||||
/* rekey interval should not be predictable */
|
||||
chacha_encrypt_bytes(&rng->rsx.rs_chacha, (uint8_t *)&rekey_fuzz,
|
||||
(uint8_t *)&rekey_fuzz, sizeof(rekey_fuzz));
|
||||
rng->rs.rs_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE);
|
||||
}
|
||||
|
||||
static inline void
|
||||
_rs_stir_if_needed(struct _rng *rng, size_t len)
|
||||
{
|
||||
if (!rng->once || rng->rs.rs_count <= len)
|
||||
_rs_stir(rng);
|
||||
if (rng->rs.rs_count <= len)
|
||||
rng->rs.rs_count = 0;
|
||||
else
|
||||
rng->rs.rs_count -= len;
|
||||
}
|
||||
|
||||
static inline void
|
||||
_rs_random_buf(struct _rng *rng, void *_buf, size_t n)
|
||||
{
|
||||
u_char *buf = (u_char *)_buf;
|
||||
u_char *keystream;
|
||||
size_t m;
|
||||
|
||||
_rs_stir_if_needed(rng, n);
|
||||
while (n > 0) {
|
||||
if (rng->rs.rs_have > 0) {
|
||||
m = minimum(n, rng->rs.rs_have);
|
||||
keystream = rng->rsx.rs_buf + sizeof(rng->rsx.rs_buf)
|
||||
- rng->rs.rs_have;
|
||||
memcpy(buf, keystream, m);
|
||||
memset(keystream, 0, m);
|
||||
buf += m;
|
||||
n -= m;
|
||||
rng->rs.rs_have -= m;
|
||||
}
|
||||
if (rng->rs.rs_have == 0)
|
||||
_rs_rekey(rng, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills buffer with random data.
|
||||
*
|
||||
* This is the best random number generator provided by cosmo libc.
|
||||
*
|
||||
* This RNG is simple. Unlike getrandom() which can fail in numerous
|
||||
* ways, this API always succeeds and returns the full number of bytes.
|
||||
*
|
||||
* This RNG is cryptographic grade. It uses D.J. Bernstein's ChaCha
|
||||
* algorithm. The code for this implementation was originally copied
|
||||
* from the OpenBSD and FreeBSD codebases.
|
||||
*
|
||||
* This RNG is fast. arc4random runs in userspace and occasionally
|
||||
* reseeds itself using getentropy(). This gives you a 10x performance
|
||||
* boost by avoiding costly system calls.
|
||||
*
|
||||
* This RNG is scalable. The Cosmopolitan Libc authors have modified the
|
||||
* original OpenBSD source code so it doesn't use a GIL. We instead load
|
||||
* balance threads across a fixed number of arc4random states which are
|
||||
* indexed based on the current cpu if possible otherwise the thread id.
|
||||
* This way threads needn't wait in sequence and can work concurrently.
|
||||
* The only other fast cryptographic userspace PRNG we've seen that can
|
||||
* do this is Microsoft's ProcessPrng() in Windows 10+.
|
||||
*
|
||||
* This RNG makes the single-threaded case go fast. Similar to FreeBSD
|
||||
* our integration with the C library enables us to know when it's safe
|
||||
* to avoid mutexes entirely. If your app never creates a thread, then
|
||||
* arc4random() will never need to pay the cost of locks.
|
||||
*
|
||||
* This RNG is fork() safe. Naive random number generators oftentimes
|
||||
* have an issue where forked child processes are likely to generate the
|
||||
* same numbers as the parent. Our libc system call wrapper ensures the
|
||||
* RNG state is appropriately wiped. However if you talk directly to the
|
||||
* kernel, using functions like sys_fork(), you're on your own. vfork()
|
||||
* should be safe, since the child process is actually just a thread.
|
||||
*/
|
||||
void
|
||||
arc4random_buf(void *buf, size_t n)
|
||||
{
|
||||
size_t i, w;
|
||||
struct _rng *rng;
|
||||
|
||||
FORBIDDEN_IN_POSIX_SPAWN;
|
||||
|
||||
/* load balance threads across fixed number of shards */
|
||||
rng = &_rs_state[cosmo_shard()];
|
||||
|
||||
/* avoid one thread hogging shard */
|
||||
for (i = 0; i < n; i += w) {
|
||||
w = n - i;
|
||||
if (w > FAIR_SHARE)
|
||||
w = FAIR_SHARE;
|
||||
if (__isthreaded >= 2)
|
||||
pthread_mutex_lock(&rng->mtx);
|
||||
_rs_random_buf(rng, (char *)buf + i, w);
|
||||
if (__isthreaded >= 2)
|
||||
pthread_mutex_unlock(&rng->mtx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns single random 32-bit value.
|
||||
*
|
||||
* The arc4random() function returns pseudo-random numbers in the range
|
||||
* of 0 to (2**32)−1, and has twice the range of rand(3) and random(3).
|
||||
*
|
||||
* This function is a wrapper around arc4random_buf() which can be used
|
||||
* to obtain an arbitrary number of bytes.
|
||||
*
|
||||
* Be careful about using the euclidean remainder operator (`%`) on the
|
||||
* result of this function. It's only safe to do that if you keep the
|
||||
* result unsigned and the denominator is a two power. What you almost
|
||||
* certainly want instead is arc4random_uniform() to do safe modulus.
|
||||
*/
|
||||
uint32_t
|
||||
arc4random(void)
|
||||
{
|
||||
uint32_t val;
|
||||
arc4random_buf(&val, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns random number less than `upper_bound`.
|
||||
*
|
||||
* This is like arc4random() except the arc4random_uniform() function
|
||||
* will calculate a uniformly distributed random number that's less than
|
||||
* `upper_bound` while avoiding "modulo bias".
|
||||
*
|
||||
* This implementation uses the optimized technique discovered by Daniel
|
||||
* Lemire in his paper "Fast Random Integer Generation in an Interval",
|
||||
* Association for Computing Machinery, ACM Trans. Model. Comput.
|
||||
* Simul., no. 1, vol. 29, pp. 1--12, New York, NY, USA, January 2019.
|
||||
* Available: https://r-libre.teluq.ca/1437/1/rangedrng.pdf
|
||||
*/
|
||||
uint32_t
|
||||
arc4random_uniform(uint32_t upper_bound)
|
||||
{
|
||||
uint64_t product;
|
||||
|
||||
/*
|
||||
* The paper uses these variable names:
|
||||
*
|
||||
* L -- log2(UINT32_MAX+1)
|
||||
* s -- upper_bound
|
||||
* x -- arc4random() return value
|
||||
* m -- product
|
||||
* l -- (uint32_t)product
|
||||
* t -- threshold
|
||||
*/
|
||||
|
||||
if (upper_bound <= 1)
|
||||
return 0;
|
||||
|
||||
product = upper_bound * (uint64_t)arc4random();
|
||||
|
||||
if ((uint32_t)product < upper_bound) {
|
||||
uint32_t threshold;
|
||||
|
||||
/* threshold = (2**32 - upper_bound) % upper_bound */
|
||||
threshold = -upper_bound % upper_bound;
|
||||
while ((uint32_t)product < threshold)
|
||||
product = upper_bound * (uint64_t)arc4random();
|
||||
}
|
||||
|
||||
return product >> 32;
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/describebacktrace.h"
|
||||
#include "libc/log/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
|
||||
@@ -32,8 +33,9 @@ void __assert_fail(const char *expr, const char *file, int line) {
|
||||
char ibuf[12];
|
||||
sigset_t m = __sig_block();
|
||||
FormatInt32(ibuf, line);
|
||||
tinyprint(2, file, ":", ibuf, ": \e[31;1massert(", expr,
|
||||
") failed\e[0m (cosmoaddr2line ", FindDebugBinary(), " ",
|
||||
tinyprint(2, file, ":", ibuf, ": ", __nocolor ? "" : "\e[31;1m", "assert(",
|
||||
expr, ") failed", __nocolor ? "" : "\e[0m", " (cosmoaddr2line ",
|
||||
FindDebugBinary(), " ",
|
||||
DescribeBacktrace(__builtin_frame_address(0)), ")\n", NULL);
|
||||
__sig_unblock(m);
|
||||
abort();
|
||||
|
||||
+24
-47
@@ -42,28 +42,15 @@
|
||||
|
||||
#define MAP_FAILED ((void *)-1)
|
||||
|
||||
#define WTERMSIG(x) (127 & (x))
|
||||
#define WCOREDUMP(x) (128 & (x))
|
||||
#define WIFEXITED(x) (!WTERMSIG(x))
|
||||
#define WEXITSTATUS(x) ((x) >> 8)
|
||||
#define WSTOPSIG(x) ((0xff00 & (x)) >> 8)
|
||||
#define WIFSTOPPED(x) __wifstopped(x)
|
||||
#define WIFSIGNALED(x) __wifsignaled(x)
|
||||
#define WIFCONTINUED(x) __wifcontinued(x)
|
||||
#define W_STOPCODE(x) ((x) << 8 | 0177)
|
||||
|
||||
#ifdef _COSMO_SOURCE
|
||||
#define clone __clone
|
||||
#define commandv __commandv
|
||||
#define fileexists __fileexists
|
||||
#define ischardev __ischardev
|
||||
#define isdirectory __isdirectory
|
||||
#define isexecutable __isexecutable
|
||||
#define isregularfile __isregularfile
|
||||
#define issymlink __issymlink
|
||||
#define makedirs __makedirs
|
||||
#define tmpfd __tmpfd
|
||||
#endif
|
||||
#define WEXITSTATUS(s) ((s) >> 8)
|
||||
#define WTERMSIG(s) ((s) & 0x7f)
|
||||
#define WSTOPSIG(s) (((s) & 0xff00) >> 8)
|
||||
#define WIFEXITED(s) (!WTERMSIG(s))
|
||||
#define WIFSTOPPED(s) (((s) & 0xff) == 0x7f)
|
||||
#define WIFSIGNALED(s) (((signed char)(((s) & 0x7f) + 1) >> 1) > 0)
|
||||
#define WCOREDUMP(s) ((s) & 0x80)
|
||||
#define WIFCONTINUED(s) (((s) & 0xffff) == 0xffff)
|
||||
#define W_STOPCODE(s) ((s) << 8 | 0x7f)
|
||||
|
||||
COSMOPOLITAN_C_START_
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
@@ -83,8 +70,6 @@ int chmod(const char *, unsigned) libcesque __read_only(1);
|
||||
int chown(const char *, unsigned, unsigned) libcesque __read_only(1);
|
||||
int chroot(const char *) libcesque __read_only(1);
|
||||
int close(int) libcesque;
|
||||
int close_range(unsigned, unsigned, unsigned) libcesque;
|
||||
int closefrom(int) libcesque;
|
||||
int creat(const char *, unsigned) libcesque __read_only(1);
|
||||
int dup(int) libcesque;
|
||||
int dup2(int, int) libcesque;
|
||||
@@ -101,7 +86,6 @@ int fchmod(int, unsigned) libcesque;
|
||||
int fchmodat(int, const char *, unsigned, int) libcesque __read_only(2);
|
||||
int fchown(int, unsigned, unsigned) libcesque;
|
||||
int fchownat(int, const char *, unsigned, unsigned, int) libcesque __read_only(2);
|
||||
int fcntl(int, int, ...) libcesque;
|
||||
int fdatasync(int) libcesque;
|
||||
int fexecve(int, char *const[], char *const[]) libcesque __read_only(2) __read_only(3);
|
||||
int flock(int, int) libcesque;
|
||||
@@ -171,7 +155,7 @@ int ttyname_r(int, char *, size_t) libcesque __write_only(2, 3);
|
||||
int unlink(const char *) libcesque __read_only(1);
|
||||
int unlinkat(int, const char *, int) libcesque __read_only(2);
|
||||
int usleep(uint64_t) libcesque;
|
||||
int vfork(void) libcesque returnstwice;
|
||||
int vfork(void) dontthrow returnstwice;
|
||||
int wait(int *) libcesque __write_only(1);
|
||||
int waitpid(int, int *, int) libcesque __write_only(2);
|
||||
int64_t clock(void) libcesque;
|
||||
@@ -194,6 +178,11 @@ unsigned ualarm(unsigned, unsigned) libcesque;
|
||||
unsigned umask(unsigned) libcesque;
|
||||
void sync(void) libcesque;
|
||||
|
||||
#if defined(_COSMO_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
|
||||
int madvise(void *, size_t, int);
|
||||
void closefrom(int) libcesque;
|
||||
#endif
|
||||
|
||||
#if defined(_COSMO_SOURCE) || defined(_GNU_SOURCE)
|
||||
int syncfs(int) libcesque;
|
||||
int prctl(int, ...) libcesque;
|
||||
@@ -204,26 +193,21 @@ int getresgid(unsigned *, unsigned *, unsigned *) libcesque __write_only(1) __wr
|
||||
int getresuid(unsigned *, unsigned *, unsigned *) libcesque __write_only(1) __write_only(2) __write_only(3);
|
||||
char *get_current_dir_name(void) libcesque __wur;
|
||||
ssize_t splice(int, int64_t *, int, int64_t *, size_t, unsigned) libcesque __read_write(2) __read_write(4);
|
||||
int memfd_create(const char *, unsigned int) libcesque __read_only(1);
|
||||
int execvpe(const char *, char *const[], char *const[]) libcesque __read_only(1) __read_only(2) __read_only(3);
|
||||
int euidaccess(const char *, int) libcesque __read_only(1);
|
||||
int eaccess(const char *, int) libcesque __read_only(1);
|
||||
int madvise(void *, uint64_t, int) libcesque __read_write(1);
|
||||
int getcpu(unsigned *, unsigned *) libcesque __write_only(1) __write_only(2);
|
||||
int close_range(unsigned, unsigned, unsigned) libcesque;
|
||||
#endif
|
||||
|
||||
#ifdef _COSMO_SOURCE
|
||||
bool32 fdexists(int) libcesque;
|
||||
bool32 fileexists(const char *) libcesque __read_only(1);
|
||||
bool32 ischardev(int) libcesque;
|
||||
bool32 isdirectory(const char *) libcesque __read_only(1);
|
||||
bool32 isexecutable(const char *) libcesque __read_only(1);
|
||||
bool32 isregularfile(const char *) libcesque __read_only(1);
|
||||
bool32 issymlink(const char *) libcesque __read_only(1);
|
||||
#if defined(_COSMO_SOURCE) || defined(_BSD_SOURCE)
|
||||
#define commandv __commandv
|
||||
#define copyfd __copyfd
|
||||
#define makedirs __makedirs
|
||||
#define tmpfd __tmpfd
|
||||
char *commandv(const char *, char *, size_t) libcesque __read_only(1) __write_only(2, 3);
|
||||
int __getcwd(char *, size_t) libcesque __write_only(1, 2);
|
||||
int clone(void *, void *, size_t, int, void *, void *, void *, void *);
|
||||
int fadvise(int, uint64_t, uint64_t, int) libcesque;
|
||||
int __clone(void *, void *, size_t, int, void *, void *, void *, void *);
|
||||
int __getcwd(char *, size_t) libcesque;
|
||||
int makedirs(const char *, unsigned) libcesque __read_only(1);
|
||||
int pivot_root(const char *, const char *) libcesque __read_only(1) __read_only(2);
|
||||
int pledge(const char *, const char *) libcesque __read_only(1) __read_only(2);
|
||||
@@ -237,26 +221,19 @@ int sys_mlockall(int) libcesque;
|
||||
int sys_munlock(const void *, size_t) libcesque __read_only(1);
|
||||
int sys_munlockall(void) libcesque;
|
||||
int sys_personality(uint64_t) libcesque;
|
||||
int sys_ptrace(int, ...) libcesque;
|
||||
int sysctl(int *, unsigned, void *, size_t *, void *, size_t) libcesque __read_write(1) __read_write(4) __read_write(5);
|
||||
int sysctlbyname(const char *, void *, size_t *, void *, size_t) libcesque __read_only(1) __write_only(2) __read_write(3) __read_only(4);
|
||||
int sysctlnametomib(const char *, int *, size_t *) libcesque __read_only(1) __write_only(2) __read_write(3);
|
||||
int tmpfd(void) libcesque;
|
||||
int touch(const char *, unsigned) libcesque __read_only(1);
|
||||
int unveil(const char *, const char *) libcesque __read_only(1);
|
||||
long ptrace(int, ...) libcesque;
|
||||
ssize_t copyfd(int, int, size_t) libcesque;
|
||||
ssize_t readansi(int, char *, size_t) libcesque;
|
||||
ssize_t tinyprint(int, const char *, ...) libcesque nullterminated();
|
||||
void shm_path_np(const char *, char[hasatleast 78]) libcesque;
|
||||
#endif /* _COSMO_SOURCE */
|
||||
#endif
|
||||
|
||||
int system(const char *) libcesque;
|
||||
|
||||
int __wifstopped(int) libcesque pureconst;
|
||||
int __wifcontinued(int) libcesque pureconst;
|
||||
int __wifsignaled(int) libcesque pureconst;
|
||||
|
||||
#if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE)
|
||||
#define lseek64 lseek
|
||||
#define pread64 pread
|
||||
|
||||
+40
-37
@@ -16,48 +16,53 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/sysv/errno.h"
|
||||
|
||||
textwindows int sys_chdir_nt_impl(char16_t path[hasatleast PATH_MAX],
|
||||
uint32_t len) {
|
||||
uint32_t n;
|
||||
char16_t var[4];
|
||||
if (len && path[len - 1] != u'\\') {
|
||||
if (len + 2 > PATH_MAX)
|
||||
return enametoolong();
|
||||
path[len + 0] = u'\\';
|
||||
path[len + 1] = u'\0';
|
||||
}
|
||||
if (SetCurrentDirectory(path)) {
|
||||
/*
|
||||
* Now we need to set a magic environment variable.
|
||||
*/
|
||||
if ((n = GetCurrentDirectory(PATH_MAX, path))) {
|
||||
if (n < PATH_MAX) {
|
||||
if (!((path[0] == '/' && path[1] == '/') ||
|
||||
(path[0] == '\\' && path[1] == '\\'))) {
|
||||
var[0] = '=';
|
||||
var[1] = path[0];
|
||||
var[2] = ':';
|
||||
var[3] = 0;
|
||||
if (!SetEnvironmentVariable(var, path))
|
||||
return __winerr();
|
||||
}
|
||||
return 0;
|
||||
textwindows int sys_chdir_nt16(char16_t path[hasatleast PATH_MAX],
|
||||
uint32_t len) {
|
||||
|
||||
// the win32 SetCurrentDirectory() API wants us to put back a trailing
|
||||
// slash that was removed earlier, when __mkntpath() normalized things
|
||||
if (len + 2 > PATH_MAX)
|
||||
return enametoolong();
|
||||
if (SetCurrentDirectory(path))
|
||||
return 0;
|
||||
|
||||
// remap win32 error codes to unix
|
||||
uint32_t dwErrorCode;
|
||||
switch ((dwErrorCode = GetLastError())) {
|
||||
case kNtErrorFileNotFound: // 2 a.k.a. ENOENT
|
||||
// it does this when the parent components exist but file didn't
|
||||
errno = ENOENT;
|
||||
break;
|
||||
case kNtErrorPathNotFound: // 3 a.k.a. ENOTDIR
|
||||
// with Windows, this means that the file doesn't exist, because
|
||||
// a parent component didn't exist. however unix still considers
|
||||
// that to be an ENOENT error. on UNIX this only becomes ENOTDIR
|
||||
// if a parent component DID exist, but was not a real directory
|
||||
if (__hasregularparent(path)) {
|
||||
errno = ENOTDIR;
|
||||
} else {
|
||||
return enametoolong();
|
||||
errno = ENOENT;
|
||||
}
|
||||
} else {
|
||||
return __winerr();
|
||||
}
|
||||
} else {
|
||||
return __fix_enotdir(__winerr(), path);
|
||||
break;
|
||||
case kNtErrorDirectory: // 267 a.k.a. EISDIR
|
||||
// it'll do this when try to chdir to a regular file that exists
|
||||
// it could also be an ELOOP error although not worth supporting
|
||||
errno = ENOTDIR;
|
||||
break;
|
||||
default:
|
||||
errno = __errno_windows2linux(dwErrorCode);
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
textwindows int sys_chdir_nt(const char *path) {
|
||||
@@ -65,7 +70,5 @@ textwindows int sys_chdir_nt(const char *path) {
|
||||
char16_t path16[PATH_MAX];
|
||||
if ((len = __mkntpath(path, path16)) == -1)
|
||||
return -1;
|
||||
if (!len)
|
||||
return enoent();
|
||||
return sys_chdir_nt_impl(path16, len);
|
||||
return sys_chdir_nt16(path16, len);
|
||||
}
|
||||
+11
-8
@@ -20,6 +20,7 @@
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/log/log.h"
|
||||
@@ -33,14 +34,19 @@
|
||||
* The current directory is shared by all threads in a process. This
|
||||
* does not update the `PWD` environment variable.
|
||||
*
|
||||
* On Windows it's important to consider that when you chdir() into a
|
||||
* directory, any attempt to rmdir() that directory will raise EACCES.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise ELOOP if a loop was detected resolving components of `path`
|
||||
* @raise EACCES if search permission was denied on directory
|
||||
* @raise ENOTDIR if component of `path` isn't a directory
|
||||
* @raise ENOTDIR if parent of `path` exists as non-directory
|
||||
* @raise ENOTDIR if `path` is a regular file that exists
|
||||
* @raise ENOMEM if insufficient memory was available
|
||||
* @raise EFAULT if `path` points to invalid memory
|
||||
* @raise ENOTSUP if `path` is a `/zip/...` file
|
||||
* @raise ENAMETOOLONG if `path` was too long
|
||||
* @raise ENOENT if `path` is empty string
|
||||
* @raise ENOENT if `path` doesn't exist
|
||||
* @raise EIO if an i/o error happened
|
||||
* @asyncsignalsafe
|
||||
@@ -49,16 +55,13 @@
|
||||
int chdir(const char *path) {
|
||||
int rc;
|
||||
struct ZiposUri zipname;
|
||||
if (_weaken(__get_tmpdir)) {
|
||||
if (_weaken(__get_tmpdir))
|
||||
_weaken(__get_tmpdir)();
|
||||
}
|
||||
if (_weaken(GetAddr2linePath)) {
|
||||
if (_weaken(GetAddr2linePath))
|
||||
_weaken(GetAddr2linePath)();
|
||||
}
|
||||
if (_weaken(GetProgramExecutableName)) {
|
||||
if (_weaken(GetProgramExecutableName))
|
||||
_weaken(GetProgramExecutableName)();
|
||||
}
|
||||
if (!path) {
|
||||
if (kisdangerous(path)) {
|
||||
rc = efault();
|
||||
} else if (_weaken(__zipos_parseuri) &&
|
||||
_weaken(__zipos_parseuri)(path, &zipname) != -1) {
|
||||
|
||||
@@ -18,9 +18,10 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#ifdef __x86_64__
|
||||
#if SupportsWindows()
|
||||
|
||||
textwindows int _check_signal(bool restartable) {
|
||||
int status;
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
@@ -48,7 +49,7 @@
|
||||
*/
|
||||
int chroot(const char *path) {
|
||||
int rc;
|
||||
if (!path) {
|
||||
if (kisdangerous(path)) {
|
||||
rc = efault();
|
||||
} else {
|
||||
rc = sys_chroot(path);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/cosmotime.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
|
||||
@@ -16,45 +16,29 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/cosmotime.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/nt/enum/status.h"
|
||||
#include "libc/nt/ntdll.h"
|
||||
#include "libc/stdio/sysparam.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/consts/timer.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
static atomic_int usingRes;
|
||||
static atomic_bool changedRes;
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/nt/winmm.h"
|
||||
#if SupportsWindows()
|
||||
|
||||
static textwindows int sys_clock_nanosleep_nt_impl(int clock,
|
||||
struct timespec abs,
|
||||
sigset_t waitmask) {
|
||||
struct timespec now, wall;
|
||||
uint32_t minRes, maxRes, oldRes;
|
||||
sys_clock_gettime_nt(0, &wall);
|
||||
if (sys_clock_gettime_nt(clock, &now))
|
||||
return -1;
|
||||
bool wantRes = clock == CLOCK_REALTIME || //
|
||||
clock == CLOCK_MONOTONIC || //
|
||||
clock == CLOCK_BOOTTIME;
|
||||
if (wantRes && !atomic_fetch_add(&usingRes, 1))
|
||||
changedRes = NtSuccess(NtQueryTimerResolution(&minRes, &maxRes, &oldRes)) &&
|
||||
NtSuccess(NtSetTimerResolution(maxRes, true, &oldRes));
|
||||
if (timespec_cmp(abs, now) > 0)
|
||||
wall = timespec_add(wall, timespec_sub(abs, now));
|
||||
int rc = _park_norestart(wall, waitmask);
|
||||
if (wantRes && atomic_fetch_sub(&usingRes, 1) == 1 && changedRes)
|
||||
NtSetTimerResolution(0, false, &minRes);
|
||||
return rc;
|
||||
return _park_norestart(wall, waitmask);
|
||||
}
|
||||
|
||||
textwindows int sys_clock_nanosleep_nt(int clock, int flags,
|
||||
@@ -82,4 +66,9 @@ BailOut:
|
||||
return rc;
|
||||
}
|
||||
|
||||
// called by WinMain() if clock_nanosleep() is linked
|
||||
__msabi textwindows dontinstrument void sys_clock_nanosleep_nt_init(void) {
|
||||
__imp_timeBeginPeriod(1); // make sleep(1ms) not take 15ms
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/cosmotime.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
@@ -54,8 +54,5 @@ int sys_clock_nanosleep(int clock, int flags, //
|
||||
rc = ecanceled();
|
||||
}
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("sys_clock_nanosleep(%s, %s, %s, [%s]) → %d% m",
|
||||
DescribeClockName(clock), DescribeSleepFlags(flags),
|
||||
DescribeTimespec(0, req), DescribeTimespec(rc, rem), rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/cosmotime.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/runtime/syslib.internal.h"
|
||||
|
||||
@@ -17,11 +17,16 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/consts/timer.h"
|
||||
|
||||
int sys_clock_nanosleep(int, int, const struct timespec *, struct timespec *);
|
||||
|
||||
/**
|
||||
* Sleeps for particular amount of time.
|
||||
*
|
||||
@@ -99,5 +104,8 @@ errno_t clock_nanosleep(int clock, int flags, //
|
||||
rc = sys_clock_nanosleep(clock, flags, req, rem);
|
||||
err = !rc ? 0 : errno;
|
||||
errno = old;
|
||||
STRACE("clock_nanosleep(%s, %s, %s, [%s]) → %s", DescribeClockName(clock),
|
||||
DescribeSleepFlags(flags), DescribeTimespec(0, req),
|
||||
DescribeTimespec(rc, rem), DescribeErrno(err));
|
||||
return err;
|
||||
}
|
||||
@@ -16,10 +16,9 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/calls/struct/timeval.internal.h"
|
||||
#include "libc/cosmotime.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
|
||||
+74
-42
@@ -16,54 +16,100 @@
|
||||
│ 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/calls/flocks.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/fds.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
#include "libc/sock/syscall_fd.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
// 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
|
||||
// file descriptors the way __zipos_open() does need to retry if
|
||||
// there's indication this race condition happened.
|
||||
#include "libc/sysv/pib.h"
|
||||
|
||||
static int close_impl(int fd) {
|
||||
|
||||
if (fd < 0) {
|
||||
// handle obvious case
|
||||
if (fd < 0)
|
||||
return ebadf();
|
||||
}
|
||||
|
||||
// give kprintf() the opportunity to dup() stderr
|
||||
if (fd == 2 && _weaken(kloghandle)) {
|
||||
if (fd == 2 && !__vforked && _weaken(kloghandle))
|
||||
_weaken(kloghandle)();
|
||||
}
|
||||
|
||||
if (__isfdkind(fd, kFdZip)) {
|
||||
unassert(_weaken(__zipos_close));
|
||||
return _weaken(__zipos_close)(fd);
|
||||
}
|
||||
|
||||
if (!IsWindows() && !IsMetal()) {
|
||||
// unix isn't required to track fds
|
||||
if (fd >= __get_pib()->fds.n) {
|
||||
if (IsWindows() || IsMetal())
|
||||
return ebadf();
|
||||
return sys_close(fd);
|
||||
}
|
||||
|
||||
if (IsWindows()) {
|
||||
return sys_close_nt(fd, fd);
|
||||
// if we're vforked on unix then we can't even modify f->kind
|
||||
if (__vforked && !(IsWindows() || IsMetal()))
|
||||
return sys_close(fd);
|
||||
|
||||
// atomically close file descriptor table entry
|
||||
int rc = 0;
|
||||
struct Fd *f = __get_pib()->fds.p + fd;
|
||||
switch (atomic_exchange(&f->kind, kFdReserved)) {
|
||||
case kFdEmpty:
|
||||
if (IsWindows() || IsMetal()) {
|
||||
rc = ebadf();
|
||||
} else {
|
||||
rc = sys_close(fd);
|
||||
}
|
||||
break;
|
||||
case kFdReserved:
|
||||
return ebadf();
|
||||
case kFdZip:
|
||||
if (_weaken(__zipos_close))
|
||||
rc = _weaken(__zipos_close)(fd);
|
||||
break;
|
||||
#if SupportsMetal()
|
||||
case kFdSerial:
|
||||
break;
|
||||
#endif
|
||||
#if SupportsWindows()
|
||||
case kFdDevRandom:
|
||||
break;
|
||||
case kFdFile:
|
||||
if (!__vforked)
|
||||
if (_weaken(__flocks_close))
|
||||
_weaken(__flocks_close)(f);
|
||||
if (!__vforked || f->was_created_during_vfork) {
|
||||
if (f->cursor)
|
||||
__cursor_unref(f->cursor);
|
||||
if (!CloseHandle(f->handle))
|
||||
rc = __winerr();
|
||||
}
|
||||
break;
|
||||
case kFdDevNull:
|
||||
case kFdConsole:
|
||||
if (!__vforked || f->was_created_during_vfork)
|
||||
if (!CloseHandle(f->handle))
|
||||
rc = __winerr();
|
||||
break;
|
||||
case kFdSocket:
|
||||
if (!__vforked || f->was_created_during_vfork)
|
||||
if (_weaken(sys_closesocket_nt))
|
||||
rc = _weaken(sys_closesocket_nt)(f);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
rc = ebadf();
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
// wipe fd and update lowest file descriptor number
|
||||
// we do this even if there was an underlying os error
|
||||
__releasefd(fd);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,6 +120,8 @@ static int close_impl(int fd) {
|
||||
* - openat()
|
||||
* - socket()
|
||||
* - accept()
|
||||
* - pipe()
|
||||
* - socketpair()
|
||||
* - landlock_create_ruleset()
|
||||
*
|
||||
* This function should never be reattempted if an error is returned;
|
||||
@@ -82,29 +130,13 @@ static int close_impl(int fd) {
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise EINTR if signal was delivered; do *not* retry
|
||||
* @raise EBADF if `fd` is negative or not open; however, an exception
|
||||
* is made by Cosmopolitan Libc for `close(-1)` which returns zero
|
||||
* and does nothing, in order to assist with code that may wish to
|
||||
* close the same resource multiple times without dirtying `errno`
|
||||
* @raise EBADF if `fd` is negative or not open
|
||||
* @raise EIO if a low-level i/o error occurred
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int close(int fd) {
|
||||
int rc;
|
||||
if (__isfdkind(fd, kFdZip)) { // XXX IsWindows()?
|
||||
BLOCK_SIGNALS;
|
||||
__fds_lock();
|
||||
rc = close_impl(fd);
|
||||
if (!__vforked)
|
||||
__releasefd(fd);
|
||||
__fds_unlock();
|
||||
ALLOW_SIGNALS;
|
||||
} else {
|
||||
rc = close_impl(fd);
|
||||
if (!__vforked)
|
||||
__releasefd(fd);
|
||||
}
|
||||
int rc = close_impl(fd);
|
||||
STRACE("close(%d) → %d% m", fd, rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -23,29 +23,24 @@
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Closes inclusive range of file descriptors, e.g.
|
||||
* Closes inclusive range of file descriptors.
|
||||
*
|
||||
* // close all non-stdio file descriptors
|
||||
* if (close_range(3, -1, 0) == -1) {
|
||||
* for (int i = 3; i < 256; ++i) {
|
||||
* close(i);
|
||||
* }
|
||||
* }
|
||||
* This system call is only supported on Linux 5.9+ and FreeBSD 13+.
|
||||
* Consider using closefrom() which will work on all platforms.
|
||||
*
|
||||
* The following flags are available:
|
||||
*
|
||||
* - `CLOSE_RANGE_UNSHARE` (Linux-only)
|
||||
* - `CLOSE_RANGE_CLOEXEC` (Linux-only)
|
||||
* - `CLOSE_RANGE_CLOEXEC` to mark file descriptors as close-on-exec
|
||||
* instead of actually closing them.
|
||||
*
|
||||
* This is only supported on Linux 5.9+ and FreeBSD 13+. Consider using
|
||||
* closefrom() which will work on OpenBSD too.
|
||||
* - `CLOSE_RANGE_UNSHARE` (Linux only) can improve performance in
|
||||
* situations where threads are in play.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @error EINVAL if flags are bad or first is greater than last
|
||||
* @error EMFILE if a weird race condition happens on Linux
|
||||
* @error ENOSYS if not Linux 5.9+ or FreeBSD 13+
|
||||
* @error ENOMEM on Linux maybe
|
||||
* @see closefrom()
|
||||
*/
|
||||
int close_range(unsigned int first, unsigned int last, unsigned int flags) {
|
||||
int rc;
|
||||
|
||||
+36
-29
@@ -18,47 +18,54 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/rlimit.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/sysv/pib.h"
|
||||
|
||||
/**
|
||||
* Closes extra file descriptors, e.g.
|
||||
* Closes extra file descriptors.
|
||||
*
|
||||
* if (closefrom(3))
|
||||
* for (int i = 3; i < 256; ++i)
|
||||
* close(i);
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise EBADF if `first` is negative
|
||||
* @raise ENOSYS if not Linux 5.9+, FreeBSD 8+, OpenBSD, or NetBSD
|
||||
* @raise EBADF on OpenBSD if `first` is greater than highest fd
|
||||
* @raise EINVAL if flags are bad or first is greater than last
|
||||
* @raise EMFILE if a weird race condition happens on Linux
|
||||
* @raise EINTR possibly on OpenBSD
|
||||
* @raise ENOMEM on Linux maybe
|
||||
* This system call is always successful.
|
||||
*/
|
||||
int closefrom(int first) {
|
||||
int rc;
|
||||
void closefrom(int minfd) {
|
||||
BLOCK_CANCELATION;
|
||||
if (first < 0) {
|
||||
// consistent with openbsd
|
||||
// freebsd allows this but it's dangerous
|
||||
// necessary on linux due to type signature
|
||||
rc = ebadf();
|
||||
} else if (IsFreebsd() || IsOpenbsd()) {
|
||||
rc = sys_closefrom(first);
|
||||
|
||||
// fix arg
|
||||
if (minfd < 0)
|
||||
minfd = 0;
|
||||
|
||||
// try syscall
|
||||
int rc;
|
||||
int olderr = errno;
|
||||
if (IsFreebsd() || IsOpenbsd()) {
|
||||
rc = sys_closefrom(minfd);
|
||||
} else if (IsLinux()) {
|
||||
rc = sys_close_range(first, 0xffffffffu, 0);
|
||||
rc = sys_close_range(minfd, 0xffffffffu, 0);
|
||||
} else if (IsNetbsd()) {
|
||||
rc = __sys_fcntl(first, 10 /*F_CLOSEM*/, first);
|
||||
rc = __sys_fcntl(minfd, 10 /*F_CLOSEM*/, minfd);
|
||||
} else {
|
||||
rc = enosys();
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
// try polyfill
|
||||
if (rc == -1) {
|
||||
long maxfd = 1024;
|
||||
if (IsWindows() || IsMetal()) {
|
||||
maxfd = __get_pib()->fds.n;
|
||||
} else {
|
||||
struct rlimit rl;
|
||||
if (!getrlimit(RLIMIT_NOFILE, &rl))
|
||||
if (rl.rlim_cur <= 0x7fffffffu)
|
||||
maxfd = rl.rlim_cur;
|
||||
}
|
||||
for (long fd = minfd; fd < maxfd; ++fd)
|
||||
close(fd);
|
||||
errno = olderr;
|
||||
}
|
||||
|
||||
ALLOW_CANCELATION;
|
||||
STRACE("closefrom(%d) → %d% m", first, rc);
|
||||
return rc;
|
||||
STRACE("closefrom(%d)", minfd);
|
||||
}
|
||||
+3
-1
@@ -23,10 +23,12 @@
|
||||
/**
|
||||
* Creates file.
|
||||
*
|
||||
* This is equivalent to saying:
|
||||
* This function is equivalent to:
|
||||
*
|
||||
* int fd = openat(AT_FDCWD, file, O_CREAT | O_WRONLY | O_TRUNC, mode);
|
||||
*
|
||||
* Please see openat() for further documentation.
|
||||
*
|
||||
* @param file specifies filesystem path to create
|
||||
* @param mode is octal bits, e.g. 0644 usually
|
||||
* @return file descriptor, or -1 w/ errno
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/createfileflags.internal.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/accessmask.h"
|
||||
#include "libc/nt/enum/creationdisposition.h"
|
||||
@@ -33,19 +32,10 @@ textwindows int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm,
|
||||
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_UNLINK | _O_NONBLOCK | _O_RANDOM | _O_SEQUENTIAL | _O_COMPRESSED |
|
||||
_O_INDEXED | _O_CLOEXEC | _O_DIRECT)) {
|
||||
if (flags & ~(O_ACCMODE | O_APPEND | O_CREAT | O_EXCL | O_TRUNC | O_NOCTTY |
|
||||
O_DIRECTORY | O_UNLINK | O_NONBLOCK | O_CLOEXEC | O_DIRECT |
|
||||
_O_TMPFILE | O_DSYNC | O_LARGEFILE))
|
||||
return einval();
|
||||
}
|
||||
|
||||
// "Some of these flags should not be combined. For instance,
|
||||
// combining kNtFileFlagRandomAccess with kNtFileFlagSequentialScan
|
||||
// is self-defeating." -Quoth MSDN § CreateFileW
|
||||
if ((flags & _O_SEQUENTIAL) && (flags & _O_RANDOM)) {
|
||||
return einval();
|
||||
}
|
||||
|
||||
switch (flags & O_ACCMODE) {
|
||||
case O_RDONLY:
|
||||
@@ -53,14 +43,13 @@ textwindows int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm,
|
||||
break;
|
||||
case O_WRONLY:
|
||||
perm = kNtFileGenericWrite;
|
||||
if (flags & _O_APPEND) {
|
||||
if (flags & O_APPEND)
|
||||
// kNtFileAppendData is already present in kNtFileGenericWrite.
|
||||
// WIN32 wont act on append access when write is already there.
|
||||
perm = kNtFileAppendData;
|
||||
}
|
||||
break;
|
||||
case O_RDWR:
|
||||
if (flags & _O_APPEND) {
|
||||
if (flags & O_APPEND) {
|
||||
perm = kNtFileGenericRead | kNtFileAppendData;
|
||||
} else {
|
||||
perm = kNtFileGenericRead | kNtFileGenericWrite;
|
||||
@@ -71,7 +60,7 @@ textwindows int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm,
|
||||
}
|
||||
|
||||
attr = 0;
|
||||
is_creating_file = !!(flags & _O_CREAT);
|
||||
is_creating_file = !!(flags & O_CREAT);
|
||||
|
||||
// POSIX O_EXEC doesn't mean the same thing as kNtGenericExecute. We
|
||||
// request execute access when we can determine it from mode's bits.
|
||||
@@ -79,12 +68,10 @@ textwindows int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm,
|
||||
// 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) {
|
||||
if (mode & 0100)
|
||||
perm |= kNtGenericExecute;
|
||||
}
|
||||
if (~mode & 0200) {
|
||||
if (~mode & 0200)
|
||||
attr |= kNtFileAttributeReadonly; // not sure why anyone would
|
||||
}
|
||||
} else {
|
||||
perm |= kNtGenericExecute;
|
||||
}
|
||||
@@ -96,37 +83,25 @@ textwindows int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm,
|
||||
share = kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete;
|
||||
|
||||
// These POSIX to WIN32 mappings are relatively straightforward.
|
||||
if (flags & _O_EXCL) {
|
||||
if (flags & O_EXCL) {
|
||||
if (is_creating_file) {
|
||||
disp = kNtCreateNew;
|
||||
} else {
|
||||
return einval();
|
||||
}
|
||||
} else if (is_creating_file) {
|
||||
if (flags & _O_TRUNC) {
|
||||
if (flags & O_TRUNC) {
|
||||
disp = kNtCreateAlways;
|
||||
} else {
|
||||
disp = kNtOpenAlways;
|
||||
}
|
||||
} else if (flags & _O_TRUNC) {
|
||||
} else if (flags & O_TRUNC) {
|
||||
disp = kNtTruncateExisting;
|
||||
} else {
|
||||
disp = kNtOpenExisting;
|
||||
}
|
||||
|
||||
if (~flags & _O_INDEXED) {
|
||||
// The Windows content indexing service ravages performance similar to
|
||||
// Windows Defender. Please pass O_INDEXED to openat() to enable this.
|
||||
attr |= kNtFileAttributeNotContentIndexed;
|
||||
}
|
||||
|
||||
if (flags & _O_COMPRESSED) {
|
||||
// Windows' transparent filesystem compression is really cool, as such
|
||||
// we've introduced a nonstandard O_COMPRESSED flag to help you use it
|
||||
attr |= kNtFileAttributeCompressed;
|
||||
}
|
||||
|
||||
if (flags & kNtFileAttributeTemporary) { // subset of _O_UNLINK
|
||||
if (flags & _O_TMPFILE)
|
||||
// "Specifying the kNtFileAttributeTemporary attribute causes file
|
||||
// systems to avoid writing data back to mass storage if sufficient
|
||||
// cache memory is available, because an application deletes a
|
||||
@@ -139,29 +114,29 @@ textwindows int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm,
|
||||
// hold as much as possible in the system cache without writing and
|
||||
// therefore may be of concern for certain applications." -MSDN
|
||||
attr |= kNtFileAttributeTemporary;
|
||||
}
|
||||
|
||||
if (!attr) {
|
||||
if (!attr)
|
||||
// "All other file attributes override kNtFileAttributeNormal [...]
|
||||
// [which] is valid only if used alone." -Quoth MSDN § CreateFileW
|
||||
attr |= kNtFileAttributeNormal;
|
||||
}
|
||||
|
||||
attr |= flags & kNtFileFlagDeleteOnClose; // subset of _O_UNLINK
|
||||
if (flags & O_UNLINK)
|
||||
attr |= kNtFileFlagDeleteOnClose;
|
||||
|
||||
if (flags & _O_DIRECTORY) {
|
||||
if (flags & O_DIRECTORY)
|
||||
// "You must set this flag to obtain a handle to a directory."
|
||||
// -Quoth MSDN § CreateFileW
|
||||
attr |= kNtFileFlagBackupSemantics;
|
||||
}
|
||||
|
||||
// Not certain yet what benefit these flags offer.
|
||||
if (flags & _O_SEQUENTIAL)
|
||||
attr |= kNtFileFlagSequentialScan;
|
||||
if (flags & _O_RANDOM)
|
||||
attr |= kNtFileFlagRandomAccess;
|
||||
if (flags & _O_DIRECT)
|
||||
// flags for slowing down performance
|
||||
if (flags & O_DSYNC)
|
||||
attr |= kNtFileFlagWriteThrough;
|
||||
if (flags & O_DIRECT) {
|
||||
if (flags & O_APPEND)
|
||||
return einval();
|
||||
attr |= kNtFileFlagNoBuffering;
|
||||
perm &= ~kNtFileAppendData; // ∈ kNtFileGenericWrite
|
||||
}
|
||||
|
||||
// TODO(jart): Should we *always* open with write permission if the
|
||||
// kernel will give it to us? We'd then deny write access
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
#ifndef COSMOPOLITAN_LIBC_CALLS_CREATEFILEFLAGS_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_CREATEFILEFLAGS_INTERNAL_H_
|
||||
|
||||
// code size optimization
|
||||
// <sync libc/sysv/consts.sh>
|
||||
#define _O_APPEND 0x00000400 // kNtFileAppendData
|
||||
#define _O_CREAT 0x00000040 // kNtOpenAlways
|
||||
#define _O_EXCL 0x00000080 // kNtCreateNew
|
||||
#define _O_TRUNC 0x00000200 // kNtCreateAlways
|
||||
#define _O_DIRECTORY 0x00010000 // kNtFileFlagBackupSemantics
|
||||
#define _O_UNLINK 0x04000100 // kNtFileAttributeTemporary|DeleteOnClose
|
||||
#define _O_DIRECT 0x00004000 // kNtFileFlagNoBuffering
|
||||
#define _O_NONBLOCK 0x00000800 // kNtFileFlagWriteThrough (not sent to win32)
|
||||
#define _O_RANDOM 0x80000000 // kNtFileFlagRandomAccess
|
||||
#define _O_SEQUENTIAL 0x40000000 // kNtFileFlagSequentialScan
|
||||
#define _O_COMPRESSED 0x20000000 // kNtFileAttributeCompressed
|
||||
#define _O_INDEXED 0x10000000 // !kNtFileAttributeNotContentIndexed
|
||||
#define _O_NOFOLLOW 0x00020000 // kNtFileFlagOpenReparsePoint
|
||||
#define _O_CLOEXEC 0x00080000
|
||||
// </sync libc/sysv/consts.sh>
|
||||
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_CREATEFILEFLAGS_INTERNAL_H_ */
|
||||
@@ -20,15 +20,16 @@
|
||||
#include "libc/fmt/internal.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/sysv/pib.h"
|
||||
|
||||
// This function is called very early by WinMain().
|
||||
textwindows char16_t *__create_pipe_name(char16_t *a) {
|
||||
char16_t *p = a;
|
||||
const char *q = "\\\\?\\pipe\\cosmo\\";
|
||||
const char *q = "\\\\.\\pipe\\cosmo\\";
|
||||
static atomic_uint x;
|
||||
while (*q)
|
||||
*p++ = *q++;
|
||||
p = __itoa16(p, __pid);
|
||||
p = __itoa16(p, __get_pib()->pid);
|
||||
*p++ = '-';
|
||||
p = __itoa16(p, atomic_fetch_add(&x, 1));
|
||||
*p = 0;
|
||||
|
||||
+42
-30
@@ -18,14 +18,13 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/createfileflags.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/rlimit.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/fds.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
@@ -34,63 +33,76 @@
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/sysv/pib.h"
|
||||
|
||||
// Implements dup(), dup2(), dup3(), and F_DUPFD for Windows.
|
||||
static textwindows int sys_dup_nt_impl(int oldfd, int newfd, int flags,
|
||||
textwindows static int sys_dup_nt_impl(int oldfd, int newfd, int flags,
|
||||
int start) {
|
||||
int64_t rc, handle;
|
||||
int64_t handle;
|
||||
unassert(!(flags & ~O_CLOEXEC));
|
||||
|
||||
// validate args
|
||||
if (!__isfdopen(oldfd))
|
||||
return ebadf();
|
||||
|
||||
__fds_lock();
|
||||
|
||||
if (!__isfdopen(oldfd) || newfd < -1) {
|
||||
__fds_unlock();
|
||||
return ebadf();
|
||||
}
|
||||
|
||||
// allocate a new file descriptor
|
||||
// determine new fd
|
||||
bool reserved = false;
|
||||
if (newfd == -1) {
|
||||
if ((newfd = __reservefd_unlocked(start)) == -1) {
|
||||
__fds_unlock();
|
||||
return -1;
|
||||
}
|
||||
reserved = true;
|
||||
} else {
|
||||
if (newfd >= ~__get_pib()->rlimit[RLIMIT_NOFILE].rlim_cur) {
|
||||
__fds_unlock();
|
||||
return emfile();
|
||||
}
|
||||
if (__ensurefds_unlocked(newfd) == -1) {
|
||||
__fds_unlock();
|
||||
return -1;
|
||||
}
|
||||
if (g_fds.p[newfd].kind) {
|
||||
sys_close_nt(newfd, newfd);
|
||||
}
|
||||
}
|
||||
|
||||
if (__isfdkind(oldfd, kFdZip)) {
|
||||
handle = (intptr_t)_weaken(__zipos_keep)(
|
||||
(struct ZiposHandle *)(intptr_t)g_fds.p[oldfd].handle);
|
||||
rc = newfd;
|
||||
(struct ZiposHandle *)(intptr_t)__get_pib()->fds.p[oldfd].handle);
|
||||
} else {
|
||||
if (DuplicateHandle(GetCurrentProcess(), g_fds.p[oldfd].handle,
|
||||
GetCurrentProcess(), &handle, 0, true,
|
||||
kNtDuplicateSameAccess)) {
|
||||
rc = newfd;
|
||||
} else {
|
||||
rc = __winerr();
|
||||
__releasefd(newfd);
|
||||
if (!DuplicateHandle(GetCurrentProcess(), __get_pib()->fds.p[oldfd].handle,
|
||||
GetCurrentProcess(), &handle, 0, true,
|
||||
kNtDuplicateSameAccess)) {
|
||||
if (reserved)
|
||||
__releasefd(newfd);
|
||||
__fds_unlock();
|
||||
return rc;
|
||||
return __winerr();
|
||||
}
|
||||
}
|
||||
|
||||
g_fds.p[newfd] = g_fds.p[oldfd];
|
||||
g_fds.p[newfd].handle = handle;
|
||||
__cursor_ref(g_fds.p[newfd].cursor);
|
||||
if (flags & _O_CLOEXEC) {
|
||||
g_fds.p[newfd].flags |= _O_CLOEXEC;
|
||||
// close overwritten fd upon success
|
||||
if (!reserved) {
|
||||
if (__get_pib()->fds.p[newfd].kind) {
|
||||
if (!__vforked || __get_pib()->fds.p[newfd].was_created_during_vfork) {
|
||||
int e = errno;
|
||||
close(newfd); // needs fds mutex to be recursive :(
|
||||
errno = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// track fd to file descriptors table
|
||||
__get_pib()->fds.p[newfd] = __get_pib()->fds.p[oldfd];
|
||||
__get_pib()->fds.p[newfd].handle = handle;
|
||||
__get_pib()->fds.p[newfd].was_created_during_vfork = __vforked;
|
||||
__cursor_ref(__get_pib()->fds.p[newfd].cursor);
|
||||
if (flags & O_CLOEXEC) {
|
||||
__get_pib()->fds.p[newfd].flags |= O_CLOEXEC;
|
||||
} else {
|
||||
g_fds.p[newfd].flags &= ~_O_CLOEXEC;
|
||||
__get_pib()->fds.p[newfd].flags &= ~O_CLOEXEC;
|
||||
}
|
||||
__fds_unlock();
|
||||
return rc;
|
||||
return newfd;
|
||||
}
|
||||
|
||||
textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) {
|
||||
|
||||
+5
-5
@@ -79,11 +79,11 @@ int dup2(int oldfd, int newfd) {
|
||||
if (!IsWindows()) {
|
||||
if (__isfdkind(oldfd, kFdZip) || __isfdkind(newfd, kFdZip)) {
|
||||
if (__vforked) {
|
||||
return enotsup();
|
||||
}
|
||||
rc = sys_dup2(oldfd, newfd, 0);
|
||||
if (rc != -1) {
|
||||
_weaken(__zipos_postdup)(oldfd, newfd);
|
||||
rc = enotsup();
|
||||
} else {
|
||||
rc = sys_dup2(oldfd, newfd, 0);
|
||||
if (rc != -1)
|
||||
_weaken(__zipos_postdup)(oldfd, newfd);
|
||||
}
|
||||
} else {
|
||||
rc = sys_dup2(oldfd, newfd, 0);
|
||||
|
||||
+20
-1
@@ -61,7 +61,26 @@ int32_t sys_dup3(int32_t oldfd, int32_t newfd, int flags) {
|
||||
cosmo_once(&g_dup3.once, sys_dup3_test);
|
||||
|
||||
if (!g_dup3.demodernize) {
|
||||
return __sys_dup3(oldfd, newfd, flags);
|
||||
int flags2;
|
||||
if (IsLinux()) {
|
||||
flags2 = flags;
|
||||
} else {
|
||||
flags2 = 0;
|
||||
if (flags & O_CLOEXEC) {
|
||||
if (IsXnu()) {
|
||||
flags2 |= 0x01000000;
|
||||
} else if (IsFreebsd()) {
|
||||
flags2 |= 0x00100000;
|
||||
} else if (IsOpenbsd()) {
|
||||
flags2 |= 0x00010000;
|
||||
} else if (IsNetbsd()) {
|
||||
flags2 |= 0x00400000;
|
||||
} else {
|
||||
return einval();
|
||||
}
|
||||
}
|
||||
}
|
||||
return __sys_dup3(oldfd, newfd, flags2);
|
||||
} else {
|
||||
return __fixupnewfd(sys_dup2(oldfd, newfd, 0), flags);
|
||||
}
|
||||
|
||||
+5
-5
@@ -74,11 +74,11 @@ int dup3(int oldfd, int newfd, int flags) {
|
||||
} else if (!IsWindows()) {
|
||||
if (__isfdkind(oldfd, kFdZip) || __isfdkind(newfd, kFdZip)) {
|
||||
if (__vforked) {
|
||||
return enotsup();
|
||||
}
|
||||
rc = sys_dup3(oldfd, newfd, flags);
|
||||
if (rc != -1) {
|
||||
_weaken(__zipos_postdup)(oldfd, newfd);
|
||||
rc = enotsup();
|
||||
} else {
|
||||
rc = sys_dup3(oldfd, newfd, flags);
|
||||
if (rc != -1)
|
||||
_weaken(__zipos_postdup)(oldfd, newfd);
|
||||
}
|
||||
} else {
|
||||
rc = sys_dup3(oldfd, newfd, flags);
|
||||
|
||||
+137
-5
@@ -16,15 +16,147 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/limits.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/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/byhandlefileinformation.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/ok.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/sysv/pib.h"
|
||||
|
||||
textwindows int sys_faccessat_nt(int dirfd, const char *path, int mode,
|
||||
uint32_t flags) {
|
||||
char16_t path16[PATH_MAX];
|
||||
if (__mkntpathat(dirfd, path, 0, path16) == -1)
|
||||
static int Atoi(const char *str) {
|
||||
int c;
|
||||
unsigned x = 0;
|
||||
if (!*str)
|
||||
return -1;
|
||||
return __fix_enotdir(ntaccesscheck(path16, mode), path16);
|
||||
while ((c = *str++)) {
|
||||
if ('0' <= c && c <= '9') {
|
||||
x *= 10;
|
||||
x += c - '0';
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
static int sys_faccessat_nt_dev(int amode, bool readable, bool writable) {
|
||||
if (amode == F_OK)
|
||||
return 0;
|
||||
if (amode == X_OK)
|
||||
return eacces();
|
||||
if (amode == R_OK)
|
||||
if (!readable)
|
||||
return eacces();
|
||||
if (amode == W_OK)
|
||||
if (!writable)
|
||||
return eacces();
|
||||
return 0;
|
||||
}
|
||||
|
||||
textwindows static int sys_faccessat_nt_impl(const char16_t *path16, int amode,
|
||||
int flags) {
|
||||
|
||||
// create access mask
|
||||
uint32_t dwDesiredAccess;
|
||||
if (amode == F_OK) {
|
||||
dwDesiredAccess = kNtFileReadAttributes;
|
||||
} else {
|
||||
// W or X implies R
|
||||
dwDesiredAccess = kNtGenericRead;
|
||||
if (amode & W_OK)
|
||||
dwDesiredAccess |= kNtGenericWrite;
|
||||
}
|
||||
|
||||
// open file
|
||||
intptr_t hFile;
|
||||
if ((hFile = CreateFile(
|
||||
path16, dwDesiredAccess,
|
||||
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, 0,
|
||||
kNtOpenExisting,
|
||||
kNtFileAttributeNormal | kNtFileFlagBackupSemantics |
|
||||
((flags & AT_SYMLINK_NOFOLLOW) ? kNtFileFlagOpenReparsePoint
|
||||
: 0),
|
||||
0)) == -1)
|
||||
return __winerr();
|
||||
|
||||
// check for write and execute access
|
||||
int rc = 0;
|
||||
if (amode & (W_OK | X_OK)) {
|
||||
struct NtByHandleFileInformation wst;
|
||||
if (!GetFileInformationByHandle(hFile, &wst))
|
||||
rc = __winerr();
|
||||
// handle read-only files
|
||||
if (!rc)
|
||||
if (amode & W_OK)
|
||||
if (wst.dwFileAttributes & kNtFileAttributeReadonly)
|
||||
rc = eacces();
|
||||
// executables, directories, and symlinks are always executable
|
||||
if (!rc)
|
||||
if (amode & X_OK)
|
||||
if (!(wst.dwFileAttributes & kNtFileAttributeReparsePoint) &&
|
||||
!(wst.dwFileAttributes & kNtFileAttributeDirectory) &&
|
||||
!IsWindowsExecutable(hFile, path16))
|
||||
rc = eacces();
|
||||
}
|
||||
|
||||
// close file
|
||||
CloseHandle(hFile);
|
||||
|
||||
// return result
|
||||
return rc;
|
||||
}
|
||||
|
||||
textwindows int sys_faccessat_nt(int dirfd, const char *path, int amode,
|
||||
int flags) {
|
||||
|
||||
// validate arguments
|
||||
if (amode & ~(R_OK | W_OK | X_OK))
|
||||
return einval();
|
||||
if (flags & ~(AT_EACCESS | AT_SYMLINK_NOFOLLOW))
|
||||
return einval();
|
||||
|
||||
// handle special files
|
||||
if (startswith(path, "/dev/")) {
|
||||
int fd;
|
||||
if (!strcmp(path + 5, "tty")) {
|
||||
return sys_faccessat_nt_dev(amode, true, true);
|
||||
} else if (!strcmp(path + 5, "null")) {
|
||||
return sys_faccessat_nt_dev(amode, true, true);
|
||||
} else if (!strcmp(path + 5, "random") || !strcmp(path + 5, "urandom")) {
|
||||
return sys_faccessat_nt_dev(amode, true, false);
|
||||
} else if (!strcmp(path + 5, "stdin")) {
|
||||
return sys_faccessat_nt_dev(amode, true, false);
|
||||
} else if (!strcmp(path + 5, "stdout")) {
|
||||
return sys_faccessat_nt_dev(amode, false, true);
|
||||
} else if (!strcmp(path + 5, "stderr")) {
|
||||
return sys_faccessat_nt_dev(amode, false, true);
|
||||
} else if (startswith(path + 5, "fd/") && (fd = Atoi(path + 8)) != -1) {
|
||||
return sys_faccessat_nt_dev(
|
||||
amode, (__get_pib()->fds.p[fd].flags & O_ACCMODE) != O_WRONLY,
|
||||
(__get_pib()->fds.p[fd].flags & O_ACCMODE) != O_RDONLY);
|
||||
}
|
||||
}
|
||||
|
||||
char16_t path16[PATH_MAX];
|
||||
if (__mkntpathat(dirfd, path, path16) == -1)
|
||||
return -1;
|
||||
|
||||
int rc;
|
||||
BLOCK_SIGNALS;
|
||||
rc = __fix_enotdir(sys_faccessat_nt_impl(path16, amode, flags), path16);
|
||||
ALLOW_SIGNALS;
|
||||
return rc;
|
||||
}
|
||||
+37
-5
@@ -22,27 +22,57 @@
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/ok.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
#define DescribeAccessMode(amode) _DescribeAccessMode(alloca(13), amode)
|
||||
static char *_DescribeAccessMode(char buf[13], int amode) {
|
||||
switch (amode) {
|
||||
case F_OK:
|
||||
return "F_OK";
|
||||
case R_OK:
|
||||
return "R_OK";
|
||||
case W_OK:
|
||||
return "W_OK";
|
||||
case X_OK:
|
||||
return "X_OK";
|
||||
case R_OK | W_OK:
|
||||
return "R_OK|W_OK";
|
||||
case R_OK | X_OK:
|
||||
return "R_OK|X_OK";
|
||||
case W_OK | X_OK:
|
||||
return "W_OK|X_OK";
|
||||
case R_OK | W_OK | X_OK:
|
||||
return "R_OK|W_OK|X_OK";
|
||||
default:
|
||||
FormatOctal32(buf, amode, true);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if effective user can access path in particular ways.
|
||||
*
|
||||
* @param dirfd is normally AT_FDCWD but if it's an open directory and
|
||||
* file is a relative path, then file is opened relative to dirfd
|
||||
* @param path is a filename or directory
|
||||
* @param amode can be `R_OK`, `W_OK`, `X_OK`, or `F_OK`
|
||||
* @param amode can be `F_OK` or a mixture of `R_OK`, `W_OK`, and `X_OK`
|
||||
* @param flags can have `AT_EACCESS` and/or `AT_SYMLINK_NOFOLLOW`
|
||||
* @return 0 if ok, or -1 and sets errno
|
||||
* @raise EINVAL if `amode` or `flags` had invalid values
|
||||
* @raise EINVAL if `AT_SYMLINK_NOFOLLOW` is used on FreeBSD
|
||||
* @raise EPERM if pledge() is in play without rpath promise
|
||||
* @raise EACCES if access for requested `amode` would be denied
|
||||
* @raise ENOTDIR if a directory component in `path` exists as non-directory
|
||||
* @raise EILSEQ on Windows if `path` has bad utf-8 of control characters
|
||||
* @raise ENOENT if component of `path` doesn't exist or `path` is empty
|
||||
* @raise ENOTSUP if `path` is a zip file and `dirfd` isn't `AT_FDCWD`
|
||||
* @note on Linux `flags` is only supported on Linux 5.8+
|
||||
@@ -51,8 +81,10 @@
|
||||
int faccessat(int dirfd, const char *path, int amode, int flags) {
|
||||
int e, rc;
|
||||
struct ZiposUri zipname;
|
||||
if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EACCESS)) ||
|
||||
!(amode == F_OK || !(amode & ~(R_OK | W_OK | X_OK)))) {
|
||||
if (kisdangerous(path)) {
|
||||
rc = efault();
|
||||
} else if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EACCESS)) ||
|
||||
!(amode == F_OK || !(amode & ~(R_OK | W_OK | X_OK)))) {
|
||||
rc = einval();
|
||||
} else if (__isfdkind(dirfd, kFdZip)) {
|
||||
rc = enotsup();
|
||||
@@ -73,7 +105,7 @@ int faccessat(int dirfd, const char *path, int amode, int flags) {
|
||||
} else {
|
||||
rc = sys_faccessat_nt(dirfd, path, amode, flags);
|
||||
}
|
||||
STRACE("faccessat(%s, %#s, %#o, %#x) → %d% m", DescribeDirfd(dirfd), path,
|
||||
amode, flags, rc);
|
||||
STRACE("faccessat(%s, %#s, %s, %#x) → %d% m", DescribeDirfd(dirfd), path,
|
||||
DescribeAccessMode(amode), flags, rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -22,14 +22,16 @@
|
||||
#include "libc/dce.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/sysv/pib.h"
|
||||
|
||||
int sys_chdir_nt_impl(char16_t[hasatleast PATH_MAX], uint32_t);
|
||||
int sys_chdir_nt16(char16_t[hasatleast PATH_MAX], uint32_t);
|
||||
|
||||
textwindows int sys_fchdir_nt(int dirfd) {
|
||||
char16_t dir[PATH_MAX];
|
||||
if (!__isfdkind(dirfd, kFdFile))
|
||||
return ebadf();
|
||||
return sys_chdir_nt_impl(
|
||||
dir, GetFinalPathNameByHandle(g_fds.p[dirfd].handle, dir, ARRAYLEN(dir),
|
||||
return sys_chdir_nt16(
|
||||
dir, GetFinalPathNameByHandle(__get_pib()->fds.p[dirfd].handle, dir,
|
||||
ARRAYLEN(dir),
|
||||
kNtFileNameNormalized | kNtVolumeNameDos));
|
||||
}
|
||||
+35
-17
@@ -17,43 +17,61 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/intrin/fds.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/intrin/fds.h"
|
||||
#include "libc/nt/enum/accessmask.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/enum/fileinfobyhandleclass.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/struct/filebasicinfo.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/sysv/pib.h"
|
||||
|
||||
textwindows int sys_fchmod_nt(int fd, uint32_t mode) {
|
||||
|
||||
// validate file descriptor
|
||||
if (fd + 0u >= g_fds.n)
|
||||
return ebadf();
|
||||
if (g_fds.p[fd].kind == kFdEmpty)
|
||||
return ebadf();
|
||||
textwindows bool32 sys_fchmod_nt_handle(intptr_t handle, uint32_t mode) {
|
||||
|
||||
// get current information
|
||||
struct NtFileBasicInfo fbi;
|
||||
if (!GetFileInformationByHandleEx(g_fds.p[fd].handle, kNtFileBasicInfo, &fbi,
|
||||
sizeof(fbi))) {
|
||||
return __winerr();
|
||||
}
|
||||
if (!GetFileInformationByHandleEx(handle, kNtFileBasicInfo, &fbi,
|
||||
sizeof(fbi)))
|
||||
return false;
|
||||
|
||||
// change attributes
|
||||
if (mode & 0222) {
|
||||
if (mode & 0200) {
|
||||
fbi.FileAttributes &= ~kNtFileAttributeReadonly;
|
||||
} else {
|
||||
fbi.FileAttributes |= kNtFileAttributeReadonly;
|
||||
}
|
||||
|
||||
// set new attributes
|
||||
if (!SetFileInformationByHandle(g_fds.p[fd].handle, kNtFileBasicInfo, &fbi,
|
||||
sizeof(fbi))) {
|
||||
return __winerr();
|
||||
}
|
||||
if (!SetFileInformationByHandle(handle, kNtFileBasicInfo, &fbi, sizeof(fbi)))
|
||||
return false;
|
||||
|
||||
// on unix saying `chmod -w dir` prevents writing files in that dir.
|
||||
// however on windows, setting kNtFileAttributeReadonly on a directory
|
||||
// doesn't prevent us from creating files within that directory. so we
|
||||
// need to do something special to make the expected behavior happen.
|
||||
unsigned dwDenyMask = 0;
|
||||
if (fbi.FileAttributes & kNtFileAttributeDirectory)
|
||||
if (fbi.FileAttributes & kNtFileAttributeReadonly)
|
||||
dwDenyMask |=
|
||||
kNtFileAddFile | kNtFileAddSubdirectory | kNtFileDeleteChild;
|
||||
if (~mode & 0400)
|
||||
dwDenyMask |= kNtFileReadData;
|
||||
if (!RestrictFileWin32(handle, 0))
|
||||
return false;
|
||||
if (dwDenyMask)
|
||||
if (!RestrictFileWin32(handle, dwDenyMask))
|
||||
return false;
|
||||
|
||||
// all good
|
||||
return true;
|
||||
}
|
||||
|
||||
textwindows int sys_fchmod_nt(int fd, uint32_t mode) {
|
||||
if (!__isfdkind(fd, kFdFile))
|
||||
return ebadf();
|
||||
if (!sys_fchmod_nt_handle(__get_pib()->fds.p[fd].handle, mode))
|
||||
return __winerr();
|
||||
return 0;
|
||||
}
|
||||
@@ -59,7 +59,7 @@ int sys_fchmodat_linux(int fd, const char *path, unsigned mode, int flag) {
|
||||
return -1;
|
||||
if (S_ISLNK(st.st_mode))
|
||||
return eopnotsupp();
|
||||
if ((fd2 = sys_openat(fd, path, (O_RDONLY | O_PATH | O_NOFOLLOW |
|
||||
if ((fd2 = sys_openat(fd, path, (O_RDONLY | _O_PATH | O_NOFOLLOW |
|
||||
O_NOCTTY | O_CLOEXEC), 0)) < 0) {
|
||||
if (fd2 == -ELOOP)
|
||||
return eopnotsupp();
|
||||
|
||||
+34
-12
@@ -18,25 +18,47 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/limits.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/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_fchmodat_nt(int dirfd, const char *path, uint32_t mode,
|
||||
int flags) {
|
||||
uint32_t attr;
|
||||
|
||||
if (flags & ~AT_SYMLINK_NOFOLLOW)
|
||||
return einval();
|
||||
|
||||
// translate path
|
||||
uint16_t path16[PATH_MAX];
|
||||
if (__mkntpathat(dirfd, path, 0, path16) == -1)
|
||||
if (__mkntpathat(dirfd, path, path16) == -1)
|
||||
return -1;
|
||||
if ((attr = GetFileAttributes(path16)) != -1u) {
|
||||
if (mode & 0222) {
|
||||
attr &= ~kNtFileAttributeReadonly;
|
||||
} else {
|
||||
attr |= kNtFileAttributeReadonly;
|
||||
}
|
||||
if (SetFileAttributes(path16, attr)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// open file
|
||||
intptr_t hFile;
|
||||
if ((hFile = CreateFile(
|
||||
path16,
|
||||
kNtFileReadAttributes | kNtFileWriteAttributes | kNtReadControl |
|
||||
kNtWriteDac,
|
||||
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, 0,
|
||||
kNtOpenExisting,
|
||||
kNtFileAttributeNormal | kNtFileFlagBackupSemantics |
|
||||
((flags & AT_SYMLINK_NOFOLLOW) ? kNtFileFlagOpenReparsePoint
|
||||
: 0),
|
||||
0)) == -1)
|
||||
return __winerr();
|
||||
|
||||
if (!sys_fchmod_nt_handle(hFile, mode)) {
|
||||
CloseHandle(hFile);
|
||||
return __winerr();
|
||||
}
|
||||
return __winerr();
|
||||
|
||||
CloseHandle(hFile);
|
||||
return 0;
|
||||
}
|
||||
+23
-4
@@ -23,6 +23,7 @@
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
@@ -43,15 +44,33 @@ int sys_fchmodat2(int, const char *, unsigned, int);
|
||||
* @param flags can have `AT_SYMLINK_NOFOLLOW`
|
||||
* @raise EROFS if `dirfd` or `path` use zip file system
|
||||
* @raise EOPNOTSUP on Linux if `path` is a symbolic link, `AT_SYMLINK_NOFOLLOW`
|
||||
* is set in `flags`, and filesystem does not support setting the mode of
|
||||
* symbolic links.
|
||||
* @errors ENOENT, ENOTDIR, ENOSYS
|
||||
* is set in `flags`, and filesystem does not support setting the mode of
|
||||
* symbolic links.
|
||||
* @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 EPERM if geteuid() doesn't match owner of file and not root
|
||||
* @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 ENOMEM if insufficient memory was available to the implementation
|
||||
* @raise EILSEQ on Windows if `path` has bad utf-8 of control characters
|
||||
* @raise EROFS if `path` points to the `/zip/...` synthetic filesystem
|
||||
* @raise ELOOP may ahappen if `SYMLOOP_MAX` symlinks were dereferenced
|
||||
* @raise ENAMETOOLONG if resolved `path` exceeded `PATH_MAX`
|
||||
* @raise EROFS if `path` resides on a real-only filesystem
|
||||
* @raise EFAULT if `path` points to invalid memory
|
||||
* @raise EINVAL if `flags` has unsupported bits
|
||||
* @asyncsignalsafe
|
||||
* @see fchmod()
|
||||
*/
|
||||
int fchmodat(int dirfd, const char *path, uint32_t mode, int flags) {
|
||||
int rc;
|
||||
if (_weaken(__zipos_notat) && (rc = __zipos_notat(dirfd, path)) == -1) {
|
||||
if (kisdangerous(path)) {
|
||||
rc = efault();
|
||||
} else if (_weaken(__zipos_notat) &&
|
||||
(rc = __zipos_notat(dirfd, path)) == -1) {
|
||||
rc = erofs();
|
||||
} else if (!IsWindows()) {
|
||||
if (IsLinux() && flags) {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
@@ -42,7 +43,10 @@
|
||||
int fchownat(int dirfd, const char *path, uint32_t uid, uint32_t gid,
|
||||
int flags) {
|
||||
int rc;
|
||||
if (_weaken(__zipos_notat) && (rc = __zipos_notat(dirfd, path)) == -1) {
|
||||
if (kisdangerous(path)) {
|
||||
rc = efault();
|
||||
} else if (_weaken(__zipos_notat) &&
|
||||
(rc = __zipos_notat(dirfd, path)) == -1) {
|
||||
rc = erofs();
|
||||
} else {
|
||||
rc = sys_fchownat(dirfd, path, uid, gid, flags);
|
||||
|
||||
@@ -1,375 +0,0 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/createfileflags.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/flock.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/calls/wincrash.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/fds.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/mem/leaks.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/enum/filelockflags.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/byhandlefileinformation.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/stdckdint.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fd.h"
|
||||
#include "libc/sysv/consts/fio.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
struct FileLock {
|
||||
struct FileLock *next;
|
||||
int64_t off;
|
||||
int64_t len;
|
||||
int fd;
|
||||
bool exc;
|
||||
};
|
||||
|
||||
struct FileLocks {
|
||||
pthread_mutex_t mu;
|
||||
struct FileLock *list;
|
||||
struct FileLock *free;
|
||||
};
|
||||
|
||||
static struct FileLocks g_locks = {
|
||||
.mu = PTHREAD_MUTEX_INITIALIZER,
|
||||
};
|
||||
|
||||
static textwindows struct FileLock *NewFileLock(void) {
|
||||
struct FileLock *fl;
|
||||
if (g_locks.free) {
|
||||
fl = g_locks.free;
|
||||
g_locks.free = fl->next;
|
||||
} else {
|
||||
unassert((fl = may_leak(_weaken(malloc)(sizeof(*fl)))));
|
||||
}
|
||||
bzero(fl, sizeof(*fl));
|
||||
fl->next = g_locks.list;
|
||||
g_locks.list = fl;
|
||||
return fl;
|
||||
}
|
||||
|
||||
static textwindows void FreeFileLock(struct FileLock *fl) {
|
||||
fl->next = g_locks.free;
|
||||
g_locks.free = fl;
|
||||
}
|
||||
|
||||
static textwindows bool OverlapsFileLock(struct FileLock *fl, int64_t off,
|
||||
int64_t len) {
|
||||
uint64_t BegA, EndA, BegB, EndB;
|
||||
BegA = off;
|
||||
EndA = off + (len - 1);
|
||||
BegB = fl->off;
|
||||
EndB = fl->off + (fl->len - 1);
|
||||
return MAX(BegA, BegB) <= MIN(EndA, EndB);
|
||||
}
|
||||
|
||||
static textwindows bool EncompassesFileLock(struct FileLock *fl, int64_t off,
|
||||
int64_t len) {
|
||||
return off <= fl->off && fl->off + fl->len <= off + len;
|
||||
}
|
||||
|
||||
static textwindows bool EqualsFileLock(struct FileLock *fl, int64_t off,
|
||||
int64_t len) {
|
||||
return fl->off == off && off + len == fl->off + fl->len;
|
||||
}
|
||||
|
||||
textwindows void sys_fcntl_nt_lock_cleanup(int fd) {
|
||||
struct FileLock *fl, *ft, **flp;
|
||||
_pthread_mutex_lock(&g_locks.mu);
|
||||
for (flp = &g_locks.list, fl = *flp; fl;) {
|
||||
if (fl->fd == fd) {
|
||||
*flp = fl->next;
|
||||
ft = fl->next;
|
||||
FreeFileLock(fl);
|
||||
fl = ft;
|
||||
} else {
|
||||
flp = &fl->next;
|
||||
fl = *flp;
|
||||
}
|
||||
}
|
||||
_pthread_mutex_unlock(&g_locks.mu);
|
||||
}
|
||||
|
||||
static textwindows int64_t GetfileSize(int64_t handle) {
|
||||
struct NtByHandleFileInformation wst;
|
||||
if (!GetFileInformationByHandle(handle, &wst))
|
||||
return __winerr();
|
||||
return (wst.nFileSizeHigh + 0ull) << 32 | wst.nFileSizeLow;
|
||||
}
|
||||
|
||||
static textwindows int sys_fcntl_nt_lock(struct Fd *f, int fd, int cmd,
|
||||
uintptr_t arg) {
|
||||
uint32_t flags;
|
||||
struct flock *l;
|
||||
int64_t off, len, end;
|
||||
struct FileLock *fl, *ft, **flp;
|
||||
|
||||
if (!_weaken(malloc)) {
|
||||
return enomem();
|
||||
}
|
||||
|
||||
l = (struct flock *)arg;
|
||||
len = l->l_len;
|
||||
off = l->l_start;
|
||||
|
||||
switch (l->l_whence) {
|
||||
case SEEK_SET:
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
off = f->cursor->shared->pointer + off;
|
||||
break;
|
||||
case SEEK_END: {
|
||||
int64_t size;
|
||||
if ((size = GetfileSize(f->handle)) == -1)
|
||||
return -1;
|
||||
off = size - off;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return einval();
|
||||
}
|
||||
|
||||
if (!len) {
|
||||
len = INT64_MAX - off;
|
||||
}
|
||||
|
||||
if (off < 0 || len < 0 || ckd_add(&end, off, len)) {
|
||||
return einval();
|
||||
}
|
||||
|
||||
bool32 ok;
|
||||
struct NtOverlapped ov = {.hEvent = f->handle, .Pointer = off};
|
||||
|
||||
if (l->l_type == F_RDLCK || l->l_type == F_WRLCK) {
|
||||
|
||||
if (cmd == F_SETLK || cmd == F_SETLKW) {
|
||||
// make it possible to transition read locks to write locks
|
||||
for (flp = &g_locks.list, fl = *flp; fl;) {
|
||||
if (fl->fd == fd) {
|
||||
if (EqualsFileLock(fl, off, len)) {
|
||||
if (fl->exc == (l->l_type == F_WRLCK)) {
|
||||
// we already have this lock
|
||||
return 0;
|
||||
} else {
|
||||
// unlock our read lock and acquire write lock below
|
||||
if (UnlockFileEx(f->handle, 0, len, len >> 32, &ov)) {
|
||||
*flp = fl->next;
|
||||
ft = fl->next;
|
||||
FreeFileLock(fl);
|
||||
fl = ft;
|
||||
continue;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else if (OverlapsFileLock(fl, off, len)) {
|
||||
return enotsup();
|
||||
}
|
||||
}
|
||||
flp = &fl->next;
|
||||
fl = *flp;
|
||||
}
|
||||
}
|
||||
|
||||
// return better information on conflicting locks if possible
|
||||
if (cmd == F_GETLK) {
|
||||
for (fl = g_locks.list; fl; fl = fl->next) {
|
||||
if (fl->fd == fd && //
|
||||
OverlapsFileLock(fl, off, len) &&
|
||||
(l->l_type == F_WRLCK || !fl->exc)) {
|
||||
l->l_whence = SEEK_SET;
|
||||
l->l_start = fl->off;
|
||||
l->l_len = fl->len;
|
||||
l->l_type = fl->exc ? F_WRLCK : F_RDLCK;
|
||||
l->l_pid = getpid();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flags = 0;
|
||||
if (cmd != F_SETLKW) {
|
||||
// TODO(jart): we should use expo backoff in wrapper function
|
||||
// should not matter since sqlite doesn't need it
|
||||
flags |= kNtLockfileFailImmediately;
|
||||
}
|
||||
if (l->l_type == F_WRLCK) {
|
||||
flags |= kNtLockfileExclusiveLock;
|
||||
}
|
||||
ok = LockFileEx(f->handle, flags, 0, len, len >> 32, &ov);
|
||||
if (cmd == F_GETLK) {
|
||||
if (ok) {
|
||||
l->l_type = F_UNLCK;
|
||||
if (!UnlockFileEx(f->handle, 0, len, len >> 32, &ov)) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
l->l_pid = -1;
|
||||
ok = true;
|
||||
}
|
||||
} else if (ok) {
|
||||
fl = NewFileLock();
|
||||
fl->off = off;
|
||||
fl->len = len;
|
||||
fl->exc = l->l_type == F_WRLCK;
|
||||
fl->fd = fd;
|
||||
}
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
if (l->l_type == F_UNLCK) {
|
||||
if (cmd == F_GETLK)
|
||||
return einval();
|
||||
|
||||
// allow a big range to unlock many small ranges
|
||||
for (flp = &g_locks.list, fl = *flp; fl;) {
|
||||
if (fl->fd == fd && EncompassesFileLock(fl, off, len)) {
|
||||
struct NtOverlapped ov = {.hEvent = f->handle, .Pointer = fl->off};
|
||||
if (UnlockFileEx(f->handle, 0, fl->len, fl->len >> 32, &ov)) {
|
||||
*flp = fl->next;
|
||||
ft = fl->next;
|
||||
FreeFileLock(fl);
|
||||
fl = ft;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
flp = &fl->next;
|
||||
fl = *flp;
|
||||
}
|
||||
}
|
||||
|
||||
// win32 won't let us carve up existing locks
|
||||
int overlap_count = 0;
|
||||
for (fl = g_locks.list; fl; fl = fl->next) {
|
||||
if (fl->fd == fd && //
|
||||
OverlapsFileLock(fl, off, len)) {
|
||||
++overlap_count;
|
||||
}
|
||||
}
|
||||
|
||||
// try to handle the carving cases needed by sqlite
|
||||
if (overlap_count == 1) {
|
||||
for (fl = g_locks.list; fl; fl = fl->next) {
|
||||
if (fl->fd == fd && //
|
||||
off <= fl->off && //
|
||||
off + len >= fl->off && //
|
||||
off + len < fl->off + fl->len) {
|
||||
// cleave left side of lock
|
||||
struct NtOverlapped ov = {.hEvent = f->handle, .Pointer = fl->off};
|
||||
if (!UnlockFileEx(f->handle, 0, fl->len, fl->len >> 32, &ov)) {
|
||||
return -1;
|
||||
}
|
||||
fl->len = (fl->off + fl->len) - (off + len);
|
||||
fl->off = off + len;
|
||||
ov.Pointer = fl->off;
|
||||
if (!LockFileEx(f->handle, kNtLockfileExclusiveLock, 0, fl->len,
|
||||
fl->len >> 32, &ov)) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (overlap_count) {
|
||||
return enotsup();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return einval();
|
||||
}
|
||||
|
||||
static textwindows int sys_fcntl_nt_dupfd(int fd, int cmd, int start) {
|
||||
if (start < 0)
|
||||
return einval();
|
||||
return sys_dup_nt(fd, -1, (cmd == F_DUPFD_CLOEXEC ? _O_CLOEXEC : 0), start);
|
||||
}
|
||||
|
||||
textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) {
|
||||
int rc;
|
||||
BLOCK_SIGNALS;
|
||||
if (__isfdkind(fd, kFdFile) || //
|
||||
__isfdkind(fd, kFdSocket) || //
|
||||
__isfdkind(fd, kFdConsole) || //
|
||||
__isfdkind(fd, kFdDevNull) || //
|
||||
__isfdkind(fd, kFdDevRandom)) {
|
||||
if (cmd == F_GETFL) {
|
||||
rc = g_fds.p[fd].flags & (O_ACCMODE | _O_APPEND | _O_DIRECT |
|
||||
_O_NONBLOCK | _O_RANDOM | _O_SEQUENTIAL);
|
||||
} else if (cmd == F_SETFL) {
|
||||
rc = sys_fcntl_nt_setfl(fd, arg);
|
||||
} else if (cmd == F_GETFD) {
|
||||
if (g_fds.p[fd].flags & _O_CLOEXEC) {
|
||||
rc = FD_CLOEXEC;
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
} else if (cmd == F_SETFD) {
|
||||
if (arg & FD_CLOEXEC) {
|
||||
g_fds.p[fd].flags |= _O_CLOEXEC;
|
||||
} else {
|
||||
g_fds.p[fd].flags &= ~_O_CLOEXEC;
|
||||
}
|
||||
rc = 0;
|
||||
} else if (cmd == F_SETLK || cmd == F_SETLKW || cmd == F_GETLK) {
|
||||
struct Fd *f = g_fds.p + fd;
|
||||
if (f->cursor) {
|
||||
_pthread_mutex_lock(&g_locks.mu);
|
||||
rc = sys_fcntl_nt_lock(f, fd, cmd, arg);
|
||||
_pthread_mutex_unlock(&g_locks.mu);
|
||||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
} else if (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC) {
|
||||
rc = sys_fcntl_nt_dupfd(fd, cmd, arg);
|
||||
} else {
|
||||
rc = einval();
|
||||
}
|
||||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
ALLOW_SIGNALS;
|
||||
return rc;
|
||||
}
|
||||
+18
-102
@@ -16,21 +16,9 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/cp.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/flock.h"
|
||||
#include "libc/calls/struct/flock.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
#undef fcntl
|
||||
|
||||
/**
|
||||
* Does things with file descriptor, e.g.
|
||||
@@ -61,34 +49,12 @@
|
||||
* - `O_NONBLOCK` may be changed
|
||||
* - `O_APPEND` may be changed
|
||||
* - `O_DIRECT` may be changed
|
||||
* - `O_SEQUENTIAL` may be changed (no-op on non-Windows)
|
||||
* - `O_RANDOM` may be changed (no-op on non-Windows)
|
||||
* - Other bits (`O_ACCMODE`, `O_CREAT`, etc.) are ignored
|
||||
* - `F_DUPFD` is like dup() but `arg` is a minimum result, e.g. 3
|
||||
* - `F_DUPFD_CLOEXEC` ditto but sets `O_CLOEXEC` on returned fd
|
||||
* - `F_SETLK` for record locking where `arg` is `struct flock *`
|
||||
* - `F_SETLKW` ditto but waits for lock (SQLite avoids this)
|
||||
* - `F_GETLK` to retrieve information about a record lock
|
||||
* - `F_OFD_SETLK` for better non-blocking lock (Linux 3.15+ only)
|
||||
* - `F_OFD_SETLKW` for better blocking lock (Linux 3.15+ only)
|
||||
* - `F_OFD_GETLK` for better lock querying (Linux 3.15+ only)
|
||||
* - `F_FULLFSYNC` on MacOS for fsync() with release barrier
|
||||
* - `F_BARRIERFSYNC` on MacOS for fsync() with even more barriers
|
||||
* - `F_SETNOSIGPIPE` on MacOS and NetBSD to control `SIGPIPE`
|
||||
* - `F_GETNOSIGPIPE` on MacOS and NetBSD to control `SIGPIPE`
|
||||
* - `F_GETPATH` on MacOS and NetBSD where arg is `char[PATH_MAX]`
|
||||
* - `F_MAXFD` on NetBSD to get max open file descriptor
|
||||
* - `F_NOCACHE` on MacOS to toggle data caching
|
||||
* - `F_GETPIPE_SZ` on Linux to get pipe size
|
||||
* - `F_SETPIPE_SZ` on Linux to set pipe size
|
||||
* - `F_NOTIFY` raise `SIGIO` upon `fd` events in `arg` (Linux only)
|
||||
* - `DN_ACCESS` for file access
|
||||
* - `DN_MODIFY` for file modifications
|
||||
* - `DN_CREATE` for file creations
|
||||
* - `DN_DELETE` for file deletions
|
||||
* - `DN_RENAME` for file renames
|
||||
* - `DN_ATTRIB` for file attribute changes
|
||||
* - `DN_MULTISHOT` bitwise or for realtime signals (non-coalesced)
|
||||
* @param arg can be FD_CLOEXEC, etc. depending
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise EBADF if `fd` isn't a valid open file descriptor
|
||||
@@ -100,82 +66,32 @@
|
||||
* @raise ESRCH if `cmd` is `F_SETOWN` and process group not found
|
||||
* @raise ENOTSUP on Windows if locking operation isn't supported yet
|
||||
* @raise EDEADLK if `cmd` was `F_SETLKW` and waiting would deadlock
|
||||
* @raise EMFILE if `cmd` is `F_DUPFD` or `F_DUPFD_CLOEXEC` and
|
||||
* `RLIMIT_NOFILE` would be exceeded
|
||||
* @cancelationpoint when `cmd` is `F_SETLKW` or `F_OFD_SETLKW`
|
||||
* @raise EMFILE if `cmd` is `F_DUPFD` and `RLIMIT_NOFILE` is exceeded
|
||||
* @cancelationpoint when `cmd` is `F_SETLKW`
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
*/
|
||||
int fcntl(int fd, int cmd, ...) {
|
||||
int rc;
|
||||
va_list va;
|
||||
uintptr_t arg;
|
||||
|
||||
va_start(va, cmd);
|
||||
arg = va_arg(va, uintptr_t);
|
||||
va_end(va);
|
||||
|
||||
if (fd >= 0) {
|
||||
if (cmd >= 0) {
|
||||
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||
rc = _weaken(__zipos_fcntl)(fd, cmd, arg);
|
||||
} else if (!IsWindows()) {
|
||||
if (cmd == F_SETLKW || cmd == F_OFD_SETLKW) {
|
||||
BEGIN_CANCELATION_POINT;
|
||||
rc = sys_fcntl(fd, cmd, arg, __sys_fcntl_cp);
|
||||
END_CANCELATION_POINT;
|
||||
} else {
|
||||
rc = sys_fcntl(fd, cmd, arg, __sys_fcntl);
|
||||
if (rc != -1 && (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC) &&
|
||||
__isfdkind(rc, kFdZip)) {
|
||||
_weaken(__zipos_postdup)(fd, rc);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rc = sys_fcntl_nt(fd, cmd, arg);
|
||||
}
|
||||
} else {
|
||||
rc = einval();
|
||||
}
|
||||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
|
||||
#if SYSDEBUG
|
||||
if (rc != -1 && cmd == F_GETFL) {
|
||||
STRACE("fcntl(%d, F_GETFL) → %s", fd, DescribeOpenFlags(rc));
|
||||
if (cmd == F_GETFL) {
|
||||
return __fcntl_getfl(fd);
|
||||
} else if (cmd == F_GETFD) {
|
||||
return __fcntl_getfd(fd);
|
||||
} else if (cmd == F_SETFL) {
|
||||
STRACE("fcntl(%d, F_SETFL, %s) → %d% m", fd, DescribeOpenFlags(arg), rc);
|
||||
} else if (cmd == F_GETFD || //
|
||||
cmd == F_GETOWN || //
|
||||
cmd == F_FULLFSYNC || //
|
||||
cmd == F_BARRIERFSYNC || //
|
||||
cmd == F_MAXFD) {
|
||||
STRACE("fcntl(%d, %s) → %d% m", fd, DescribeFcntlCmd(cmd), rc);
|
||||
} else if (cmd == F_GETFL) {
|
||||
STRACE("fcntl(%d, %s) → %s% m", fd, DescribeFcntlCmd(cmd),
|
||||
DescribeOpenFlags(rc));
|
||||
} else if (cmd == F_SETLK || //
|
||||
cmd == F_SETLKW || //
|
||||
cmd == F_GETLK || //
|
||||
((SupportsLinux() || SupportsXnu()) && //
|
||||
cmd != -1 && //
|
||||
(cmd == F_OFD_SETLK || //
|
||||
cmd == F_OFD_SETLKW || //
|
||||
cmd == F_OFD_GETLK))) {
|
||||
STRACE("fcntl(%d, %s, %s) → %d% m", fd, DescribeFcntlCmd(cmd),
|
||||
DescribeFlock(cmd, (struct flock *)arg), rc);
|
||||
} else if ((SupportsNetbsd() || SupportsXnu()) && cmd != -1 &&
|
||||
cmd == F_GETPATH) {
|
||||
STRACE("fcntl(%d, %s, [%s]) → %d% m", fd, DescribeFcntlCmd(cmd),
|
||||
!rc ? (const char *)arg : "n/a", rc);
|
||||
} else if (SupportsLinux() && cmd != -1 && cmd == F_NOTIFY) {
|
||||
STRACE("fcntl(%d, %s, %s) → %d% m", fd, DescribeFcntlCmd(cmd),
|
||||
DescribeDnotifyFlags(arg), rc);
|
||||
return __fcntl_setfl(fd, arg);
|
||||
} else if (cmd == F_SETFD) {
|
||||
return __fcntl_setfd(fd, arg);
|
||||
} else if (cmd == F_DUPFD) {
|
||||
return __fcntl_dupfd(fd, arg);
|
||||
} else if (cmd == F_DUPFD_CLOEXEC) {
|
||||
return __fcntl_dupfd_cloexec(fd, arg);
|
||||
} else if (cmd == F_GETLK || cmd == F_SETLK || cmd == F_SETLKW) {
|
||||
return __fcntl_lock(fd, cmd, arg);
|
||||
} else {
|
||||
STRACE("fcntl(%d, %s, %ld) → %d% m", fd, DescribeFcntlCmd(cmd), arg, rc);
|
||||
return __fcntl_misc(fd, cmd, arg);
|
||||
}
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2025 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 │
|
||||
@@ -16,33 +16,34 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/flock.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
int sys_fcntl(int fd, int cmd, uintptr_t arg, int impl(int, int, ...)) {
|
||||
int e, rc;
|
||||
bool islock;
|
||||
if ((islock = cmd == F_GETLK || //
|
||||
cmd == F_SETLK || //
|
||||
cmd == F_SETLKW)) {
|
||||
if (!arg) {
|
||||
return efault();
|
||||
}
|
||||
cosmo2flock(arg);
|
||||
}
|
||||
e = errno;
|
||||
rc = impl(fd, cmd, arg);
|
||||
if (islock) {
|
||||
flock2cosmo(arg);
|
||||
} else if (rc == -1 && cmd == F_DUPFD_CLOEXEC && errno == EINVAL) {
|
||||
errno = e;
|
||||
rc = __fixupnewfd(impl(fd, F_DUPFD, arg), O_CLOEXEC);
|
||||
int __fcntl_dupfd(int fd, ...) {
|
||||
int rc;
|
||||
int arg;
|
||||
va_list va;
|
||||
va_start(va, fd);
|
||||
arg = va_arg(va, int);
|
||||
va_end(va);
|
||||
if (!IsWindows()) {
|
||||
rc = __sys_fcntl(fd, F_DUPFD, arg);
|
||||
} else if (arg < 0) {
|
||||
rc = einval();
|
||||
} else if (__isfdopen(fd)) {
|
||||
rc = sys_dup_nt(fd, -1, 0, arg);
|
||||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
if (rc != -1 && _weaken(__zipos_postdup))
|
||||
_weaken(__zipos_postdup)(fd, rc);
|
||||
STRACE("fcntl(%d, F_DUPFD, %d) → %d% m", fd, arg, rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2025 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 │
|
||||
@@ -16,51 +16,54 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/fds.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/runtime/syslib.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
int sys_getentropy(void *, size_t) asm("sys_getrandom");
|
||||
|
||||
/**
|
||||
* Returns random seeding bytes, the POSIX way.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise EFAULT if the `n` bytes at `p` aren't valid memory
|
||||
* @raise EIO is returned if more than 256 bytes are requested
|
||||
* @see getrandom()
|
||||
*/
|
||||
int getentropy(void *p, size_t n) {
|
||||
int __fcntl_dupfd_cloexec(int fd, ...) {
|
||||
int rc;
|
||||
if (n > 256) {
|
||||
rc = eio();
|
||||
} else if ((!p && n)) {
|
||||
rc = efault();
|
||||
} else if (IsXnuSilicon()) {
|
||||
rc = __syslib->__getentropy(p, n);
|
||||
} else if (IsXnu() || IsOpenbsd()) {
|
||||
rc = sys_getentropy(p, n);
|
||||
} else {
|
||||
ssize_t got;
|
||||
BLOCK_CANCELATION;
|
||||
rc = 0;
|
||||
for (size_t i = 0; i < n; i += got) {
|
||||
got = __getrandom(p + i, n - i, 0);
|
||||
if (got == -1) {
|
||||
if (errno == EAGAIN || errno == EINTR) {
|
||||
got = 0;
|
||||
} else {
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int arg;
|
||||
va_list va;
|
||||
va_start(va, fd);
|
||||
arg = va_arg(va, int);
|
||||
va_end(va);
|
||||
if (arg < 0) {
|
||||
rc = einval();
|
||||
} else if (!IsWindows()) {
|
||||
int cmd;
|
||||
if (IsXnu()) {
|
||||
cmd = 67;
|
||||
} else if (IsFreebsd()) {
|
||||
cmd = 17;
|
||||
} else if (IsOpenbsd()) {
|
||||
cmd = 10;
|
||||
} else if (IsNetbsd()) {
|
||||
cmd = 12;
|
||||
} else {
|
||||
cmd = F_DUPFD_CLOEXEC;
|
||||
}
|
||||
ALLOW_CANCELATION;
|
||||
int e = errno;
|
||||
rc = __sys_fcntl(fd, cmd, arg);
|
||||
if (rc == -1 && errno == EINVAL) {
|
||||
errno = e;
|
||||
rc = __fixupnewfd(__sys_fcntl(fd, F_DUPFD, arg), O_CLOEXEC);
|
||||
}
|
||||
} else if (__isfdopen(fd)) {
|
||||
rc = sys_dup_nt(fd, -1, O_CLOEXEC, arg);
|
||||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("getentropy(%p, %'zu) → %'ld% m", p, n, rc);
|
||||
if (rc != -1 && _weaken(__zipos_postdup))
|
||||
_weaken(__zipos_postdup)(fd, rc);
|
||||
STRACE("fcntl(%d, F_DUPFD_CLOEXEC, %d) → %d% m", fd, arg, rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2025 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 │
|
||||
@@ -16,36 +16,31 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ 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/errno.h"
|
||||
#include "libc/intrin/fds.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/sysv/pib.h"
|
||||
|
||||
/**
|
||||
* Returns true if file descriptor exists.
|
||||
*
|
||||
* This function is equivalent to:
|
||||
*
|
||||
* fcntl(fd, F_GETFL) != -1
|
||||
*
|
||||
* Except it won't clobber `errno` and has minimal linkage.
|
||||
*/
|
||||
bool32 fdexists(int fd) {
|
||||
bool32 res;
|
||||
if (IsWindows() || IsMetal()) {
|
||||
int e = errno;
|
||||
if (__sys_fcntl(fd, F_GETFL) != -1) {
|
||||
res = true;
|
||||
int __fcntl_getfd(int fd) {
|
||||
int rc;
|
||||
if (__isfdkind(fd, kFdZip)) {
|
||||
rc = FD_CLOEXEC;
|
||||
} else if (!IsWindows()) {
|
||||
rc = __sys_fcntl(fd, F_GETFD, 0);
|
||||
} else if (__isfdopen(fd)) {
|
||||
if (__get_pib()->fds.p[fd].flags & O_CLOEXEC) {
|
||||
rc = FD_CLOEXEC;
|
||||
} else {
|
||||
errno = e;
|
||||
res = false;
|
||||
rc = 0;
|
||||
}
|
||||
} else {
|
||||
res = __isfdopen(fd);
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("fdexists(%d) → %hhhd", fd, res);
|
||||
return res;
|
||||
STRACE("fcntl(%d, F_GETFD) → %d% m", fd, rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2025 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/sysv/pib.h"
|
||||
|
||||
#define o_nonblock 0004
|
||||
#define o_append 0010
|
||||
#define o_async 0100
|
||||
#define o_direct (IsFreebsd() ? 0x00010000 : IsNetbsd() ? 0x00080000 : 0)
|
||||
|
||||
static int __unxoflags(int flags) {
|
||||
if (flags == -1)
|
||||
return -1;
|
||||
if (IsLinux())
|
||||
return flags;
|
||||
int flags2 = flags & O_ACCMODE;
|
||||
if (flags & 4)
|
||||
flags2 |= O_NONBLOCK;
|
||||
if (flags & 8)
|
||||
flags2 |= O_APPEND;
|
||||
if (flags & 128)
|
||||
flags2 |= O_SYNC;
|
||||
if (flags & (IsXnu() ? 0x00400000
|
||||
: IsFreebsd() ? 0x01000000
|
||||
: IsOpenbsd() ? 0x00000080
|
||||
: IsNetbsd() ? 0x00010000
|
||||
: 0))
|
||||
flags2 |= O_DSYNC;
|
||||
if (flags & (IsFreebsd() ? 0x00010000 : IsNetbsd() ? 0x00080000 : 0))
|
||||
flags2 |= O_DIRECT;
|
||||
if (flags & (IsXnu() ? 0x01000000
|
||||
: IsFreebsd() ? 0x00100000
|
||||
: IsOpenbsd() ? 0x00010000
|
||||
: IsNetbsd() ? 0x00400000
|
||||
: 0))
|
||||
flags2 |= O_CLOEXEC;
|
||||
return flags2;
|
||||
}
|
||||
|
||||
int __fcntl_getfl(int fd) {
|
||||
int rc;
|
||||
if (__isfdkind(fd, kFdZip)) {
|
||||
goto WindowsImpl;
|
||||
} else if (!IsWindows()) {
|
||||
rc = __sys_fcntl(fd, F_GETFL, 0);
|
||||
if (rc != -1 && !IsLinux())
|
||||
rc = __unxoflags(rc);
|
||||
} else if (__isfdopen(fd)) {
|
||||
WindowsImpl:
|
||||
rc = __get_pib()->fds.p[fd].flags &
|
||||
(O_ACCMODE | O_APPEND | O_DIRECT | O_NONBLOCK);
|
||||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("fcntl(%d, F_GETFL) → %s% m", fd, DescribeOpenFlags(rc));
|
||||
return rc;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2025 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 │
|
||||
@@ -16,46 +16,52 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/cp.internal.h"
|
||||
#include "libc/calls/flocks.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/flock.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/likely.h"
|
||||
#include "libc/intrin/fds.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/sysv/pib.h"
|
||||
|
||||
#define IsPeek(request) (IsLinux() && (request) - 1u < 3)
|
||||
|
||||
/**
|
||||
* Traces process.
|
||||
*
|
||||
* @param op can be PTRACE_xxx
|
||||
* @param pid is child process id
|
||||
* @param addr points inside child address space
|
||||
* @param data is address of output word when peeking
|
||||
* @note de facto linux only
|
||||
* @vforksafe
|
||||
*/
|
||||
int sys_ptrace(int op, ...) {
|
||||
int __fcntl_lock(int fd, int cmd, ...) {
|
||||
int rc;
|
||||
va_list va;
|
||||
int rc, pid;
|
||||
long addr, *data;
|
||||
va_start(va, op);
|
||||
pid = va_arg(va, int);
|
||||
addr = va_arg(va, long);
|
||||
data = va_arg(va, long *);
|
||||
uintptr_t arg;
|
||||
va_start(va, cmd);
|
||||
arg = va_arg(va, uintptr_t);
|
||||
va_end(va);
|
||||
rc = __sys_ptrace(op, pid, addr, data);
|
||||
#if SYSDEBUG
|
||||
if (UNLIKELY(__strace > 0) && strace_enabled(0) > 0) {
|
||||
if (rc != -1 && IsPeek(op) && data) {
|
||||
STRACE("sys_ptrace(%s, %d, %p, [%p]) → %p% m", DescribePtrace(op), pid,
|
||||
addr, *data, rc);
|
||||
if (__isfdkind(fd, kFdZip)) {
|
||||
rc = einval();
|
||||
} else if (!IsWindows()) {
|
||||
cosmo2flock(arg);
|
||||
if (cmd == F_SETLKW) {
|
||||
BEGIN_CANCELATION_POINT;
|
||||
rc = __sys_fcntl_cp(fd, cmd, arg);
|
||||
END_CANCELATION_POINT;
|
||||
} else {
|
||||
STRACE("sys_ptrace(%s, %d, %p, %p) → %p% m", DescribePtrace(op), pid,
|
||||
addr, data, rc);
|
||||
rc = __sys_fcntl(fd, cmd, arg);
|
||||
}
|
||||
flock2cosmo(arg);
|
||||
} else if (__isfdopen(fd)) {
|
||||
BLOCK_SIGNALS;
|
||||
struct Fd *f = __get_pib()->fds.p + fd;
|
||||
if (f->cursor) {
|
||||
rc = __flocks_fcntl(f, fd, cmd, arg, _SigMask);
|
||||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
ALLOW_SIGNALS;
|
||||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
#endif
|
||||
STRACE("fcntl(%d, %s, %s) → %d% m", fd, DescribeFcntlCmd(cmd),
|
||||
DescribeFlock(cmd, (struct flock *)arg), rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2025 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 │
|
||||
@@ -16,20 +16,25 @@
|
||||
│ 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-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Creates anonymous file.
|
||||
*
|
||||
* @param name is used for the `/proc/self/fd/FD` symlink
|
||||
* @param flags can have `MFD_CLOEXEC`, `MFD_ALLOW_SEALING`
|
||||
* @raise ENOSYS if not RHEL8+
|
||||
*/
|
||||
int memfd_create(const char *name, unsigned int flags) {
|
||||
int __fcntl_misc(int fd, int cmd, ...) {
|
||||
int rc;
|
||||
rc = sys_memfd_create(name, flags);
|
||||
STRACE("memfd_create(%#s, %#x) → %d% m", name, flags, rc);
|
||||
va_list va;
|
||||
uintptr_t arg;
|
||||
va_start(va, cmd);
|
||||
arg = va_arg(va, uintptr_t);
|
||||
va_end(va);
|
||||
if (IsWindows() || IsMetal()) {
|
||||
rc = einval();
|
||||
} else {
|
||||
rc = __sys_fcntl(fd, cmd, arg);
|
||||
}
|
||||
STRACE("fcntl(%d, %s, %ld) → %d% m", fd, DescribeFcntlCmd(cmd), arg, rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2025 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 │
|
||||
@@ -16,44 +16,42 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/sysv/consts/ptrace.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Traces process.
|
||||
*
|
||||
* This API is terrible. Consider using sys_ptrace().
|
||||
*
|
||||
* @param request can be PTRACE_xxx
|
||||
* @note de facto linux only atm
|
||||
* @vforksafe
|
||||
*/
|
||||
long ptrace(int request, ...) {
|
||||
// TODO(jart): FreeBSD addr and data args are different
|
||||
int pid;
|
||||
int __fcntl_setfd(int fd, ...) {
|
||||
int rc;
|
||||
int arg;
|
||||
va_list va;
|
||||
bool ispeek;
|
||||
long rc, peek, addr, *data;
|
||||
va_start(va, request);
|
||||
pid = va_arg(va, int);
|
||||
addr = va_arg(va, long);
|
||||
data = va_arg(va, long *);
|
||||
va_start(va, fd);
|
||||
arg = va_arg(va, int);
|
||||
va_end(va);
|
||||
if (request == -1) {
|
||||
rc = einval(); /* see consts.sh */
|
||||
if (arg & ~FD_CLOEXEC) {
|
||||
rc = einval();
|
||||
} else if (__isfdkind(fd, kFdZip)) {
|
||||
if (arg == FD_CLOEXEC) {
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = einval();
|
||||
}
|
||||
} else if (!IsWindows()) {
|
||||
rc = __sys_fcntl(fd, F_SETFD, arg);
|
||||
} else if (__isfdopen(fd)) {
|
||||
if (arg & FD_CLOEXEC) {
|
||||
__get_pib()->fds.p[fd].flags |= O_CLOEXEC;
|
||||
} else {
|
||||
__get_pib()->fds.p[fd].flags &= ~O_CLOEXEC;
|
||||
}
|
||||
rc = 0;
|
||||
} else {
|
||||
ispeek = IsLinux() && request - 1u < 3;
|
||||
if (ispeek)
|
||||
data = &peek;
|
||||
rc = __sys_ptrace(request, pid, addr, data);
|
||||
if (rc != -1 && ispeek)
|
||||
rc = peek;
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("ptrace(%s, %d, %p, %p) → %p% m", DescribePtrace(request), pid, addr,
|
||||
data, rc);
|
||||
STRACE("fcntl(%d, F_SETFD, %d) → %d% m", fd, arg, rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2025 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 │
|
||||
@@ -16,48 +16,43 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/createfileflags.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/fds.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fd.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static int __zipos_dupfd(int fd, int cmd, int start) {
|
||||
int __fcntl_setfl(int fd, ...) {
|
||||
int rc;
|
||||
if (start < 0)
|
||||
return einval();
|
||||
if (IsWindows()) {
|
||||
return sys_dup_nt(fd, -1, (cmd == F_DUPFD_CLOEXEC ? _O_CLOEXEC : 0), start);
|
||||
}
|
||||
rc = sys_fcntl(fd, cmd, start, __sys_fcntl);
|
||||
if (rc != -1) {
|
||||
__zipos_postdup(fd, rc);
|
||||
int arg;
|
||||
va_list va;
|
||||
va_start(va, fd);
|
||||
arg = va_arg(va, int);
|
||||
va_end(va);
|
||||
if (__isfdkind(fd, kFdZip)) {
|
||||
if (arg & O_APPEND) {
|
||||
rc = einval();
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
} else if (!IsWindows()) {
|
||||
int flags = __xoflags(arg);
|
||||
if (flags != -1) {
|
||||
rc = __sys_fcntl(fd, F_SETFL, flags);
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
} else if (__isfdopen(fd)) {
|
||||
rc = sys_fcntl_nt_setfl(&__get_pib()->fds.p[fd], arg);
|
||||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("fcntl(%d, F_SETFL, %s) → %d% m", fd, DescribeOpenFlags(arg), rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __zipos_fcntl(int fd, int cmd, uintptr_t arg) {
|
||||
if (cmd == F_GETFD) {
|
||||
if (g_fds.p[fd].flags & O_CLOEXEC) {
|
||||
return FD_CLOEXEC;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else if (cmd == F_SETFD) {
|
||||
if (arg & FD_CLOEXEC) {
|
||||
g_fds.p[fd].flags |= O_CLOEXEC;
|
||||
return FD_CLOEXEC;
|
||||
} else {
|
||||
g_fds.p[fd].flags &= ~O_CLOEXEC;
|
||||
return 0;
|
||||
}
|
||||
} else if (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC) {
|
||||
return __zipos_dupfd(fd, cmd, arg);
|
||||
} else {
|
||||
return einval();
|
||||
}
|
||||
}
|
||||
@@ -18,12 +18,14 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/struct/byhandlefileinformation.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#ifdef __x86_64__
|
||||
#include "libc/sysv/pib.h"
|
||||
#if SupportsWindows()
|
||||
|
||||
textwindows int sys_fdatasync_nt(int fd, bool fake) {
|
||||
struct NtByHandleFileInformation wst;
|
||||
@@ -31,9 +33,9 @@ textwindows int sys_fdatasync_nt(int fd, bool fake) {
|
||||
return ebadf();
|
||||
if (!__isfdkind(fd, kFdFile))
|
||||
return einval();
|
||||
if (GetFileType(g_fds.p[fd].handle) != kNtFileTypeDisk)
|
||||
if (GetFileType(__get_pib()->fds.p[fd].handle) != kNtFileTypeDisk)
|
||||
return einval();
|
||||
if (!GetFileInformationByHandle(g_fds.p[fd].handle, &wst))
|
||||
if (!GetFileInformationByHandle(__get_pib()->fds.p[fd].handle, &wst))
|
||||
return __winerr();
|
||||
if (wst.dwFileAttributes & kNtFileAttributeDirectory) {
|
||||
// Flushing a directory handle is possible, but it needs
|
||||
@@ -42,9 +44,9 @@ textwindows int sys_fdatasync_nt(int fd, bool fake) {
|
||||
}
|
||||
if (fake)
|
||||
return 0;
|
||||
if (_check_signal(false) == -1)
|
||||
return -1;
|
||||
return FlushFileBuffers(g_fds.p[fd].handle) ? 0 : __winerr();
|
||||
// TODO(jart): Call FlushViewOfFile() on memory maps?
|
||||
// See also msync() call added to SQLite.
|
||||
return FlushFileBuffers(__get_pib()->fds.p[fd].handle) ? 0 : __winerr();
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
+12
-11
@@ -29,10 +29,11 @@
|
||||
/**
|
||||
* Blocks until kernel flushes non-metadata buffers for fd to disk.
|
||||
*
|
||||
* NOTE: For `IsXnu()` it's recommended that `fcntl(F_FULLFSYNC)` be
|
||||
* favored instead of this function, and if that fails, the fallback
|
||||
* path should call `fsync()` see the SQLite codebase. In the future
|
||||
* Cosmopolitan might do this automatically.
|
||||
* MacOS doesn't officially have fdatasync. XNU claims to have fsync but
|
||||
* it doesn't do much. So this wrapper calls `fcntl(F_FULLFSYNC)` and if
|
||||
* it fails (due to lack of filesystem support) this falls back to fsync
|
||||
*
|
||||
* You should expect this function to take milliseconds to complete.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise ECANCELED if thread was cancelled in masked mode
|
||||
@@ -53,14 +54,14 @@ int fdatasync(int fd) {
|
||||
BEGIN_CANCELATION_POINT;
|
||||
if (__isfdkind(fd, kFdZip)) {
|
||||
rc = erofs();
|
||||
} else if (!IsWindows()) {
|
||||
if (!fake) {
|
||||
rc = sys_fdatasync(fd);
|
||||
} else {
|
||||
rc = sys_fsync_fake(fd);
|
||||
}
|
||||
} else {
|
||||
} else if (IsWindows()) {
|
||||
rc = sys_fdatasync_nt(fd, fake);
|
||||
} else if (fake) {
|
||||
rc = sys_fsync_fake(fd);
|
||||
} else if (IsXnu()) {
|
||||
rc = sys_fsync_xnu(fd);
|
||||
} else {
|
||||
rc = sys_fdatasync(fd);
|
||||
}
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("fdatasync%s(%d) → %d% m", fake ? "_fake" : "", fd, rc);
|
||||
|
||||
+42
-42
@@ -16,62 +16,62 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/ctype.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
static int IsAlpha(int c) {
|
||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
||||
}
|
||||
|
||||
static textwindows bool SubpathExistsThatsNotDirectory(char16_t *path) {
|
||||
char16_t *p;
|
||||
uint32_t attrs;
|
||||
while ((p = strrchr16(path, '\\'))) {
|
||||
if (p == path)
|
||||
// don't bother checking GetFileAttributes(u"\\")
|
||||
textwindows bool __hasregularparent(char16_t *p) {
|
||||
bool first = true;
|
||||
char16_t *e = p + strlen16(p);
|
||||
while (e > p) {
|
||||
if (e[-1] == '\\')
|
||||
break;
|
||||
if (p == path + 2 && IsAlpha(path[0]) && path[1] == ':')
|
||||
// don't bother checking GetFileAttributes(u"C:\\")
|
||||
if (e - p >= 2 && e[-1] == '?' && e[-2] == '\\')
|
||||
break;
|
||||
*p = u'\0';
|
||||
if ((attrs = GetFileAttributes(path)) != -1u &&
|
||||
!(attrs & kNtFileAttributeDirectory)) {
|
||||
return true;
|
||||
if (e - p >= 3 && e[-1] == '?' && e[-2] == '?' && e[-3] == '\\')
|
||||
break;
|
||||
if (e - p >= 3 && e[-1] == ':' && isalpha(e[-2]) && e[-3] == '\\')
|
||||
break;
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
uint32_t dwFileAttrs;
|
||||
if ((dwFileAttrs = GetFileAttributes(p)) != -1u)
|
||||
return !(dwFileAttrs & kNtFileAttributeDirectory);
|
||||
}
|
||||
while (e > p) {
|
||||
int c = e[-1];
|
||||
*--e = 0;
|
||||
if (c == '\\')
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
textwindows dontinline int64_t __fix_enotdir3(int64_t rc, char16_t *path1,
|
||||
char16_t *path2) {
|
||||
if (rc == -1 && (errno == kNtErrorPathNotFound || // Windows returns ENOTDIR
|
||||
errno == kNtErrorInvalidName)) { // e.g. has trailing slash
|
||||
bool isdir = false;
|
||||
if ((!path1 || !(isdir |= SubpathExistsThatsNotDirectory(path1))) &&
|
||||
(!path2 || !(isdir |= SubpathExistsThatsNotDirectory(path2)))) {
|
||||
errno = kNtErrorFileNotFound; // ENOENT
|
||||
} else if (isdir) {
|
||||
errno = kNtErrorPathNotFound; // ENOTDIR
|
||||
// When files don't exist, normally UNIX systems raise ENOENT, except if
|
||||
// one of the parent directory components in the path exists, but is not
|
||||
// actually a dirctory, in which case ENOTDIR is raised instead. Example
|
||||
// would be stat("/etc/passwd/lol") raising ENOTDIR since /etc/passwd is
|
||||
// obviously a regular file.
|
||||
textwindows int64_t __fix_enotdir(int64_t rc, char16_t *path) {
|
||||
if (rc == -1) {
|
||||
switch (errno) {
|
||||
case ENOTDIR:
|
||||
if (!__hasregularparent(path))
|
||||
errno = ENOENT;
|
||||
break;
|
||||
case ENOENT:
|
||||
if (__hasregularparent(path))
|
||||
errno = ENOTDIR;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
// WIN32 doesn't distinguish between ENOTDIR and ENOENT. UNIX strictly
|
||||
// requires that a directory component *exists* but is not a directory
|
||||
// whereas WIN32 will return ENOTDIR if a dir label simply isn't found
|
||||
//
|
||||
// - ENOTDIR: A component used as a directory in pathname is not, in
|
||||
// fact, a directory. -or- pathname is relative and dirfd is a file
|
||||
// descriptor referring to a file other than a directory.
|
||||
//
|
||||
// - ENOENT: A directory component in pathname does not exist or is a
|
||||
// dangling symbolic link.
|
||||
//
|
||||
textwindows int64_t __fix_enotdir(int64_t rc, char16_t *path) {
|
||||
return __fix_enotdir3(rc, path, 0);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2025 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 │
|
||||
@@ -16,23 +16,26 @@
|
||||
│ 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_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/files.h"
|
||||
|
||||
/**
|
||||
* Returns true if file exists and is a directory on Windows NT.
|
||||
*/
|
||||
bool isdirectory_nt(const char *path) {
|
||||
uint32_t x;
|
||||
char16_t path16[PATH_MAX];
|
||||
if (__mkntpath(path, path16) == -1)
|
||||
return -1;
|
||||
if ((x = GetFileAttributes(path16)) != -1u) {
|
||||
return !!(x & kNtFileAttributeDirectory);
|
||||
} else {
|
||||
return false;
|
||||
textwindows dontinline int64_t __fix_enotdir2(int64_t rc, char16_t *path1,
|
||||
char16_t *path2) {
|
||||
if (rc == -1) {
|
||||
switch (errno) {
|
||||
case ENOTDIR:
|
||||
if (!__hasregularparent(path1))
|
||||
if (!__hasregularparent(path2))
|
||||
errno = ENOENT;
|
||||
break;
|
||||
case ENOENT:
|
||||
if (__hasregularparent(path1) || //
|
||||
__hasregularparent(path2))
|
||||
errno = ENOTDIR;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -19,8 +19,8 @@
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fd.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
||||
// Applies file descriptor fixups on XNU or old Linux.
|
||||
@@ -34,7 +34,8 @@ int __fixupnewfd(int fd, int flags) {
|
||||
}
|
||||
if (flags & O_NONBLOCK) {
|
||||
unassert((file_mode = __sys_fcntl(fd, F_GETFL)) != -1);
|
||||
unassert(!__sys_fcntl(fd, F_SETFL, file_mode | O_NONBLOCK));
|
||||
unassert(
|
||||
!__sys_fcntl(fd, F_SETFL, file_mode | (IsLinux() ? O_NONBLOCK : 4)));
|
||||
}
|
||||
}
|
||||
return fd;
|
||||
|
||||
Loaded 100 of 2593 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user