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:
Justine Tunney committed 2025-11-16 21:15:26 -08:00
1 parent f1dc5cff44
commit 5ddb5c2ada
2593 files changed
+205176 -32550

No files matched your search

+1
View File
@@ -1,6 +1,7 @@
# -*- conf -*-
/o
/core
/cosmocc
/.cosmocc
+51 -37
View File
@@ -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
+2
View File
@@ -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
View File
@@ -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)
+43 -2
View File
@@ -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
View File
@@ -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()
+1 -1
View File
@@ -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
View File
@@ -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
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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.
BIN
View File
Binary file not shown.
+1
View File
@@ -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
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+55 -24
View File
@@ -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
+2 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -6,7 +6,7 @@
namespace ctl {
struct string;
class string;
class istream : public ios
{
+1 -1
View File
@@ -4,7 +4,7 @@
#define COSMOPOLITAN_CTL_NEW_H_
#ifndef TINY
__static_yoink("__demangle");
__static_yoink("cosmo_demangle");
#endif
namespace ctl {
+2 -1
View File
@@ -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) {
+1
View File
@@ -167,6 +167,7 @@ See below for detailed the API documentation.
#include <stdint.h>
#include <stdio.h>
#include <cosmotime.h>
#include <time.h>
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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))) {
+146
View File
@@ -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
View File
@@ -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);
+1 -1
View File
@@ -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
View File
@@ -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);
}
+98
View File
@@ -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);
}
+39
View File
@@ -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
View File
@@ -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;
}
+1
View File
@@ -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
View File
@@ -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());
}
+1
View File
@@ -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
View File
@@ -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);
+58
View File
@@ -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);
}
+3 -1
View File
@@ -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
View File
@@ -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)),
+3
View File
@@ -45,4 +45,7 @@ int main(int argc, char *argv[]) {
return 0;
}
#else
int main(int argc, char *argv[]) {
}
#endif /* __x86_64__ */
+3
View File
@@ -61,4 +61,7 @@ int main(int argc, char *argv[]) {
}
}
#else
int main(int argc, char *argv[]) {
}
#endif /* __x86_64__ */
+3
View File
@@ -43,4 +43,7 @@ int main(int argc, char *argv[]) {
;
}
#else
int main(int argc, char *argv[]) {
}
#endif /* __x86_64__ */
+22
View File
@@ -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
View File
@@ -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
View File
@@ -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
+15
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+330
View File
@@ -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;
}
+4 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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) {
+2 -1
View File
@@ -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
View File
@@ -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);
+1
View File
@@ -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"
+12 -23
View File
@@ -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__ */
+1
View File
@@ -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"
-3
View File
@@ -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;
}
+1
View File
@@ -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"
+8
View File
@@ -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;
}
+1 -2
View File
@@ -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
View File
@@ -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;
}
+7 -12
View File
@@ -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
View File
@@ -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
View File
@@ -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
+24 -49
View File
@@ -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
-22
View File
@@ -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_ */
+3 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+5 -3
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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) {
+5 -1
View File
@@ -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);
-375
View File
@@ -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
View File
@@ -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;
}
+80
View File
@@ -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();
}
}
+8 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+3 -2
View File
@@ -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