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:
Justine Tunney 2023-07-29 18:44:15 -07:00
parent 140a8a52e5
commit 18bb5888e1
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
311 changed files with 1239 additions and 2622 deletions

View file

@ -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 \

View file

@ -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));

View file

@ -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) {

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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])) {

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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.

View file

@ -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");

View file

@ -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, */
/* }; */

View file

@ -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
View 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;
}
}
}

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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 &&

View file

@ -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();
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;
}