Improve system call support

This commit is contained in:
Justine Tunney 2021-08-25 21:35:58 -07:00
parent 63b867bd2f
commit 3085ac7837
65 changed files with 900 additions and 544 deletions

View file

@ -87,6 +87,7 @@ int chdir(const char *);
int chmod(const char *, uint32_t);
int chown(const char *, uint32_t, uint32_t);
int chroot(const char *);
int clone(int (*)(void *), void *, int, void *, ...);
int close(int);
int closedir(DIR *);
int creat(const char *, uint32_t);

View file

@ -28,7 +28,7 @@
* @asyncsignalsafe
* @vforksafe
*/
nodiscard int dup(int fd) {
int dup(int fd) {
if (!IsWindows()) {
return sys_dup(fd);
} else {

View file

@ -16,6 +16,7 @@
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/ntspawn.h"
#include "libc/mem/alloca.h"
@ -44,6 +45,8 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
startinfo.hStdError = g_fds.p[2].handle;
rc = ntspawn(program, argv, envp, 0, 0, 0, 1, 0, 0, &startinfo, &procinfo);
if (rc == -1) return -1;
CloseHandle(g_fds.p[0].handle);
CloseHandle(g_fds.p[1].handle);
CloseHandle(procinfo.hThread);
do {
WaitForSingleObject(procinfo.hProcess, -1);

View file

@ -30,7 +30,8 @@
#include "libc/runtime/runtime.h"
#include "libc/sysv/errfuns.h"
textwindows int sys_fadvise_nt(int fd, uint64_t offset, uint64_t len, int advice) {
textwindows int sys_fadvise_nt(int fd, uint64_t offset, uint64_t len,
int advice) {
int64_t h2;
NtStatus status;
uint32_t sharemode;

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/stat.h"
@ -26,26 +27,44 @@
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
#include "libc/zipos/zipos.internal.h"
/**
* Returns true if file exists at path.
*
* This function is equivalent to:
*
* struct stat st;
* return stat(path, &st) != -1;
*
* Please note that things which aren't strictly files, e.g. directories
* or sockets, could be considered files for the purposes of this
* function. The stat() function may be used to differentiate them.
*/
bool fileexists(const char *path) {
int rc, olderr;
int e;
struct stat st;
struct ZiposUri zipname;
uint16_t path16[PATH_MAX];
if (IsAsan() && !__asan_is_valid(path, 1)) return efault();
if (!IsWindows()) {
olderr = errno;
rc = __sys_fstatat(AT_FDCWD, path, &st, 0);
if (rc == -1 && (errno == ENOENT || errno == ENOTDIR)) {
errno = olderr;
if (weaken(__zipos_open) && weaken(__zipos_parseuri)(path, &zipname) != -1) {
e = errno;
if (weaken(__zipos_stat)(&zipname, &st) != -1) {
return true;
} else {
errno = e;
return false;
}
} else if (IsMetal()) {
return false;
} else if (!IsWindows()) {
e = errno;
if (__sys_fstatat(AT_FDCWD, path, &st, 0) != -1) {
return true;
} else {
errno = e;
return false;
}
return rc != -1;
} else {
if (__mkntpath(path, path16) == -1) return -1;
return GetFileAttributes(path16) != -1u;

View file

@ -16,11 +16,12 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/safemacros.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/sysdebug.internal.h"
#include "libc/fmt/conv.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/bsr.h"
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/enum/fileinfobyhandleclass.h"
@ -37,12 +38,6 @@
#include "libc/sysv/consts/s.h"
#include "libc/sysv/errfuns.h"
#if 0
#define DEBUG(FMT, ...) (dprintf)(2, FMT "\n", ##__VA_ARGS__)
#else
#define DEBUG(FMT, ...) (void)0
#endif
static textwindows uint32_t GetSizeOfReparsePoint(int64_t fh) {
wint_t x, y;
const char16_t *p;
@ -69,7 +64,7 @@ static textwindows uint32_t GetSizeOfReparsePoint(int64_t fh) {
z += x < 0200 ? 1 : bsrl(tpenc(x)) >> 3;
}
} else {
DEBUG("GetSizeOfReparsePoint failed %d", GetLastError());
SYSDEBUG("GetSizeOfReparsePoint failed %d", GetLastError());
}
return z;
}
@ -122,8 +117,10 @@ 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, PAGESIZE) / 512;
}
} else {
SYSDEBUG("GetFileInformationByHandle failed %d", GetLastError());
}
break;
default:
@ -131,6 +128,7 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *st) {
}
return 0;
} else {
SYSDEBUG("GetFileType failed %d", GetLastError());
return __winerr();
}
}

View file

@ -109,9 +109,9 @@ char *sys_getcwd(char *, u64) hidden;
char *sys_getcwd_xnu(char *, u64) hidden;
i32 __sys_dup3(i32, i32, i32) hidden;
i32 __sys_execve(const char *, char *const[], char *const[]) hidden;
i32 __sys_fcntl(i32, i32, u64) hidden;
i32 __sys_fstat(i32, struct stat *) hidden;
i32 __sys_fstatat(i32, const char *, struct stat *, i32) hidden;
i32 __sys_fcntl(i32, i32, ...) hidden;
i32 __sys_fstat(i32, void *) hidden;
i32 __sys_fstatat(i32, const char *, void *, i32) hidden;
i32 __sys_getrusage(i32, struct rusage *) hidden;
i32 __sys_openat(i32, const char *, i32, u32) hidden;
i32 __sys_pipe2(i32[hasatleast 2], u32) hidden;

View file

@ -16,21 +16,56 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/metastat.internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/nt/enum/filetype.h"
#include "libc/nt/files.h"
#include "libc/sysv/errfuns.h"
#include "libc/zipos/zipos.internal.h"
/**
* Returns true if file descriptor is backed by character i/o.
*
* This function is equivalent to:
*
* struct stat st;
* return stat(path, &st) != -1 && S_ISCHR(st.st_mode);
*
* Except faster and with fewer dependencies.
*
* @see isregularfile(), isdirectory(), issymlink(), fileexists()
*/
textstartup bool32 ischardev(int fd) {
int olderr;
struct stat st;
olderr = errno;
if (fstat(fd, &st) != -1) {
return S_ISCHR(st.st_mode);
bool32 ischardev(int fd) {
int e;
union metastat st;
if (__isfdkind(fd, kFdZip)) {
e = errno;
if (weaken(__zipos_fstat)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, &st.linux) !=
-1) {
return S_ISCHR(st.linux.st_mode);
} else {
errno = e;
return false;
}
} else if (IsMetal()) {
return true;
} else if (!IsWindows()) {
e = errno;
if (__sys_fstat(fd, &st) != -1) {
return S_ISCHR(METASTAT(st, st_mode));
} else {
errno = e;
return false;
}
} else {
errno = olderr;
return false;
return __isfdkind(fd, kFdConsole) ||
(__isfdkind(fd, kFdFile) &&
GetFileType(g_fds.p[fd].handle) == kNtFileTypeChar);
}
}

View file

@ -16,8 +16,10 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/metastat.internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/errno.h"
@ -25,19 +27,44 @@
#include "libc/nt/files.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
#include "libc/zipos/zipos.internal.h"
/**
* Returns true if file exists and is a directory.
*
* This function is equivalent to:
*
* struct stat st;
* return fstatat(AT_FDCWD, path, &st, AT_SYMLINK_NOFOLLOW) != -1 &&
* S_ISDIR(st.st_mode);
*
* Except faster and with fewer dependencies.
*
* @see isregularfile(), issymlink(), ischardev()
*/
bool isdirectory(const char *path) {
struct stat st;
int rc, olderr;
int rc, e;
union metastat st;
struct ZiposUri zipname;
if (IsAsan() && !__asan_is_valid(path, 1)) return efault();
if (!IsWindows()) {
olderr = errno;
rc = sys_fstatat(AT_FDCWD, path, &st, AT_SYMLINK_NOFOLLOW);
if (rc == -1 && (errno == ENOENT || errno == ENOTDIR)) errno = olderr;
return rc != -1 && S_ISDIR(st.st_mode);
if (weaken(__zipos_open) && weaken(__zipos_parseuri)(path, &zipname) != -1) {
e = errno;
if (weaken(__zipos_stat)(&zipname, &st.linux) != -1) {
return S_ISDIR(st.linux.st_mode);
} else {
errno = e;
return false;
}
} else if (IsMetal()) {
return false;
} else if (!IsWindows()) {
e = errno;
if (__sys_fstatat(AT_FDCWD, path, &st, AT_SYMLINK_NOFOLLOW) != -1) {
return S_ISDIR(METASTAT(st, st_mode));
} else {
errno = e;
return false;
}
} else {
return isdirectory_nt(path);
}

View file

@ -16,29 +16,52 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/metastat.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
#include "libc/zipos/zipos.internal.h"
/**
* Returns true if file exists and is a regular file.
*
* This function is equivalent to:
*
* return fstatat(AT_FDCWD, path, &st, AT_SYMLINK_NOFOLLOW) != -1 &&
* S_ISREG(st.st_mode);
*
* Except faster and with fewer dependencies.
*
* @see isdirectory(), ischardev(), issymlink()
*/
bool isregularfile(const char *path) {
struct stat st;
int rc, olderr;
int rc, e;
union metastat st;
struct ZiposUri zipname;
if (IsAsan() && !__asan_is_valid(path, 1)) return efault();
if (!IsWindows()) {
olderr = errno;
rc = sys_fstatat(AT_FDCWD, path, &st, AT_SYMLINK_NOFOLLOW);
if (rc == -1 && (errno == ENOENT || errno == ENOTDIR)) {
errno = olderr;
if (weaken(__zipos_open) && weaken(__zipos_parseuri)(path, &zipname) != -1) {
e = errno;
if (weaken(__zipos_stat)(&zipname, &st.linux) != -1) {
return S_ISREG(st.linux.st_mode);
} else {
errno = e;
return false;
}
} else if (IsMetal()) {
return false;
} else if (!IsWindows()) {
e = errno;
if (__sys_fstatat(AT_FDCWD, path, &st, AT_SYMLINK_NOFOLLOW) != -1) {
return S_ISREG(METASTAT(st, st_mode));
} else {
errno = e;
return false;
}
return rc != -1 && S_ISREG(st.st_mode);
} else {
return isregularfile_nt(path);
}

View file

@ -16,8 +16,10 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/metastat.internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/errno.h"
@ -25,19 +27,38 @@
#include "libc/nt/files.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
#include "libc/zipos/zipos.internal.h"
/**
* Returns true if file exists and is a symbolic link.
*
* This function is equivalent to:
*
* struct stat st;
* return fstatat(AT_FDCWD, path, &st, AT_SYMLINK_NOFOLLOW) != -1 &&
* S_ISLNK(st.st_mode);
*
* Except faster and with fewer dependencies.
*
* @see isregularfile(), isdirectory(), fileexists(), ischardev()
*/
bool issymlink(const char *path) {
struct stat st;
int rc, olderr;
int e;
union metastat st;
struct ZiposUri zipname;
if (IsAsan() && !__asan_is_valid(path, 1)) return efault();
if (!IsWindows()) {
olderr = errno;
rc = sys_fstatat(AT_FDCWD, path, &st, AT_SYMLINK_NOFOLLOW);
if (rc == -1 && (errno == ENOENT || errno == ENOTDIR)) errno = olderr;
return rc != -1 && S_ISLNK(st.st_mode);
if (weaken(__zipos_open) && weaken(__zipos_parseuri)(path, &zipname) != -1) {
return false;
} else if (IsMetal()) {
return false;
} else if (!IsWindows()) {
e = errno;
if (__sys_fstatat(AT_FDCWD, path, &st, AT_SYMLINK_NOFOLLOW) != -1) {
return S_ISLNK(METASTAT(st, st_mode));
} else {
errno = e;
return false;
}
} else {
return issymlink_nt(path);
}

View file

@ -19,6 +19,7 @@
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sysdebug.internal.h"
#include "libc/mem/mem.h"
#include "libc/nt/enum/accessmask.h"
#include "libc/nt/enum/securityimpersonationlevel.h"
@ -34,12 +35,6 @@
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/errfuns.h"
#if 0
#define DEBUG(FMT, ...) (dprintf)(2, FMT "\n", ##__VA_ARGS__)
#else
#define DEBUG(FMT, ...) (void)0
#endif
/**
* Asks Microsoft if we're authorized to use a folder or file.
*
@ -92,24 +87,24 @@ TryAgain:
if (result || flags == F_OK) {
rc = 0;
} else {
DEBUG("ntaccesscheck finale failed %d %d", result, flags);
SYSDEBUG("ntaccesscheck finale failed %d %d", result, flags);
rc = eacces();
}
} else {
rc = __winerr();
DEBUG("AccessCheck failed: %m");
SYSDEBUG("AccessCheck failed: %m");
}
} else {
rc = __winerr();
DEBUG("DuplicateToken failed: %m");
SYSDEBUG("DuplicateToken failed: %m");
}
} else {
rc = __winerr();
DEBUG("OpenProcessToken failed: %m");
SYSDEBUG("OpenProcessToken failed: %m");
}
} else {
e = GetLastError();
DEBUG("GetFileSecurity failed: %d %d", e, secsize);
SYSDEBUG("GetFileSecurity failed: %d %d", e, secsize);
if (!IsTiny() && e == kNtErrorInsufficientBuffer) {
if (!freeme && weaken(malloc) && (freeme = weaken(malloc)(secsize))) {
s = freeme;

View file

@ -17,28 +17,45 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/o.h"
int sys_openat(int dirfd, const char *file, int flags, unsigned mode) {
int fd, err;
err = errno;
fd = __sys_openat(dirfd, file, flags, mode);
static bool once, modernize;
int d, e, f;
/*
* RHEL5 doesn't support O_CLOEXEC
* What on earth is it doing here?
* It returns -530!
* RHEL5 doesn't support O_CLOEXEC. It's hard to test for this.
* Sometimes the system call succeeds and it just doesn't set the
* flag. Other times, it return -530 which makes no sense.
*/
if (IsLinux() && fd == -1 && errno > 255) {
errno = err;
fd = __sys_openat(dirfd, file, flags & ~O_CLOEXEC, mode);
if (fd != -1 && (flags & O_CLOEXEC)) {
__sys_fcntl(fd, F_SETFD, FD_CLOEXEC);
if (!IsLinux() || !(flags & O_CLOEXEC) || modernize) {
return __sys_openat(dirfd, file, flags, mode);
} else if (once) {
if ((d = __sys_openat(dirfd, file, flags & ~O_CLOEXEC, mode)) != -1) {
e = errno;
if (__sys_fcntl(d, F_SETFD, FD_CLOEXEC) == -1) {
errno = e;
}
}
} else {
e = errno;
if ((d = __sys_openat(dirfd, file, flags, mode)) != -1) {
if ((f = __sys_fcntl(d, F_GETFD)) != -1) {
if (f & FD_CLOEXEC) {
modernize = true;
} else {
__sys_fcntl(d, F_SETFD, FD_CLOEXEC);
}
}
errno = e;
once = true;
} else if (errno > 255) {
once = true;
return sys_openat(dirfd, file, flags, mode);
}
}
return fd;
return d;
}

View file

@ -45,10 +45,6 @@ int openat(int dirfd, const char *file, int flags, ...) {
va_list va;
unsigned mode;
struct ZiposUri zipname;
if (strstr(file, "0/o/dbg/test")) {
(dprintf)(2, "-- wut %`'s\n", file);
if (weaken(__die)) weaken(__die)();
}
va_start(va, flags);
mode = va_arg(va, unsigned);
va_end(va);

View file

@ -20,6 +20,7 @@
#include "libc/calls/internal.h"
#include "libc/calls/struct/framebuffervirtualscreeninfo.h"
#include "libc/dce.h"
#include "libc/nt/enum/version.h"
#include "libc/nt/system.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/errfuns.h"
@ -70,9 +71,9 @@ int reboot(int howto) {
if (howto == 0xD000FCE2u) {
ok = !!SetSuspendState(0, 0, 0);
} else {
howto |= kNtShutdownForceOthers;
howto |= kNtShutdownForceSelf;
if (NtGetVersion() >= 8) {
howto |= kNtShutdownForceOthers;
if (NtGetVersion() >= kNtVersionWindows8) {
howto |= kNtShutdownHybrid;
}
if (immediately) {

View file

@ -5,6 +5,14 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define METASTAT(x, field) \
(IsLinux() || IsMetal() ? x.linux.field \
: IsXnu() ? x.xnu.field \
: IsFreebsd() ? x.freebsd.field \
: IsOpenbsd() ? x.openbsd.field \
: IsNetbsd() ? x.netbsd.field \
: 0)
struct stat_xnu {
int32_t st_dev;
uint16_t st_mode, st_nlink;

View file

@ -0,0 +1,11 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_SYSDEBUG_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_SYSDEBUG_INTERNAL_H_
#include "libc/calls/calls.h"
#if 0
#define SYSDEBUG(FMT, ...) (dprintf)(2, FMT "\n", ##__VA_ARGS__)
#else
#define SYSDEBUG(FMT, ...) (void)0
#endif
#endif /* COSMOPOLITAN_LIBC_CALLS_SYSDEBUG_INTERNAL_H_ */

View file

@ -16,9 +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/calls/internal.h"
#include "libc/calls/struct/rusage.h"
#include "libc/calls/sysdebug.internal.h"
#include "libc/fmt/conv.h"
#include "libc/macros.internal.h"
#include "libc/nt/accounting.h"
@ -62,12 +64,21 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
dwExitCode = kNtStillActive;
if (options & WNOHANG) {
i = WaitForMultipleObjects(count, handles, false, 0);
if (i == kNtWaitTimeout) return 0;
if (i == kNtWaitTimeout) {
return 0;
}
} else {
i = WaitForMultipleObjects(count, handles, false, -1);
}
if (i == kNtWaitFailed) return __winerr();
if (!GetExitCodeProcess(handles[i], &dwExitCode)) return __winerr();
if (i == kNtWaitFailed) {
SYSDEBUG("WaitForMultipleObjects failed %d", GetLastError());
return __winerr();
}
assert(__isfdkind(pids[i], kFdProcess));
if (!GetExitCodeProcess(handles[i], &dwExitCode)) {
SYSDEBUG("GetExitCodeProcess failed %d", GetLastError());
return __winerr();
}
if (dwExitCode == kNtStillActive) continue;
if (opt_out_wstatus) { /* @see WEXITSTATUS() */
*opt_out_wstatus = (dwExitCode & 0xff) << 8;
@ -76,14 +87,12 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
memset(opt_out_rusage, 0, sizeof(*opt_out_rusage));
if (GetProcessTimes(g_fds.p[pids[i]].handle, &createfiletime,
&exitfiletime, &kernelfiletime, &userfiletime)) {
opt_out_rusage->ru_utime.tv_sec =
ReadFileTime(userfiletime) / HECTONANOSECONDS;
opt_out_rusage->ru_utime.tv_usec =
ReadFileTime(userfiletime) % HECTONANOSECONDS;
opt_out_rusage->ru_stime.tv_sec =
ReadFileTime(kernelfiletime) / HECTONANOSECONDS;
opt_out_rusage->ru_stime.tv_usec =
ReadFileTime(kernelfiletime) % HECTONANOSECONDS;
opt_out_rusage->ru_utime =
WindowsDurationToTimeVal(ReadFileTime(userfiletime));
opt_out_rusage->ru_stime =
WindowsDurationToTimeVal(ReadFileTime(kernelfiletime));
} else {
SYSDEBUG("GetProcessTimes failed %d", GetLastError());
}
}
CloseHandle(g_fds.p[pids[i]].handle);

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/sysdebug.internal.h"
#include "libc/calls/ucontext.h"
#include "libc/nt/enum/exceptionhandleractions.h"
#include "libc/nt/enum/signal.h"
@ -29,6 +30,7 @@ textwindows unsigned __wincrash(struct NtExceptionPointers *ep) {
ucontext_t ctx;
struct siginfo si;
} g;
SYSDEBUG("__wincrash");
switch (ep->ExceptionRecord->ExceptionCode) {
case kNtSignalBreakpoint:
sig = SIGTRAP;

View file

@ -19,6 +19,13 @@
#include "libc/nt/struct/teb.h"
#include "libc/runtime/runtime.h"
/**
* Returns New Technology version, e.g.
*
* if (IsWindows() && NtGetVersion() >= kNtVersionWindows10) {...}
*
* This can only be called on Windows.
*/
textwindows noasan int NtGetVersion(void) {
return (NtGetPeb()->OSMajorVersion & 0xff) << 8 | NtGetPeb()->OSMinorVersion;
}

View file

@ -19,7 +19,9 @@
#include "libc/bits/safemacros.internal.h"
#include "libc/calls/calls.h"
#include "libc/fmt/fmt.h"
#include "libc/log/color.internal.h"
#include "libc/log/gdb.h"
#include "libc/log/internal.h"
#include "libc/log/log.h"
#include "libc/nexgen32e/stackframe.h"
#include "libc/nexgen32e/vendor.internal.h"
@ -32,8 +34,6 @@
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/w.h"
#define RESTORE_TTY "\e[?1000;1002;1015;1006l\e[?25h"
/**
* Launches GDB debugger GUI for current process.
*
@ -59,7 +59,7 @@ relegated int(attachdebugger)(intptr_t continuetoaddr) {
(ttyfd = open(_PATH_TTY, O_RDWR | O_CLOEXEC)) == -1) {
return -1;
}
write(ttyfd, RESTORE_TTY, strlen(RESTORE_TTY));
__restore_tty(ttyfd);
snprintf(pidstr, sizeof(pidstr), "%u", getpid());
layout = "layout asm";
if ((elf = FindDebugBinary())) {

View file

@ -17,9 +17,11 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/safemacros.internal.h"
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/log/color.internal.h"
#include "libc/log/log.h"
#include "libc/nt/enum/version.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
@ -47,7 +49,9 @@ bool cancolor(void) {
static bool once;
static bool result;
if (!once) {
result = !!strcmp(nulltoempty(getenv("DONTANSIMEBRO")), "1") &&
result = (!IsWindows() || NtGetVersion() >= kNtVersionWindows10 ||
!ischardev(1)) &&
!!strcmp(nulltoempty(getenv("DONTANSIMEBRO")), "1") &&
!!strcmp(nulltoempty(getenv("TERM")), "dumb");
once = true;
}

View file

@ -30,7 +30,7 @@
relegated wontreturn void __die(void) {
static bool once;
if (cmpxchg(&once, false, true)) {
__restore_tty();
__restore_tty(1);
if (IsDebuggerPresent(false)) DebugBreak();
ShowBacktrace(2, NULL);
}

View file

@ -14,7 +14,7 @@ extern struct sigaction g_oldcrashacts[8] hidden;
void __start_fatal(const char *, int) hidden;
void __start_fatal_ndebug(void) hidden;
void __oncrash(int, struct siginfo *, struct ucontext *) relegated;
void __restore_tty(void);
void __restore_tty(int);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -265,7 +265,7 @@ relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) {
: 0);
}
if (gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT)) return;
__restore_tty();
__restore_tty(1);
ShowCrashReport(err, STDERR_FILENO, sig, si, ctx);
exit(128 + sig);
unreachable;

View file

@ -18,6 +18,7 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/termios.h"
#include "libc/log/color.internal.h"
#include "libc/log/internal.h"
#include "libc/str/str.h"
@ -36,9 +37,9 @@ const void *const g_oldtermios_ctor[] initarray = {
g_oldtermios_init,
};
void __restore_tty(void) {
if (g_oldtermios.c_lflag && isatty(1)) {
write(1, ANSI_RESTORE, strlen(ANSI_RESTORE));
tcsetattr(1, TCSAFLUSH, &g_oldtermios);
void __restore_tty(int fd) {
if (g_oldtermios.c_lflag && isatty(fd) && cancolor()) {
write(fd, ANSI_RESTORE, strlen(ANSI_RESTORE));
tcsetattr(fd, TCSAFLUSH, &g_oldtermios);
}
}

View file

@ -32,7 +32,7 @@
relegated void __start_fatal(const char *file, int line) {
bool colorful;
char s[16 + 16 + 16 + 16 + PATH_MAX + 16 + NAME_MAX + 16], *p = s;
__restore_tty();
__restore_tty(1);
colorful = cancolor();
*p++ = '\r';
if (colorful) p = stpcpy(p, "\e[J\e[30;101m");

View file

@ -29,7 +29,7 @@
*/
relegated void __start_fatal_ndebug(void) {
char s[16 + 16 + 16 + 16 + PATH_MAX + 16], *p = s;
__restore_tty();
__restore_tty(1);
*p++ = '\r';
if (cancolor()) p = stpcpy(p, "\e[J");
p = stpcpy(p, "error:");

107
libc/runtime/clone.c Normal file
View file

@ -0,0 +1,107 @@
/*-*- 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 2021 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/dce.h"
#include "libc/nexgen32e/nt2sysv.h"
#include "libc/nexgen32e/stackframe.h"
#include "libc/nt/runtime.h"
#include "libc/nt/thread.h"
#include "libc/sysv/consts/clone.h"
#include "libc/sysv/consts/nr.h"
#include "libc/sysv/errfuns.h"
struct WinThread {
int (*func)(void *);
void *param;
void *stack;
};
static noasan textwindows uint32_t winthread(void *param) {
struct WinThread *wt = param;
asm volatile("mov\t%%rbp,%%r14\n\t"
"mov\t%%rsp,%%r15\n\t"
"xor\t%%ebp,%%ebp\n\t"
"mov\t%2,%%rsp\n\t"
"call\t*%0\n\t"
"mov\t%%r14,%%rbp\n\t"
"mov\t%%r15,%%rsp"
: /* no outputs */
: "m"(wt->func), "D"(wt->param), "m"(wt->stack)
: "r14", "r15", "memory");
return 0;
}
/**
* Creates thread.
*
* @note CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND creates thread
* @note CLONE_VFORK|CLONE_VM|SIGCHLD does vfork()
* @note SIGCHLD does fork()
*/
privileged int clone(int (*f)(void *), void *stack, int flags, void *arg, ...) {
int64_t h;
va_list va;
intptr_t ax;
uint32_t tid;
int32_t *ptid;
register void *tls asm("r8");
register int32_t *ctid asm("r10");
register int (*func)(void *) asm("r9");
if (IsLinux() || IsNetbsd()) {
va_start(va, arg);
ptid = va_arg(va, int32_t *);
tls = va_arg(va, void *);
ctid = va_arg(va, int32_t *);
va_end(va);
func = f;
stack = (void *)(((uintptr_t)stack & -16) - 8);
*(intptr_t *)stack = (intptr_t)arg;
asm volatile("syscall"
: "=a"(ax)
: "0"(__NR_clone), "D"(flags), "S"(stack), "d"(ptid),
"r"(ctid), "r"(tls), "r"(func)
: "rcx", "r11", "memory");
if (ax) return ax;
asm volatile("xor\t%%ebp,%%ebp\n\t"
"pop\t%%rdi\n\t"
"call\t%1"
: "=a"(ax)
: "r"(func)
: "memory");
asm volatile("syscall"
: /* no outputs */
: "a"(__NR_exit), "D"(ax)
: "memory");
unreachable;
} else if (IsWindows()) {
if (flags == (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)) {
if ((h = CreateThread(0, PAGESIZE, NT2SYSV(winthread),
&(struct WinThread){f, arg, stack}, 0, &tid))) {
CloseHandle(h);
return tid;
} else {
return __winerr();
}
} else {
return einval();
}
} else {
return enosys();
}
}

View file

@ -20,6 +20,7 @@
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sysdebug.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/macros.internal.h"
@ -90,7 +91,10 @@ void *mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) {
f = flags | MAP_FIXED;
if (IsOpenbsd() && (f & MAP_GROWSDOWN)) { /* openbsd:dubstack */
dm = sys_mmap(addr, size, prot, f & ~MAP_GROWSDOWN, fd, off);
if (dm.addr == MAP_FAILED) return MAP_FAILED;
if (dm.addr == MAP_FAILED) {
SYSDEBUG("sys_mmap failed");
return MAP_FAILED;
}
}
dm = sys_mmap(addr, size, prot, f, fd, off);
if (dm.addr == MAP_FAILED || dm.addr != addr) {

View file

@ -24,9 +24,12 @@
#include "libc/sock/internal.h"
#include "libc/sock/yoink.inc"
#include "libc/sysv/consts/fio.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/consts/so.h"
#include "libc/sysv/consts/sock.h"
#include "libc/sysv/consts/sol.h"
#include "libc/sysv/errfuns.h"
textwindows int sys_accept_nt(struct Fd *fd, void *addr, uint32_t *addrsize,
@ -38,33 +41,28 @@ textwindows int sys_accept_nt(struct Fd *fd, void *addr, uint32_t *addrsize,
for (;;) {
if (!WSAPoll(&(struct sys_pollfd_nt){fd->handle, POLLIN}, 1, 1000))
continue;
if ((client = __reservefd()) == -1) return -1;
if ((h = WSAAccept(fd->handle, addr, (int32_t *)addrsize, 0, 0)) != -1) {
oflags = 0;
if (flags & SOCK_CLOEXEC) oflags |= O_CLOEXEC;
if (flags & SOCK_NONBLOCK) oflags |= O_NONBLOCK;
if (flags & SOCK_NONBLOCK) {
if (__sys_ioctlsocket_nt(g_fds.p[client].handle, FIONBIO,
(uint32_t[]){1}) == -1) {
__winsockerr();
__sys_closesocket_nt(g_fds.p[client].handle);
__releasefd(client);
return -1;
if ((!(flags & SOCK_NONBLOCK) ||
__sys_ioctlsocket_nt(h, FIONBIO, (uint32_t[]){1}) != -1) &&
(sockfd2 = calloc(1, sizeof(struct SockFd)))) {
if ((client = __reservefd()) != -1) {
sockfd2->family = sockfd->family;
sockfd2->type = sockfd->type;
sockfd2->protocol = sockfd->protocol;
sockfd2->event = WSACreateEvent();
g_fds.p[client].kind = kFdSocket;
g_fds.p[client].flags = oflags;
g_fds.p[client].handle = h;
g_fds.p[client].extra = (uintptr_t)sockfd2;
return client;
}
free(sockfd2);
}
sockfd2 = calloc(1, sizeof(struct SockFd));
sockfd2->family = sockfd->family;
sockfd2->type = sockfd->type;
sockfd2->protocol = sockfd->protocol;
sockfd2->event = WSACreateEvent();
g_fds.p[client].kind = kFdSocket;
g_fds.p[client].flags = oflags;
g_fds.p[client].handle = h;
g_fds.p[client].extra = (uintptr_t)sockfd2;
return client;
} else {
__releasefd(client);
return __winsockerr();
__sys_closesocket_nt(h);
}
return __winsockerr();
}
}

View file

@ -26,24 +26,27 @@
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/errfuns.h"
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t timeoutms) {
int got;
size_t i;
uint64_t waitfor;
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t ms) {
int i, got, waitfor;
struct sys_pollfd_nt ntfds[64];
if (nfds > 64) return einval();
if (nfds >= ARRAYLEN(ntfds)) return einval();
for (i = 0; i < nfds; ++i) {
if (!__isfdkind(fds[i].fd, kFdSocket)) return ebadf();
ntfds[i].handle = g_fds.p[fds[i].fd].handle;
ntfds[i].events = fds[i].events & (POLLPRI | POLLIN | POLLOUT);
if (fds[i].fd >= 0) {
if (!__isfdkind(fds[i].fd, kFdSocket)) return enotsock();
ntfds[i].handle = g_fds.p[fds[i].fd].handle;
ntfds[i].events = fds[i].events & (POLLPRI | POLLIN | POLLOUT);
} else {
ntfds[i].handle = -1;
ntfds[i].events = POLLIN;
}
}
for (;;) {
if (cmpxchg(&__interrupted, true, false)) return eintr();
waitfor = MIN(1000, timeoutms); /* for ctrl+c */
waitfor = MIN(1000, ms); /* for ctrl+c */
if ((got = WSAPoll(ntfds, nfds, waitfor)) != -1) {
if (!got && (timeoutms -= waitfor)) continue;
if (!got && (ms -= waitfor) > 0) continue;
for (i = 0; i < nfds; ++i) {
fds[i].revents = ntfds[i].revents;
fds[i].revents = ntfds[i].handle < 0 ? 0 : ntfds[i].revents;
}
return got;
} else {

View file

@ -18,16 +18,21 @@
*/
#include "libc/calls/internal.h"
#include "libc/mem/mem.h"
#include "libc/nt/winsock.h"
#include "libc/nt/iphlpapi.h"
#include "libc/nt/winsock.h"
#include "libc/sock/internal.h"
#include "libc/sock/yoink.inc"
#include "libc/sysv/consts/fio.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/so.h"
#include "libc/sysv/consts/sock.h"
#include "libc/sysv/consts/sol.h"
/* ioctl(SIOCGIFCONFIG) for Windows need to access the following functions through
* weak reference. This ensure those symbols are not stripped during final link
/*
* ioctl(SIOCGIFCONFIG) for Windows need to access the following
* functions through weak reference. This ensure those symbols are not
* stripped during final link.
*/
STATIC_YOINK("GetAdaptersAddresses");
STATIC_YOINK("tprecode16to8");

View file

@ -23,7 +23,9 @@
#include "libc/sysv/errfuns.h"
/**
* Creates new system resource for network communication.
* Creates new system resource for network communication, e.g.
*
* int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
*
* @param family can be AF_UNIX, AF_INET, etc.
* @param type can be SOCK_STREAM (for TCP), SOCK_DGRAM (e.g. UDP), or

View file

@ -67,7 +67,7 @@ struct dirstream {
struct {
unsigned buf_pos;
unsigned buf_end;
uint64_t buf[BUFSIZ / 8];
uint64_t buf[(BUFSIZ + 256) / 8];
};
struct {
bool isdone;

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/sysdebug.internal.h"
#include "libc/mem/mem.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
@ -58,6 +59,7 @@ FILE *fopen(const char *pathname, const char *mode) {
FILE *f;
bool noclose;
int fd, flags;
SYSDEBUG("fopen(%`'s)", pathname);
flags = fopenflags(mode);
pathname = fixpathname(pathname, flags);
if ((fd = openpathname(pathname, flags, &noclose)) != -1) {

View file

@ -2057,7 +2057,7 @@ syscon nr __NR_execve 0x003b 0x200003b 0x003b 0x003b 0x03b 0xfff # D
syscon nr __NR_wait4 0x003d 0x2000007 0x0007 0x000b 0x1c1 0xfff
syscon nr __NR_kill 0x003e 0x2000025 0x0025 0x007a 0x025 0xfff
syscon nr __NR_killpg 0xfff 0xfff 0x0092 0xfff 0xfff 0xfff
syscon nr __NR_clone 0x0038 0xfff 0xfff 0xfff 0xfff 0xfff
syscon nr __NR_clone 0x0038 0xfff 0xfff 0xfff 0x11f 0xfff
syscon nr __NR_tkill 0x00c8 0xfff 0xfff 0xfff 0xfff 0xfff
syscon nr __NR_futex 0x00ca 0xfff 0xfff 0x0053 0xfff 0xfff
syscon nr __NR_set_robust_list 0x0111 0xfff 0xfff 0xfff 0xfff 0xfff

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon nr,__NR_clone,0x0038,0xfff,0xfff,0xfff,0xfff,0xfff
.syscon nr,__NR_clone,0x0038,0xfff,0xfff,0xfff,0x11f,0xfff

26
libc/sysv/consts/clone.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_CLONE_H_
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_CLONE_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define CLONE_VM 0x00000100
#define CLONE_FS 0x00000200
#define CLONE_FILES 0x00000400
#define CLONE_SIGHAND 0x00000800
#define CLONE_PTRACE 0x00002000
#define CLONE_VFORK 0x00004000
#define CLONE_PARENT 0x00008000
#define CLONE_THREAD 0x00010000
#define CLONE_NEWNS 0x00020000
#define CLONE_SYSVSEM 0x00040000
#define CLONE_SETTLS 0x00080000
#define CLONE_PARENT_SETTID 0x00100000
#define CLONE_CHILD_CLEARTID 0x00200000
#define CLONE_DETACHED 0x00400000
#define CLONE_UNTRACED 0x00800000
#define CLONE_CHILD_SETTID 0x01000000
#define CLONE_STOPPED 0x02000000
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_CLONE_H_ */

View file

@ -1,32 +1,32 @@
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_S_H_
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_S_H_
#define S_ISVTX 01000 /* THE STICKY BIT */
#define S_ISGID 02000 /* the setgid bit */
#define S_ISUID 04000 /* the setuid bit */
#define S_IXUSR 00100 /* user --x; just use octal */
#define S_IWUSR 00200 /* user -w-; just use octal */
#define S_IRUSR 00400 /* user r--; just use octal */
#define S_IRWXU 00700 /* user rwx; just use octal */
#define S_IXGRP 00010 /* group --x; just use octal */
#define S_IWGRP 00020 /* group -w-; just use octal */
#define S_IRGRP 00040 /* group r--; just use octal */
#define S_IRWXG 00070 /* group rwx; just use octal */
#define S_IXOTH 00001 /* other --x; just use octal */
#define S_IWOTH 00002 /* other -w-; just use octal */
#define S_IROTH 00004 /* other r--; just use octal */
#define S_IRWXO 00007 /* other rwx; just use octal */
#define S_IREAD 00400 /* just use octal */
#define S_IEXEC 00100 /* just use octal */
#define S_IWRITE 00200 /* just use octal */
#define S_IFIFO 0010000 /* pipe */
#define S_IFCHR 0020000 /* character device */
#define S_IFDIR 0040000 /* directory */
#define S_IFBLK 0060000 /* block device */
#define S_IFREG 0100000 /* regular file */
#define S_IFLNK 0120000 /* symbolic link */
#define S_IFSOCK 0140000 /* socket */
#define S_IFMT 0170000 /* mask of file types above */
#define S_IFIFO (1 << 12) /* pipe */
#define S_IFCHR (2 << 12) /* character device */
#define S_IFDIR (4 << 12) /* directory */
#define S_IFBLK (6 << 12) /* block device */
#define S_IFREG (8 << 12) /* regular file */
#define S_IFLNK (10 << 12) /* symbolic link */
#define S_IFSOCK (12 << 12) /* socket */
#define S_IFMT (15 << 12) /* mask of file types above */
#define S_ISVTX 0001000 /* THE STICKY BIT */
#define S_ISGID 0002000 /* the setgid bit */
#define S_ISUID 0004000 /* the setuid bit */
#define S_IXUSR 0000100 /* user --x; just use octal */
#define S_IWUSR 0000200 /* user -w-; just use octal */
#define S_IRUSR 0000400 /* user r--; just use octal */
#define S_IRWXU 0000700 /* user rwx; just use octal */
#define S_IXGRP 0000010 /* group --x; just use octal */
#define S_IWGRP 0000020 /* group -w-; just use octal */
#define S_IRGRP 0000040 /* group r--; just use octal */
#define S_IRWXG 0000070 /* group rwx; just use octal */
#define S_IXOTH 0000001 /* other --x; just use octal */
#define S_IWOTH 0000002 /* other -w-; just use octal */
#define S_IROTH 0000004 /* other r--; just use octal */
#define S_IRWXO 0000007 /* other rwx; just use octal */
#define S_IREAD 0000400 /* just use octal */
#define S_IEXEC 0000100 /* just use octal */
#define S_IWRITE 0000200 /* just use octal */
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_S_H_ */

View file

@ -16,6 +16,7 @@
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/struct/dirent.h"
#include "libc/calls/struct/stat.h"
@ -35,19 +36,18 @@ static int rmrfdir(const char *dirpath) {
while ((e = readdir(d))) {
if (!strcmp(e->d_name, ".")) continue;
if (!strcmp(e->d_name, "..")) continue;
if (strchr(e->d_name, '/')) abort();
assert(!strchr(e->d_name, '/'));
path = xjoinpaths(dirpath, e->d_name);
if (e->d_type == DT_DIR) {
rc = rmrfdir(path);
} else {
rc = unlink(path);
}
free(path);
if (rc == -1) {
free(path);
closedir(d);
return -1;
}
free(path);
}
rc = closedir(d);
rc |= rmdir(dirpath);

View file

@ -45,7 +45,7 @@ wchar_t *utf8toutf32(const char *p, size_t n, size_t *z) {
if (n == -1) n = p ? strlen(p) : 0;
if ((q = r = malloc(n * sizeof(wchar_t) + sizeof(wchar_t)))) {
for (i = 0; i < n;) {
if (0 && i + 16 < n) { /* 10x speedup for ascii */
if (i + 16 < n) { /* 10x speedup for ascii */
memset(vz, 0, 16);
do {
memcpy(v1, p + i, 16);

View file

@ -66,6 +66,8 @@ int rmrf(const char *);
int makedirs(const char *, unsigned);
char *xdirname(const char *) paramsnonnull() _XMAL;
char *xjoinpaths(const char *, const char *) paramsnonnull() _XMAL;
char *xreadlink(const char *) paramsnonnull() _XMAL;
char *xreadlinkat(int, const char *) paramsnonnull() _XMAL;
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § eXtended apis » time

30
libc/x/xreadlink.c Normal file
View file

@ -0,0 +1,30 @@
/*-*- 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 2021 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/sysv/consts/at.h"
#include "libc/x/x.h"
/**
* Reads symbolic link.
*
* @return nul-terminated string, or null w/ errno
* @see readlink()
*/
char *xreadlink(const char *path) {
return xreadlinkat(AT_FDCWD, path);
}

47
libc/x/xreadlinkat.c Normal file
View file

@ -0,0 +1,47 @@
/*-*- 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 2021 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/mem/mem.h"
#include "libc/sysv/errfuns.h"
#include "libc/x/x.h"
/**
* Reads symbolic link.
*
* @return nul-terminated string, or null w/ errno
* @see readlinkat()
*/
char *xreadlinkat(int dirfd, const char *path) {
ssize_t rc;
size_t n, c;
char *p, *q;
c = PAGESIZE;
p = xmalloc(c);
if ((rc = readlinkat(dirfd, path, p, c)) != -1) {
if ((n = rc) < c) {
p[n] = 0;
if ((q = realloc(p, n + 1))) p = q;
return p;
} else {
enametoolong();
}
}
free(p);
return 0;
}