mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-26 20:40:28 +00:00
Make more fixes and improvements
- Remove PAGESIZE constant - Fix realloc() documentation - Fix ttyname_r() error reporting - Make forking more reliable on Windows - Make execvp() a few microseconds faster - Make system() a few microseconds faster - Tighten up the socket-related magic numbers - Loosen restrictions on mmap() offset alignment - Improve GetProgramExecutableName() with getenv("_") - Use mkstemp() as basis for mktemp(), tmpfile(), tmpfd() - Fix flakes in pthread_cancel_test, unix_test, fork_test - Fix recently introduced futex stack overflow regression - Let sockets be passed as stdio to subprocesses on Windows - Improve security of bind() on Windows w/ SO_EXCLUSIVEADDRUSE
This commit is contained in:
parent
140a8a52e5
commit
18bb5888e1
311 changed files with 1239 additions and 2622 deletions
|
@ -134,12 +134,6 @@ o/$(MODE)/libc/calls/mkntenvblock.o: private \
|
|||
CPPFLAGS += \
|
||||
-DSTACK_FRAME_UNLIMITED
|
||||
|
||||
# we must segregate codegen because:
|
||||
# file contains multiple independently linkable apis
|
||||
COPTS += \
|
||||
-ffunction-sections \
|
||||
-fdata-sections
|
||||
|
||||
# we always want -Os because:
|
||||
# va_arg codegen is very bloated in default mode
|
||||
o//libc/calls/open.o \
|
||||
|
|
|
@ -17,14 +17,14 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/nexgen32e/rdtsc.h"
|
||||
#include "libc/nexgen32e/x86feature.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
static struct {
|
||||
pthread_once_t once;
|
||||
_Atomic(uint32_t) once;
|
||||
struct timespec base_wall;
|
||||
uint64_t base_tick;
|
||||
} g_mono;
|
||||
|
@ -39,7 +39,7 @@ int sys_clock_gettime_mono(struct timespec *time) {
|
|||
uint64_t cycles;
|
||||
struct timespec res;
|
||||
if (X86_HAVE(INVTSC)) {
|
||||
pthread_once(&g_mono.once, sys_clock_gettime_mono_init);
|
||||
cosmo_once(&g_mono.once, sys_clock_gettime_mono_init);
|
||||
cycles = rdtsc() - g_mono.base_tick;
|
||||
nanos = cycles / 3;
|
||||
*time = timespec_add(g_mono.base_wall, timespec_fromnanos(nanos));
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
|
@ -30,10 +31,9 @@
|
|||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
static struct CopyFileRange {
|
||||
pthread_once_t once;
|
||||
_Atomic(uint32_t) once;
|
||||
bool ok;
|
||||
} g_copy_file_range;
|
||||
|
||||
|
@ -104,7 +104,7 @@ ssize_t copy_file_range(int infd, int64_t *opt_in_out_inoffset, int outfd,
|
|||
int64_t *opt_in_out_outoffset, size_t uptobytes,
|
||||
uint32_t flags) {
|
||||
ssize_t rc;
|
||||
pthread_once(&g_copy_file_range.once, copy_file_range_init);
|
||||
cosmo_once(&g_copy_file_range.once, copy_file_range_init);
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
|
||||
if (!g_copy_file_range.ok) {
|
||||
|
|
|
@ -30,6 +30,17 @@
|
|||
* The `O_CLOEXEC` flag shall be cleared from the resulting file
|
||||
* descriptor; see dup3() to preserve it.
|
||||
*
|
||||
* One use case for duplicating file descriptors is to be able to
|
||||
* reassign an open()'d file or pipe() to the stdio of an executed
|
||||
* subprocess. On Windows, in order for this to work, the subprocess
|
||||
* needs to be a Cosmopolitan program that has socket() linked.
|
||||
*
|
||||
* Only small programs should duplicate sockets. That's because this
|
||||
* implementation uses DuplicateHandle() on Windows, which Microsoft
|
||||
* says might cause its resources to leak internally. Thus it likely
|
||||
* isn't a good idea to design a server that does it a lot and lives
|
||||
* a long time, without contributing a patch to this implementation.
|
||||
*
|
||||
* @param fd remains open afterwards
|
||||
* @return some arbitrary new number for fd
|
||||
* @raise EPERM if pledge() is in play without stdio
|
||||
|
|
|
@ -27,13 +27,24 @@
|
|||
/**
|
||||
* Duplicates file descriptor, granting it specific number.
|
||||
*
|
||||
* The `O_CLOEXEC` flag shall be cleared from the resulting file
|
||||
* descriptor; see dup3() to preserve it.
|
||||
*
|
||||
* Unlike dup3(), the dup2() function permits oldfd and newfd to be the
|
||||
* same, in which case the only thing this function does is test if
|
||||
* oldfd is open.
|
||||
*
|
||||
* The `O_CLOEXEC` flag shall be cleared from the resulting file
|
||||
* descriptor; see dup3() to preserve it.
|
||||
*
|
||||
* One use case for duplicating file descriptors is to be able to
|
||||
* reassign an open()'d file or pipe() to the stdio of an executed
|
||||
* subprocess. On Windows, in order for this to work, the subprocess
|
||||
* needs to be a Cosmopolitan program that has socket() linked.
|
||||
*
|
||||
* Only small programs should duplicate sockets. That's because this
|
||||
* implementation uses DuplicateHandle() on Windows, which Microsoft
|
||||
* says might cause its resources to leak internally. Thus it likely
|
||||
* isn't a good idea to design a server that does it a lot and lives
|
||||
* a long time, without contributing a patch to this implementation.
|
||||
*
|
||||
* @param oldfd isn't closed afterwards
|
||||
* @param newfd if already assigned, is silently closed beforehand;
|
||||
* unless it's equal to oldfd, in which case dup2() is a no-op
|
||||
|
|
|
@ -19,18 +19,18 @@
|
|||
#include "libc/assert.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
#define F_DUP2FD 10
|
||||
#define F_DUP2FD_CLOEXEC 18
|
||||
|
||||
static struct Dup3 {
|
||||
pthread_once_t once;
|
||||
_Atomic(uint32_t) once;
|
||||
bool demodernize;
|
||||
} g_dup3;
|
||||
|
||||
|
@ -58,7 +58,7 @@ int32_t sys_dup3(int32_t oldfd, int32_t newfd, int flags) {
|
|||
return __sys_fcntl(oldfd, how, newfd);
|
||||
}
|
||||
|
||||
pthread_once(&g_dup3.once, sys_dup3_test);
|
||||
cosmo_once(&g_dup3.once, sys_dup3_test);
|
||||
|
||||
if (!g_dup3.demodernize) {
|
||||
return __sys_dup3(oldfd, newfd, flags);
|
||||
|
|
|
@ -28,9 +28,19 @@
|
|||
/**
|
||||
* Duplicates file descriptor/handle.
|
||||
*
|
||||
* On Windows, we can't guarantee the desired file descriptor is used.
|
||||
* We can however remap the standard handles (non-atomically) if their
|
||||
* symbolic names are used.
|
||||
* The `O_CLOEXEC` flag shall be cleared from the resulting file
|
||||
* descriptor; see dup3() to preserve it.
|
||||
*
|
||||
* One use case for duplicating file descriptors is to be able to
|
||||
* reassign an open()'d file or pipe() to the stdio of an executed
|
||||
* subprocess. On Windows, in order for this to work, the subprocess
|
||||
* needs to be a Cosmopolitan program that has socket() linked.
|
||||
*
|
||||
* Only small programs should duplicate sockets. That's because this
|
||||
* implementation uses DuplicateHandle() on Windows, which Microsoft
|
||||
* says might cause its resources to leak internally. Thus it likely
|
||||
* isn't a good idea to design a server that does it a lot and lives
|
||||
* a long time, without contributing a patch to this implementation.
|
||||
*
|
||||
* @param oldfd isn't closed afterwards
|
||||
* @param newfd if already assigned, is silently closed beforehand;
|
||||
|
|
|
@ -42,11 +42,6 @@ int execlp(const char *prog, const char *arg, ... /*, NULL*/) {
|
|||
va_list va, vb;
|
||||
char pathbuf[PATH_MAX];
|
||||
|
||||
// resolve path of executable
|
||||
if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// turn varargs into array
|
||||
va_copy(vb, va);
|
||||
va_start(va, arg);
|
||||
|
@ -60,6 +55,15 @@ int execlp(const char *prog, const char *arg, ... /*, NULL*/) {
|
|||
}
|
||||
va_end(vb);
|
||||
|
||||
if (strchr(prog, '/')) {
|
||||
return execv(prog, argv);
|
||||
}
|
||||
|
||||
// resolve path of executable
|
||||
if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// change argv[0] to resolved path if it's ambiguous
|
||||
// otherwise the program won't have much luck finding itself
|
||||
if (argv[0] && *prog != '/' && *exe == '/' && !strcmp(prog, argv[0])) {
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/ntspawn.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/nt/accounting.h"
|
||||
#include "libc/nt/console.h"
|
||||
|
@ -35,6 +37,7 @@
|
|||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
|
@ -84,22 +87,36 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
// execve operation is unrecoverable from this point
|
||||
|
||||
// close cloexec handles
|
||||
for (i = 3; i < g_fds.n; ++i) {
|
||||
if (g_fds.p[i].kind != kFdEmpty && (g_fds.p[i].flags & O_CLOEXEC)) {
|
||||
// close non-stdio and cloexec handles
|
||||
for (i = 0; i < g_fds.n; ++i) {
|
||||
if (g_fds.p[i].kind == kFdEmpty) {
|
||||
g_fds.p[i].handle = -1;
|
||||
} else if (i > 2 || (g_fds.p[i].flags & O_CLOEXEC)) {
|
||||
__imp_CloseHandle(g_fds.p[i].handle);
|
||||
g_fds.p[i].handle = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int bits;
|
||||
char buf[32], *v = 0;
|
||||
if (_weaken(socket)) {
|
||||
for (bits = i = 0; i < 3; ++i) {
|
||||
if (g_fds.p[i].kind == kFdSocket) {
|
||||
bits |= 1 << i;
|
||||
}
|
||||
}
|
||||
FormatInt32(stpcpy(buf, "__STDIO_SOCKETS="), bits);
|
||||
v = buf;
|
||||
}
|
||||
bzero(&startinfo, sizeof(startinfo));
|
||||
startinfo.cb = sizeof(struct NtStartupInfo);
|
||||
startinfo.dwFlags = kNtStartfUsestdhandles;
|
||||
startinfo.hStdInput = __getfdhandleactual(0);
|
||||
startinfo.hStdOutput = __getfdhandleactual(1);
|
||||
startinfo.hStdError = __getfdhandleactual(2);
|
||||
startinfo.hStdInput = g_fds.p[0].handle;
|
||||
startinfo.hStdOutput = g_fds.p[1].handle;
|
||||
startinfo.hStdError = g_fds.p[2].handle;
|
||||
|
||||
// spawn the process
|
||||
rc = ntspawn(program, argv, envp, 0, 0, 0, true, 0, 0, &startinfo, &procinfo);
|
||||
rc = ntspawn(program, argv, envp, v, 0, 0, true, 0, 0, &startinfo, &procinfo);
|
||||
if (rc == -1) {
|
||||
STRACE("panic: unrecoverable ntspawn(%#s) error: %m", program);
|
||||
__imp_ExitProcess(6543);
|
||||
|
|
|
@ -40,6 +40,11 @@
|
|||
* to be valid UTF-8 in order to round-trip the WIN32 API, without being
|
||||
* corrupted.
|
||||
*
|
||||
* On Windows, only file descriptors 0, 1 and 2 can be passed to a child
|
||||
* process in such a way that allows them to be automatically discovered
|
||||
* when the child process initializes. Cosmpolitan currently treats your
|
||||
* other file descriptors as implicitly O_CLOEXEC.
|
||||
*
|
||||
* @param program will not be PATH searched, see commandv()
|
||||
* @param argv[0] is the name of the program to run
|
||||
* @param argv[1,n-2] optionally specify program arguments
|
||||
|
|
|
@ -27,7 +27,9 @@
|
|||
/**
|
||||
* Executes program, with path environment search.
|
||||
*
|
||||
* The current process is replaced with the executed one.
|
||||
* This function is a wrapper of the execve() system call that does path
|
||||
* resolution. The `PATH` environment variable is taken from your global
|
||||
* `environ` rather than the `envp` argument.
|
||||
*
|
||||
* @param prog is the program to launch
|
||||
* @param argv is [file,argv₁..argvₙ₋₁,NULL]
|
||||
|
@ -47,6 +49,10 @@ int execvpe(const char *prog, char *const argv[], char *const *envp) {
|
|||
return efault();
|
||||
}
|
||||
|
||||
if (strchr(prog, '/')) {
|
||||
return execve(prog, argv, envp);
|
||||
}
|
||||
|
||||
// resolve path of executable
|
||||
if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) {
|
||||
return -1;
|
||||
|
|
|
@ -321,8 +321,9 @@ static textwindows int sys_fcntl_nt_setfl(int fd, unsigned *flags, unsigned arg,
|
|||
//
|
||||
// - O_NONBLOCK make read() raise EAGAIN
|
||||
// - O_NDELAY same thing as O_NONBLOCK
|
||||
// - O_ACCMODE but has a minimal effect
|
||||
//
|
||||
allowed = O_NONBLOCK;
|
||||
allowed = O_ACCMODE | O_NONBLOCK;
|
||||
if (changed & ~allowed) {
|
||||
// the following access mode flags are supported, but it's currently
|
||||
// not possible to change them on windows.
|
||||
|
|
|
@ -102,7 +102,7 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *st) {
|
|||
st->st_ctim = FileTimeToTimeSpec(wst.ftCreationFileTime);
|
||||
st->st_birthtim = st->st_ctim;
|
||||
st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow;
|
||||
st->st_blksize = PAGESIZE;
|
||||
st->st_blksize = 4096;
|
||||
st->st_dev = wst.dwVolumeSerialNumber;
|
||||
st->st_rdev = 0;
|
||||
st->st_ino = (uint64_t)wst.nFileIndexHigh << 32 | wst.nFileIndexLow;
|
||||
|
@ -118,7 +118,7 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *st) {
|
|||
&fci, sizeof(fci))) {
|
||||
actualsize = fci.CompressedFileSize;
|
||||
}
|
||||
st->st_blocks = ROUNDUP(actualsize, PAGESIZE) / 512;
|
||||
st->st_blocks = ROUNDUP(actualsize, 4096) / 512;
|
||||
}
|
||||
} else {
|
||||
STRACE("%s failed %m", "GetFileInformationByHandle");
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/metalfile.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
@ -34,7 +35,10 @@
|
|||
#define KERN_PROC_PATHNAME_FREEBSD 12
|
||||
#define KERN_PROC_PATHNAME_NETBSD 5
|
||||
|
||||
char program_executable_name[PATH_MAX];
|
||||
static struct ProgramExecutableName {
|
||||
_Atomic(uint32_t) once;
|
||||
char buf[PATH_MAX];
|
||||
} program_executable_name;
|
||||
|
||||
static inline int IsAlpha(int c) {
|
||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
||||
|
@ -50,7 +54,9 @@ static inline char *StrCat(char buf[PATH_MAX], const char *a, const char *b) {
|
|||
}
|
||||
|
||||
static inline void GetProgramExecutableNameImpl(char *p, char *e) {
|
||||
int c;
|
||||
char *q;
|
||||
char **ep;
|
||||
ssize_t rc;
|
||||
size_t i, n;
|
||||
union {
|
||||
|
@ -84,9 +90,9 @@ static inline void GetProgramExecutableNameImpl(char *p, char *e) {
|
|||
|
||||
// if argv[0] exists then turn it into an absolute path. we also try
|
||||
// adding a .com suffix since the ape auto-appends it when resolving
|
||||
if (__argc && (((q = __argv[0]) && !sys_faccessat(AT_FDCWD, q, F_OK, 0)) ||
|
||||
((q = StrCat(u.path, __argv[0], ".com")) &&
|
||||
!sys_faccessat(AT_FDCWD, q, F_OK, 0)))) {
|
||||
if (((q = __argv[0]) && !sys_faccessat(AT_FDCWD, q, F_OK, 0)) ||
|
||||
((q = StrCat(u.path, __argv[0], ".com")) &&
|
||||
!sys_faccessat(AT_FDCWD, q, F_OK, 0))) {
|
||||
if (*q != '/') {
|
||||
if (q[0] == '.' && q[1] == '/') {
|
||||
q += 2;
|
||||
|
@ -103,6 +109,19 @@ static inline void GetProgramExecutableNameImpl(char *p, char *e) {
|
|||
return;
|
||||
}
|
||||
|
||||
// if getenv("_") exists then use that
|
||||
for (ep = __envp; (q = *ep); ++ep) {
|
||||
if (*q++ == '_' && *q++ == '=') {
|
||||
while ((c = *q++)) {
|
||||
if (p + 1 < e) {
|
||||
*p++ = c;
|
||||
}
|
||||
}
|
||||
*p = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if argv[0] doesn't exist, then fallback to interpreter name
|
||||
if ((rc = sys_readlinkat(AT_FDCWD, "/proc/self/exe", p, e - p - 1)) > 0 ||
|
||||
(rc = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", p, e - p - 1)) > 0) {
|
||||
|
@ -125,28 +144,29 @@ static inline void GetProgramExecutableNameImpl(char *p, char *e) {
|
|||
}
|
||||
|
||||
// otherwise give up and just copy argv[0] into it
|
||||
if (!*p && __argv[0] && strlen(__argv[0]) < e - p) {
|
||||
strcpy(p, __argv[0]);
|
||||
if (!*p && (q = __argv[0])) {
|
||||
while ((c = *q++)) {
|
||||
if (p + 1 < e) {
|
||||
*p++ = c;
|
||||
}
|
||||
}
|
||||
*p = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void InitProgramExecutableName(void) {
|
||||
int e;
|
||||
e = errno;
|
||||
GetProgramExecutableNameImpl(
|
||||
program_executable_name.buf,
|
||||
program_executable_name.buf + sizeof(program_executable_name.buf));
|
||||
errno = e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns absolute path of program.
|
||||
*/
|
||||
char *GetProgramExecutableName(void) {
|
||||
int e;
|
||||
static bool once;
|
||||
if (!once) {
|
||||
e = errno;
|
||||
GetProgramExecutableNameImpl(
|
||||
program_executable_name,
|
||||
program_executable_name + sizeof(program_executable_name));
|
||||
errno = e;
|
||||
once = true;
|
||||
}
|
||||
return program_executable_name;
|
||||
cosmo_once(&program_executable_name.once, InitProgramExecutableName);
|
||||
return program_executable_name.buf;
|
||||
}
|
||||
|
||||
/* const void *const GetProgramExecutableNameCtor[] initarray = { */
|
||||
/* GetProgramExecutableName, */
|
||||
/* }; */
|
||||
|
|
|
@ -48,7 +48,7 @@ __attribute__((__constructor__)) static void kTmpPathInit(void) {
|
|||
char16_t path16[PATH_MAX];
|
||||
|
||||
if ((s = getenv("TMPDIR")) && (n = strlen(s)) < PATH_MAX / 2) {
|
||||
memcpy(kTmpPath, s, n);
|
||||
if (n) memcpy(kTmpPath, s, n);
|
||||
if (n && kTmpPath[n - 1] != '/') {
|
||||
kTmpPath[n + 0] = '/';
|
||||
kTmpPath[n + 1] = 0;
|
||||
|
|
80
libc/calls/mkstemp.c
Normal file
80
libc/calls/mkstemp.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 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/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/temp.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Creates temporary file name and file descriptor.
|
||||
*
|
||||
* The best way to construct your path template is:
|
||||
*
|
||||
* char path[PATH_MAX+1];
|
||||
* strlcat(path, kTmpDir, sizeof(path));
|
||||
* strlcat(path, "sauce.XXXXXX", sizeof(path));
|
||||
*
|
||||
* This usage pattern makes mkstemp() equivalent to tmpfd():
|
||||
*
|
||||
* int fd;
|
||||
* fd = mkstemp(path);
|
||||
* unlink(path);
|
||||
*
|
||||
* This usage pattern makes mkstemp() equivalent to mktemp():
|
||||
*
|
||||
* close(mkstemp(path));
|
||||
* puts(path);
|
||||
*
|
||||
* @param template is mutated to replace last six X's with rng
|
||||
* @return open file descriptor r + w exclusive or -1 w/ errno
|
||||
* @raise EINVAL if `template` didn't end with `XXXXXX`
|
||||
* @see tmpfd() if you don't need a path
|
||||
*/
|
||||
int mkstemp(char *template) {
|
||||
uint64_t w;
|
||||
int i, n, e, fd;
|
||||
if ((n = strlen(template)) < 6 ||
|
||||
READ16LE(template + n - 2) != READ16LE("XX") ||
|
||||
READ32LE(template + n - 6) != READ32LE("XXXX")) {
|
||||
return einval();
|
||||
}
|
||||
for (;;) {
|
||||
w = _rand64();
|
||||
for (i = 0; i < 6; ++i) {
|
||||
template[n - 6 + i] = "0123456789abcdefghijklmnopqrstuvwxyz"[w % 36];
|
||||
w /= 36;
|
||||
}
|
||||
e = errno;
|
||||
if ((fd = open(template,
|
||||
O_RDWR | O_CREAT | O_EXCL | (IsWindows() ? 0x00410000 : 0),
|
||||
0600)) != -1) {
|
||||
return fd;
|
||||
} else if (errno == EEXIST) {
|
||||
errno = e;
|
||||
} else {
|
||||
template[0] = 0;
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,28 +17,25 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/calls/struct/iovec.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/stdio/temp.h"
|
||||
|
||||
/**
|
||||
* Transfers memory to pipe.
|
||||
* Generates temporary filename.
|
||||
*
|
||||
* @param flags can have SPLICE_F_{MOVE,NONBLOCK,MORE,GIFT}
|
||||
* @return number of bytes actually transferred, or -1 w/ errno
|
||||
* char tmp[14] = "/tmp/hiXXXXXX";
|
||||
* puts(mkstemp(tmp));
|
||||
*
|
||||
* @param template is mutated to replace last six X's with rng
|
||||
* @raise EINVAL if `template` didn't end with `XXXXXX`
|
||||
* @return pointer to mutated `template`, or 0 w/ errno
|
||||
* @see mkstemp()
|
||||
*/
|
||||
ssize_t vmsplice(int fd, const struct iovec *chunks, int64_t count,
|
||||
uint32_t flags) {
|
||||
int olderr;
|
||||
ssize_t wrote;
|
||||
olderr = errno;
|
||||
if ((wrote = sys_vmsplice(fd, chunks, count, flags)) == -1) {
|
||||
errno = olderr;
|
||||
if (count) {
|
||||
wrote = write(fd, chunks[0].iov_base, chunks[0].iov_len);
|
||||
} else {
|
||||
wrote = write(fd, NULL, 0);
|
||||
}
|
||||
char *mktemp(char *template) {
|
||||
int fd;
|
||||
if ((fd = mkstemp(template)) != -1) {
|
||||
close(fd);
|
||||
return template;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return wrote;
|
||||
}
|
|
@ -132,6 +132,7 @@
|
|||
* @raise ENOTSUP if `file` is on zip file system and process is vfork()'d
|
||||
* @raise ENOSPC if file system is full when `file` would be `O_CREAT`ed
|
||||
* @raise EINTR if we needed to block and a signal was delivered instead
|
||||
* @raise EEXIST if `O_CREAT|O_EXCL` are used and `file` already existed
|
||||
* @raise ECANCELED if thread was cancelled in masked mode
|
||||
* @raise ENOENT if `file` doesn't exist when `O_CREAT` isn't in `flags`
|
||||
* @raise ENOENT if `file` points to a string that's empty
|
||||
|
|
|
@ -86,7 +86,7 @@ ssize_t read(int fd, void *buf, size_t size) {
|
|||
rc = ebadf();
|
||||
}
|
||||
END_CANCELLATION_POINT;
|
||||
DATATRACE("read(%d, [%#.*hhs%s], %'zu) → %'zd% m", fd, MAX(0, MIN(40, rc)),
|
||||
buf, rc > 40 ? "..." : "", size, rc);
|
||||
DATATRACE("read(%d, [%#.*hhs%s], %'zu) → %'zd% m", fd,
|
||||
(int)MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ ssize_t sys_readv_metal(struct Fd *fd, const struct iovec *iov, int iovlen) {
|
|||
file = (struct MetalFile *)fd->handle;
|
||||
for (toto = i = 0; i < iovlen && file->pos < file->size; ++i) {
|
||||
got = MIN(iov[i].iov_len, file->size - file->pos);
|
||||
memcpy(iov[i].iov_base, file->base, got);
|
||||
if (got) memcpy(iov[i].iov_base, file->base, got);
|
||||
toto += got;
|
||||
}
|
||||
return toto;
|
||||
|
|
|
@ -145,7 +145,7 @@ restart:
|
|||
l++;
|
||||
}
|
||||
if (q+l >= PATH_MAX) goto toolong;
|
||||
memcpy(output+q, stack+p, l);
|
||||
if (l) memcpy(output+q, stack+p, l);
|
||||
output[q+l] = 0;
|
||||
p += l;
|
||||
|
||||
|
@ -223,7 +223,7 @@ skip_readlink:
|
|||
if (q-p && !IsSlash(stack[l-1])) stack[l++] = '/';
|
||||
if (l + (q-p) + 1 >= PATH_MAX) goto toolong;
|
||||
memmove(output + l, output + p, q - p + 1);
|
||||
memcpy(output, stack, l);
|
||||
if (l) memcpy(output, stack, l);
|
||||
q = l + q-p;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
|
@ -28,10 +29,9 @@
|
|||
#include "libc/mem/alloca.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
static struct Splice {
|
||||
pthread_once_t once;
|
||||
_Atomic(uint32_t) once;
|
||||
bool ok;
|
||||
} g_splice;
|
||||
|
||||
|
@ -79,7 +79,7 @@ ssize_t splice(int infd, int64_t *opt_in_out_inoffset, int outfd,
|
|||
int64_t *opt_in_out_outoffset, size_t uptobytes,
|
||||
uint32_t flags) {
|
||||
ssize_t rc;
|
||||
pthread_once(&g_splice.once, splice_init);
|
||||
cosmo_once(&g_splice.once, splice_init);
|
||||
if (!g_splice.ok) {
|
||||
rc = enosys();
|
||||
} else if (IsAsan() && ((opt_in_out_inoffset &&
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nt/enum/accessmask.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
|
@ -31,12 +32,13 @@
|
|||
#include "libc/nt/struct/tokenprivileges.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
__msabi extern typeof(GetFileAttributes) *const __imp_GetFileAttributesW;
|
||||
|
||||
static _Bool g_winlink_allowed;
|
||||
static pthread_once_t g_winlink_once;
|
||||
static struct {
|
||||
_Atomic(uint32_t) once;
|
||||
_Bool allowed;
|
||||
} g_winlink;
|
||||
|
||||
static textwindows void InitializeWinlink(void) {
|
||||
int64_t tok;
|
||||
|
@ -48,7 +50,7 @@ static textwindows void InitializeWinlink(void) {
|
|||
tp.Privileges[0].Luid = id;
|
||||
tp.Privileges[0].Attributes = kNtSePrivilegeEnabled;
|
||||
if (!AdjustTokenPrivileges(tok, 0, &tp, sizeof(tp), 0, 0)) return;
|
||||
g_winlink_allowed = GetLastError() != kNtErrorNotAllAssigned;
|
||||
g_winlink.allowed = GetLastError() != kNtErrorNotAllAssigned;
|
||||
}
|
||||
|
||||
textwindows int sys_symlinkat_nt(const char *target, int newdirfd,
|
||||
|
@ -82,8 +84,8 @@ textwindows int sys_symlinkat_nt(const char *target, int newdirfd,
|
|||
|
||||
// windows only lets administrators do this
|
||||
// even then we're required to ask for permission
|
||||
pthread_once(&g_winlink_once, InitializeWinlink);
|
||||
if (!g_winlink_allowed) {
|
||||
cosmo_once(&g_winlink.once, InitializeWinlink);
|
||||
if (!g_winlink.allowed) {
|
||||
return eperm();
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
* @note Linux documentation says this call is "dangerous"; for highest
|
||||
* assurance of data recovery after crash, consider fsync() on both
|
||||
* file and directory
|
||||
* @see fsync(), fdatasync(), PAGESIZE
|
||||
* @see fsync(), fdatasync()
|
||||
*/
|
||||
int sync_file_range(int fd, int64_t offset, int64_t bytes, unsigned flags) {
|
||||
int rc, olderr;
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
│ 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/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/temp.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
@ -64,6 +64,7 @@
|
|||
* @return file descriptor on success, or -1 w/ errno
|
||||
* @raise ECANCELED if thread was cancelled in masked mode
|
||||
* @raise EINTR if signal was delivered
|
||||
* @see mkstemp() if you need a path
|
||||
* @see tmpfile() for stdio version
|
||||
* @cancellationpoint
|
||||
* @asyncsignalsafe
|
||||
|
@ -71,45 +72,23 @@
|
|||
* @vforksafe
|
||||
*/
|
||||
int tmpfd(void) {
|
||||
FILE *f;
|
||||
unsigned x;
|
||||
int fd, i, j, e;
|
||||
char path[PATH_MAX], *p;
|
||||
e = errno;
|
||||
if (IsLinux() && (fd = open(kTmpPath, O_RDWR | _O_TMPFILE, 0600)) != -1) {
|
||||
return fd;
|
||||
}
|
||||
errno = e;
|
||||
p = path;
|
||||
p = stpcpy(p, kTmpPath);
|
||||
p = stpcpy(p, "tmp.");
|
||||
if (program_invocation_short_name &&
|
||||
strlen(program_invocation_short_name) < 128) {
|
||||
p = stpcpy(p, program_invocation_short_name);
|
||||
*p++ = '.';
|
||||
}
|
||||
for (i = 0; i < 10; ++i) {
|
||||
x = _rand64();
|
||||
for (j = 0; j < 6; ++j) {
|
||||
p[j] = "0123456789abcdefghijklmnopqrstuvwxyz"[x % 36];
|
||||
x /= 36;
|
||||
}
|
||||
p[j] = 0;
|
||||
int e, fd;
|
||||
const char *prog;
|
||||
char path[PATH_MAX + 1];
|
||||
if (IsLinux()) {
|
||||
e = errno;
|
||||
if ((fd = open(path,
|
||||
O_RDWR | O_CREAT | O_EXCL | (IsWindows() ? _O_TMPFILE : 0),
|
||||
0600)) != -1) {
|
||||
if (!IsWindows()) {
|
||||
if (unlink(path)) {
|
||||
notpossible;
|
||||
}
|
||||
}
|
||||
if ((fd = open(kTmpPath, O_RDWR | _O_TMPFILE, 0600)) != -1) {
|
||||
return fd;
|
||||
} else if (errno == EEXIST) {
|
||||
errno = e;
|
||||
} else {
|
||||
break;
|
||||
errno = e;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
path[0] = 0;
|
||||
strlcat(path, kTmpPath, sizeof(path));
|
||||
if (!(prog = program_invocation_short_name)) prog = "tmp";
|
||||
strlcat(path, prog, sizeof(path));
|
||||
strlcat(path, ".XXXXXX", sizeof(path));
|
||||
if ((fd = mkstemp(path)) == -1) return -1;
|
||||
if (!IsWindows()) unassert(!unlink(path));
|
||||
return fd;
|
||||
}
|
||||
|
|
|
@ -17,15 +17,26 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sysparam.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/log/log.h"
|
||||
|
||||
static char ttyname_buf[PATH_MAX];
|
||||
#include "libc/paths.h"
|
||||
|
||||
/**
|
||||
* Returns name of terminal.
|
||||
*
|
||||
* This function isn't required to be thread safe, consider ttyname_r().
|
||||
*
|
||||
* @return terminal path on success, or null w/ errno
|
||||
* @see ttyname_r()
|
||||
*/
|
||||
char *ttyname(int fd) {
|
||||
int rc = ttyname_r(fd, ttyname_buf, sizeof(ttyname_buf));
|
||||
if (rc != 0) return NULL;
|
||||
return &ttyname_buf[0];
|
||||
errno_t err;
|
||||
static char buf[sizeof(_PATH_DEV) + MAXNAMLEN];
|
||||
if (!(err = ttyname_r(fd, buf, sizeof(buf)))) {
|
||||
return buf;
|
||||
} else {
|
||||
errno = err;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "libc/errno.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/fmt/magnumstrs.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/nt/console.h"
|
||||
|
@ -34,66 +35,79 @@
|
|||
|
||||
#define FIODGNAME 0x80106678 // freebsd
|
||||
|
||||
static textwindows dontinline int sys_ttyname_nt(int fd, char *buf,
|
||||
size_t size) {
|
||||
static textwindows errno_t sys_ttyname_nt(int fd, char *buf, size_t size) {
|
||||
uint32_t mode;
|
||||
const char *s;
|
||||
if (GetConsoleMode(g_fds.p[fd].handle, &mode)) {
|
||||
if (mode & kNtEnableVirtualTerminalInput) {
|
||||
strncpy(buf, "CONIN$", size);
|
||||
if (strlcpy(buf,
|
||||
(mode & kNtEnableVirtualTerminalInput) ? "CONIN$" : "CONOUT$",
|
||||
size) < size) {
|
||||
return 0;
|
||||
} else {
|
||||
strncpy(buf, "CONOUT$", size);
|
||||
return 0;
|
||||
return ERANGE;
|
||||
}
|
||||
} else {
|
||||
return enotty();
|
||||
return ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
static int ttyname_freebsd(int fd, char *buf, size_t size) {
|
||||
// clobbers errno
|
||||
static errno_t ttyname_freebsd(int fd, char *buf, size_t size) {
|
||||
struct fiodgname_arg {
|
||||
int len;
|
||||
void *buf;
|
||||
} fg;
|
||||
fg.buf = buf;
|
||||
fg.len = size;
|
||||
if (sys_ioctl(fd, FIODGNAME, &fg) != -1) return 0;
|
||||
return enotty();
|
||||
if (sys_ioctl(fd, FIODGNAME, &fg) != -1) {
|
||||
return 0;
|
||||
} else {
|
||||
return ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
static int ttyname_linux(int fd, char *buf, size_t size) {
|
||||
struct stat st1, st2;
|
||||
if (!isatty(fd)) return errno;
|
||||
char name[PATH_MAX];
|
||||
FormatInt32(stpcpy(name, "/proc/self/fd/"), fd);
|
||||
// clobbers errno
|
||||
static errno_t ttyname_linux(int fd, char *buf, size_t size) {
|
||||
ssize_t got;
|
||||
struct stat st1, st2;
|
||||
char name[14 + 12 + 1];
|
||||
if (!isatty(fd)) return errno;
|
||||
FormatInt32(stpcpy(name, "/proc/self/fd/"), fd);
|
||||
got = readlink(name, buf, size);
|
||||
if (got == -1) return errno;
|
||||
if ((size_t)got >= size) return erange();
|
||||
if (got >= size) return ERANGE;
|
||||
buf[got] = 0;
|
||||
if (stat(buf, &st1) || fstat(fd, &st2)) return errno;
|
||||
if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) return enodev();
|
||||
if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) return ENODEV;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of terminal, reentrantly.
|
||||
* Returns name of terminal.
|
||||
*
|
||||
* @return 0 on success, or error number on error
|
||||
* @raise ERANGE if `size` was too small
|
||||
* @returnserrno
|
||||
* @threadsafe
|
||||
*/
|
||||
int ttyname_r(int fd, char *buf, size_t size) {
|
||||
int rc;
|
||||
errno_t ttyname_r(int fd, char *buf, size_t size) {
|
||||
errno_t e, res;
|
||||
e = errno;
|
||||
if (IsLinux()) {
|
||||
rc = ttyname_linux(fd, buf, size);
|
||||
res = ttyname_linux(fd, buf, size);
|
||||
} else if (IsFreebsd()) {
|
||||
rc = ttyname_freebsd(fd, buf, size);
|
||||
res = ttyname_freebsd(fd, buf, size);
|
||||
} else if (IsWindows()) {
|
||||
if (__isfdkind(fd, kFdFile)) {
|
||||
rc = sys_ttyname_nt(fd, buf, size);
|
||||
if (__isfdopen(fd)) {
|
||||
res = sys_ttyname_nt(fd, buf, size);
|
||||
} else {
|
||||
rc = ebadf();
|
||||
res = EBADF;
|
||||
}
|
||||
} else {
|
||||
rc = enosys();
|
||||
res = ENOSYS;
|
||||
}
|
||||
STRACE("ttyname_r(%d, %s) → %d% m", fd, buf, rc);
|
||||
return rc;
|
||||
errno = e;
|
||||
STRACE("ttyname_r(%d, %#.*hhs) → %s", fd, (int)size, buf,
|
||||
!res ? "0" : _strerrno(res));
|
||||
return res;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue