Make improvements

- Invent openatemp() API
- Invent O_UNLINK open flag
- Introduce getenv_secure() API
- Remove `git pull` from cosmocc
- Fix utimes() when path is NULL
- Fix mktemp() to never return NULL
- Fix utimensat() UTIME_OMIT on XNU
- Improve utimensat() code for RHEL5
- Turn `argv[0]` C:/ to /C/ on Windows
- Introduce tmpnam() and tmpnam_r() APIs
- Fix more const issues with internal APIs
- Permit utimes() on WIN32 in O_RDONLY mode
- Fix fdopendir() to check fd is a directory
- Fix recent crash regression in landlock make
- Fix futimens(AT_FDCWD, NULL) to return EBADF
- Use workaround so `make -j` doesn't fork bomb
- Rename dontdiscard to __wur (just like glibc)
- Fix st_size for WIN32 symlinks containing UTF-8
- Introduce stdio ext APIs needed by GNU coreutils
- Fix lstat() on WIN32 for symlinks to directories
- Move some constants from normalize.inc to limits.h
- Fix segv with memchr() and memcmp() overlapping page
- Implement POSIX fflush() behavior for reader streams
- Implement AT_SYMLINK_NOFOLLOW for utimensat() on WIN32
- Don't change read-only status of existing files on WIN32
- Correctly handle `0x[^[:xdigit:]]` case in strtol() functions
This commit is contained in:
Justine Tunney 2023-09-06 03:54:42 -07:00
parent 8596e83cce
commit f531acc8f9
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
297 changed files with 1920 additions and 1681 deletions

View file

@ -15,7 +15,7 @@
"alignas(x)",
"alignof(x)",
"artificial=",
"dontdiscard=",
"__wur=",
"mayalias=",
"forceinline=",
"forcealign(x)=",

View file

@ -181,10 +181,6 @@ fi
if [ "$1" = "--update" ]; then
cd $COSMO || exit
if GIT=$(command -v git); then
echo "running git pull on cosmo..." >&2
"$GIT" pull --quiet || exit
fi
echo "building cosmo host toolchain..." >&2
make --silent -j toolchain MODE= || exit
echo "building cosmo target (MODE=$MODE) toolchain..." >&2

View file

@ -150,10 +150,6 @@ fi
if [ "$1" = "--update" ]; then
cd $COSMO || exit
if GIT=$(command -v git); then
echo "running git pull on cosmo..." >&2
"$GIT" pull --quiet || exit
fi
echo "building cosmo host toolchain..." >&2
make --silent -j toolchain MODE= || exit
echo "building cosmo x86_64 target (MODE=$MODE) toolchain..." >&2

View file

@ -16,6 +16,7 @@
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/fmt/itoa.h"
#include "libc/limits.h"
#include "libc/log/appendresourcereport.internal.h"
#include "libc/log/internal.h"
#include "libc/log/log.h"

View file

@ -7,15 +7,16 @@
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/time/time.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/timespec.h"
#include "libc/fmt/itoa.h"
#include "libc/limits.h"
#include "libc/log/log.h"
#include "libc/math.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/time/time.h"
#include "libc/x/xspawn.h"
/**

View file

@ -26,11 +26,6 @@
#define _POSIX_MEMLOCK_RANGE _POSIX_VERSION
#define _POSIX_SPAWN _POSIX_VERSION
#define EOF -1 /* end of file */
#define WEOF -1u /* end of file (multibyte) */
#define _IOFBF 0 /* fully buffered */
#define _IOLBF 1 /* line buffered */
#define _IONBF 2 /* no buffering */
#define SEEK_SET 0 /* relative to beginning */
#define SEEK_CUR 1 /* relative to current position */
#define SEEK_END 2 /* relative to end */
@ -99,7 +94,6 @@ int execv(const char *, char *const[]);
int execve(const char *, char *const[], char *const[]);
int execvp(const char *, char *const[]);
int faccessat(int, const char *, int, int);
int fadvise(int, uint64_t, uint64_t, int);
int fchdir(int);
int fchmod(int, unsigned) dontthrow;
int fchmodat(int, const char *, unsigned, int);
@ -136,7 +130,6 @@ int mkdirat(int, const char *, unsigned);
int mkfifo(const char *, unsigned);
int mkfifoat(int, const char *, unsigned);
int mknod(const char *, unsigned, uint64_t);
int mknodat(int, const char *, int, uint64_t);
int nice(int);
int open(const char *, int, ...);
int openat(int, const char *, int, ...);
@ -205,7 +198,7 @@ int setresgid(unsigned, unsigned, unsigned);
int setresuid(unsigned, unsigned, unsigned);
int getresgid(unsigned *, unsigned *, unsigned *);
int getresuid(unsigned *, unsigned *, unsigned *);
char *get_current_dir_name(void) dontdiscard;
char *get_current_dir_name(void) __wur;
int sync_file_range(int, int64_t, int64_t, unsigned);
ssize_t splice(int, int64_t *, int, int64_t *, size_t, unsigned);
int memfd_create(const char *, unsigned int);
@ -245,6 +238,7 @@ int tmpfd(void);
int touch(const char *, unsigned);
int unveil(const char *, const char *);
long ptrace(int, ...);
int fadvise(int, uint64_t, uint64_t, int);
ssize_t copyfd(int, int, size_t);
ssize_t readansi(int, char *, size_t);
ssize_t tinyprint(int, const char *, ...) nullterminated();

View file

@ -33,7 +33,7 @@
#define _O_EXCL 0x00000080 // kNtCreateNew
#define _O_TRUNC 0x00000200 // kNtCreateAlways
#define _O_DIRECTORY 0x00010000 // kNtFileFlagBackupSemantics
#define _O_TMPFILE 0x00410000 // AttributeTemporary|FlagDeleteOnClose
#define _O_UNLINK 0x04000100 // kNtFileAttributeTemporary|DeleteOnClose
#define _O_DIRECT 0x00004000 // kNtFileFlagNoBuffering
#define _O_NONBLOCK 0x00000800 // kNtFileFlagWriteThrough (not sent to win32)
#define _O_RANDOM 0x80000000 // kNtFileFlagRandomAccess
@ -51,11 +51,18 @@ textwindows int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm,
if (flags &
~(O_ACCMODE | _O_APPEND | _O_CREAT | _O_EXCL | _O_TRUNC | _O_DIRECTORY |
_O_TMPFILE | _O_NONBLOCK | _O_RANDOM | _O_SEQUENTIAL | _O_COMPRESSED |
_O_UNLINK | _O_NONBLOCK | _O_RANDOM | _O_SEQUENTIAL | _O_COMPRESSED |
_O_INDEXED | _O_CLOEXEC | _O_DIRECT)) {
return einval();
}
// "Some of these flags should not be combined. For instance,
// combining kNtFileFlagRandomAccess with kNtFileFlagSequentialScan
// is self-defeating." -Quoth MSDN § CreateFileW
if ((flags & _O_SEQUENTIAL) && (flags & _O_RANDOM)) {
return einval();
}
switch (flags & O_ACCMODE) {
case O_RDONLY:
perm = kNtFileGenericRead;
@ -80,7 +87,7 @@ textwindows int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm,
}
attr = 0;
is_creating_file = (flags & _O_CREAT) || (flags & _O_TMPFILE) == _O_TMPFILE;
is_creating_file = !!(flags & _O_CREAT);
// POSIX O_EXEC doesn't mean the same thing as kNtGenericExecute. We
// request execute access when we can determine it from mode's bits.
@ -125,33 +132,64 @@ textwindows int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm,
disp = kNtOpenExisting;
}
// Please use tmpfd() or tmpfile() instead of O_TMPFILE.
if ((flags & _O_TMPFILE) == _O_TMPFILE) {
attr |= kNtFileAttributeTemporary | kNtFileFlagDeleteOnClose;
} else {
attr |= kNtFileAttributeNormal;
if (flags & _O_DIRECTORY) {
attr |= kNtFileFlagBackupSemantics;
}
}
// The Windows content indexing service ravages performance similar to
// Windows Defender. Please pass O_INDEXED to openat() to enable this.
if (~flags & _O_INDEXED) {
// The Windows content indexing service ravages performance similar to
// Windows Defender. Please pass O_INDEXED to openat() to enable this.
attr |= kNtFileAttributeNotContentIndexed;
}
// Windows' transparent filesystem compression is really cool, as such
// we've introduced a nonstandard O_COMPRESSED flag to help you use it
if (flags & _O_COMPRESSED) {
// Windows' transparent filesystem compression is really cool, as such
// we've introduced a nonstandard O_COMPRESSED flag to help you use it
attr |= kNtFileAttributeCompressed;
}
if (flags & kNtFileAttributeTemporary) { // subset of _O_UNLINK
// "Specifying the kNtFileAttributeTemporary attribute causes file
// systems to avoid writing data back to mass storage if sufficient
// cache memory is available, because an application deletes a
// temporary file after a handle is closed. In that case, the
// system can entirely avoid writing the data. Although it does not
// directly control data caching in the same way as the
// [kNtFileFlagNoBuffering, kNtFileFlagWriteThrough,
// kNtFileFlagSequentialScan, and kNtFileFlagRandomAccess] flags,
// the kNtFileAttributeTemporary attribute does tell the system to
// hold as much as possible in the system cache without writing and
// therefore may be of concern for certain applications." -MSDN
attr |= kNtFileAttributeTemporary;
}
if (!attr) {
// "All other file attributes override kNtFileAttributeNormal [...]
// [which] is valid only if used alone." -Quoth MSDN § CreateFileW
attr |= kNtFileAttributeNormal;
}
attr |= flags & kNtFileFlagDeleteOnClose; // subset of _O_UNLINK
if (flags & _O_DIRECTORY) {
// "You must set this flag to obtain a handle to a directory."
// -Quoth MSDN § CreateFileW
attr |= kNtFileFlagBackupSemantics;
}
// Not certain yet what benefit these flags offer.
if (flags & _O_SEQUENTIAL) attr |= kNtFileFlagSequentialScan;
if (flags & _O_RANDOM) attr |= kNtFileFlagRandomAccess;
if (flags & _O_DIRECT) attr |= kNtFileFlagNoBuffering;
// TODO(jart): Should we *always* open with write permission if the
// kernel will give it to us? We'd then deny write access
// in libc system call wrappers.
//
// "When an application creates a file across a network, it is better
// to use kNtGenericRead | kNtGenericWrite for dwDesiredAccess than
// to use kNtGenericWrite alone. The resulting code is faster,
// because the redirector can use the cache manager and send fewer
// SMBs with more data. This combination also avoids an issue where
// writing to a file across a network can occasionally return
// kNtErrorAccessDenied." -Quoth MSDN
if (out_perm) *out_perm = perm;
if (out_share) *out_share = share;
if (out_disp) *out_disp = disp;

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/limits.h"
#include "libc/mem/alloca.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"

View file

@ -26,6 +26,7 @@
#include "libc/errno.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/limits.h"
#include "libc/mem/alloca.h"
#include "libc/paths.h"
#include "libc/runtime/runtime.h"

View file

@ -19,6 +19,7 @@
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/limits.h"
#include "libc/mem/alloca.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"

View file

@ -33,6 +33,7 @@
#include "libc/intrin/safemacros.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/limits.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.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/internal.h"
#include "libc/calls/struct/stat.h"
@ -25,6 +26,7 @@
#include "libc/intrin/bsr.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
#include "libc/mem/alloca.h"
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/enum/fileinfobyhandleclass.h"
#include "libc/nt/enum/filetype.h"
@ -39,102 +41,119 @@
#include "libc/sysv/consts/s.h"
#include "libc/sysv/errfuns.h"
static textwindows uint32_t GetSizeOfReparsePoint(int64_t fh) {
wint_t x, y;
const char16_t *p;
uint32_t mem, i, n, z = 0;
struct NtReparseDataBuffer *rdb;
long buf[(sizeof(*rdb) + PATH_MAX * sizeof(char16_t)) / sizeof(long)];
mem = sizeof(buf);
rdb = (struct NtReparseDataBuffer *)buf;
if (DeviceIoControl(fh, kNtFsctlGetReparsePoint, 0, 0, rdb, mem, &n, 0)) {
i = 0;
n = rdb->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(char16_t);
p = (char16_t *)((char *)rdb->SymbolicLinkReparseBuffer.PathBuffer +
rdb->SymbolicLinkReparseBuffer.PrintNameOffset);
while (i < n) {
x = p[i++] & 0xffff;
if (!IsUcs2(x)) {
if (i < n) {
y = p[i++] & 0xffff;
x = MergeUtf16(x, y);
} else {
x = 0xfffd;
}
static textwindows long GetSizeOfReparsePoint(int64_t fh) {
uint32_t mem =
sizeof(struct NtReparseDataBuffer) + PATH_MAX * sizeof(char16_t);
void *buf = alloca(mem);
uint32_t dwBytesReturned;
struct NtReparseDataBuffer *rdb = (struct NtReparseDataBuffer *)buf;
if (!DeviceIoControl(fh, kNtFsctlGetReparsePoint, 0, 0, rdb, mem,
&dwBytesReturned, 0)) {
return -1;
}
const char16_t *p =
(const char16_t *)((char *)rdb->SymbolicLinkReparseBuffer.PathBuffer +
rdb->SymbolicLinkReparseBuffer.PrintNameOffset);
uint32_t n =
rdb->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(char16_t);
uint32_t i = 0;
uint32_t z = 0;
while (i < n) {
wint_t x = p[i++] & 0xffff;
if (!IsUcs2(x)) {
if (i < n) {
wint_t y = p[i++] & 0xffff;
x = MergeUtf16(x, y);
} else {
x = 0xfffd;
}
z += x < 0200 ? 1 : _bsrl(tpenc(x)) >> 3;
}
} else {
STRACE("%s failed %m", "GetSizeOfReparsePoint");
if (x >= 0200) {
z += _bsrl(tpenc(x)) >> 3;
}
++z;
}
return z;
}
textwindows int sys_fstat_nt(int64_t handle, struct stat *st) {
int filetype;
uint32_t umask;
uint64_t actualsize;
struct NtFileCompressionInfo fci;
struct NtByHandleFileInformation wst;
if (!st) return efault();
if ((filetype = GetFileType(handle))) {
bzero(st, sizeof(*st));
umask = atomic_load_explicit(&__umask, memory_order_acquire);
switch (filetype) {
case kNtFileTypeChar:
st->st_mode = S_IFCHR | (0666 & ~umask);
break;
case kNtFileTypePipe:
st->st_mode = S_IFIFO | (0666 & ~umask);
break;
case kNtFileTypeDisk:
if (GetFileInformationByHandle(handle, &wst)) {
st->st_mode = 0555 & ~umask;
st->st_flags = wst.dwFileAttributes;
if (!(wst.dwFileAttributes & kNtFileAttributeReadonly)) {
st->st_mode |= 0222 & ~umask;
}
if (wst.dwFileAttributes & kNtFileAttributeDirectory) {
st->st_mode |= S_IFDIR;
} else if (wst.dwFileAttributes & kNtFileAttributeReparsePoint) {
st->st_mode |= S_IFLNK;
} else {
st->st_mode |= S_IFREG;
}
st->st_atim = FileTimeToTimeSpec(wst.ftLastAccessFileTime);
st->st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime);
st->st_ctim = FileTimeToTimeSpec(wst.ftCreationFileTime);
st->st_birthtim = st->st_ctim;
st->st_gid = st->st_uid = __synthesize_uid();
st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow;
st->st_blksize = 4096;
st->st_dev = wst.dwVolumeSerialNumber;
st->st_rdev = 0;
st->st_ino = (uint64_t)wst.nFileIndexHigh << 32 | wst.nFileIndexLow;
st->st_nlink = wst.nNumberOfLinks;
if (S_ISLNK(st->st_mode)) {
if (!st->st_size) {
st->st_size = GetSizeOfReparsePoint(handle);
}
} else {
actualsize = st->st_size;
if (S_ISREG(st->st_mode) &&
GetFileInformationByHandleEx(handle, kNtFileCompressionInfo,
&fci, sizeof(fci))) {
actualsize = fci.CompressedFileSize;
}
st->st_blocks = ROUNDUP(actualsize, 4096) / 512;
}
} else {
STRACE("%s failed %m", "GetFileInformationByHandle");
textwindows int sys_fstat_nt(int64_t handle, struct stat *out_st) {
struct stat st = {0};
// Always set st_blksize to avoid divide by zero issues.
// The Linux kernel sets this for /dev/tty and similar too.
// TODO(jart): GetVolumeInformationByHandle?
st.st_blksize = 4096;
// We'll use the "umask" to fake out the mode bits.
uint32_t umask = atomic_load_explicit(&__umask, memory_order_acquire);
switch (GetFileType(handle)) {
case kNtFileTypeUnknown:
break;
case kNtFileTypeChar:
st.st_mode = S_IFCHR | (0666 & ~umask);
break;
case kNtFileTypePipe:
st.st_mode = S_IFIFO | (0666 & ~umask);
break;
case kNtFileTypeDisk: {
struct NtByHandleFileInformation wst;
if (!GetFileInformationByHandle(handle, &wst)) {
return __winerr();
}
st.st_mode = 0555 & ~umask;
st.st_flags = wst.dwFileAttributes;
if (!(wst.dwFileAttributes & kNtFileAttributeReadonly)) {
st.st_mode |= 0222 & ~umask;
}
if (wst.dwFileAttributes & kNtFileAttributeReparsePoint) {
st.st_mode |= S_IFLNK;
} else if (wst.dwFileAttributes & kNtFileAttributeDirectory) {
st.st_mode |= S_IFDIR;
} else {
st.st_mode |= S_IFREG;
}
st.st_atim = FileTimeToTimeSpec(wst.ftLastAccessFileTime);
st.st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime);
st.st_birthtim = FileTimeToTimeSpec(wst.ftCreationFileTime);
// compute time of last status change
if (timespec_cmp(st.st_atim, st.st_mtim) > 0) {
st.st_ctim = st.st_atim;
} else {
st.st_ctim = st.st_mtim;
}
st.st_gid = st.st_uid = __synthesize_uid();
st.st_size = (wst.nFileSizeHigh + 0ull) << 32 | wst.nFileSizeLow;
st.st_dev = wst.dwVolumeSerialNumber;
st.st_ino = (wst.nFileIndexHigh + 0ull) << 32 | wst.nFileIndexLow;
st.st_nlink = wst.nNumberOfLinks;
if (S_ISLNK(st.st_mode)) {
if (!st.st_size) {
long size = GetSizeOfReparsePoint(handle);
if (size == -1) return -1;
st.st_size = size;
}
break;
default:
break;
} else {
// st_size = uncompressed size
// st_blocks*512 = physical size
uint64_t physicalsize;
struct NtFileCompressionInfo fci;
if (!(wst.dwFileAttributes &
(kNtFileAttributeDirectory | kNtFileAttributeReparsePoint)) &&
GetFileInformationByHandleEx(handle, kNtFileCompressionInfo, &fci,
sizeof(fci))) {
physicalsize = fci.CompressedFileSize;
} else {
physicalsize = st.st_size;
}
st.st_blocks = ROUNDUP(physicalsize, st.st_blksize) / 512;
}
break;
}
return 0;
} else {
STRACE("%s failed %m", "GetFileType");
return __winerr();
default:
__builtin_unreachable();
}
memcpy(out_st, &st, sizeof(st));
return 0;
}

View file

@ -17,16 +17,9 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/metastat.internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/stat.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/sysv/errfuns.h"
/**
* Forms fstat() on System Five.
* @asyncsignalsafe
*/
int32_t sys_fstat(int32_t fd, struct stat *st) {
void *p;
union metastat ms;

View file

@ -34,7 +34,9 @@ textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st,
uint16_t path16[PATH_MAX];
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
if ((fh = CreateFile(
path16, kNtFileReadAttributes, 0, 0, kNtOpenExisting,
path16, kNtFileReadAttributes,
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, 0,
kNtOpenExisting,
kNtFileAttributeNormal | kNtFileFlagBackupSemantics |
((flags & AT_SYMLINK_NOFOLLOW) ? kNtFileFlagOpenReparsePoint
: 0),

View file

@ -17,10 +17,8 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/metastat.internal.h"
#include "libc/calls/struct/stat.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/sysv/errfuns.h"
/**
* Performs fstatat() on System Five.
@ -30,7 +28,6 @@ int32_t sys_fstatat(int32_t dirfd, const char *path, struct stat *st,
int32_t flags) {
void *p;
union metastat ms;
if (IsAsan() && !__asan_is_valid_str(path)) return efault();
if (st) {
p = &ms;
} else {

View file

@ -44,7 +44,11 @@
*/
int futimens(int fd, const struct timespec ts[2]) {
int rc;
rc = __utimens(fd, 0, ts, 0);
if (fd < 0) {
rc = ebadf(); // so we don't confuse __utimens if caller passes AT_FDCWD
} else {
rc = __utimens(fd, 0, ts, 0);
}
STRACE("futimens(%d, {%s, %s}) → %d% m", fd, DescribeTimespec(0, ts),
DescribeTimespec(0, ts ? ts + 1 : 0), rc);
return rc;

View file

@ -42,19 +42,16 @@
int futimes(int fd, const struct timeval tv[2]) {
int rc;
struct timespec ts[2];
if (tv) {
ts[0].tv_sec = tv[0].tv_sec;
ts[0].tv_nsec = tv[0].tv_usec * 1000;
ts[1].tv_sec = tv[1].tv_sec;
ts[1].tv_nsec = tv[1].tv_usec * 1000;
if (fd < 0) {
rc = ebadf(); // so we don't confuse __utimens if caller passes AT_FDCWD
} else if (tv) {
ts[0] = timeval_totimespec(tv[0]);
ts[1] = timeval_totimespec(tv[1]);
rc = __utimens(fd, 0, ts, 0);
} else {
rc = __utimens(fd, 0, 0, 0);
}
STRACE("futimes(%d, {%s, %s}) → %d% m", fd, DescribeTimeval(0, tv),
DescribeTimeval(0, tv ? tv + 1 : 0), rc);
return rc;
}

View file

@ -24,6 +24,7 @@
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/limits.h"
#include "libc/log/backtrace.internal.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"

View file

@ -22,6 +22,7 @@
#include "libc/cosmo.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/runtime.h"

View file

@ -16,21 +16,46 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/atomic.h"
#include "libc/cosmo.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/pr.h"
bool __is_linux_2_6_23(void) {
#ifdef __x86_64__
static struct {
atomic_uint once;
bool res;
} __is_linux_2_6_23_data;
static bool __is_linux_2_6_23_impl(void) {
int rc;
if (!IsLinux()) return false;
if (IsGenuineBlink()) return true;
asm volatile("syscall"
: "=a"(rc)
: "0"(157), "D"(PR_GET_SECCOMP)
: "rcx", "r11", "memory");
return rc != -EINVAL;
}
static void __is_linux_2_6_23_init(void) {
__is_linux_2_6_23_data.res = __is_linux_2_6_23_impl();
}
#endif /* x86 */
/**
* Returns true if we're running on non-ancient Linux.
* @note this function must only be called on Linux
*/
bool __is_linux_2_6_23(void) {
unassert(IsLinux()); // should be checked by caller
#ifdef __x86_64__
cosmo_once(&__is_linux_2_6_23_data.once, __is_linux_2_6_23_init);
return __is_linux_2_6_23_data.res;
#else
return true;
#endif

View file

@ -37,7 +37,7 @@ int issetugid(void) {
int rc;
if (IsLinux()) {
rc = !!__getauxval(AT_SECURE).value;
} else if (IsMetal()) {
} else if (IsMetal() || IsWindows()) {
rc = 0;
} else {
rc = sys_issetugid();

View file

@ -18,6 +18,7 @@
*/
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/nt/systeminfo.h"
#include "libc/runtime/runtime.h"

View file

@ -27,10 +27,8 @@
int lutimes(const char *filename, const struct timeval tv[2]) {
struct timespec ts[2];
if (tv) {
ts[0].tv_sec = tv[0].tv_sec;
ts[0].tv_nsec = tv[0].tv_usec * 1000;
ts[1].tv_sec = tv[1].tv_sec;
ts[1].tv_nsec = tv[1].tv_usec * 1000;
ts[0] = timeval_totimespec(tv[0]);
ts[1] = timeval_totimespec(tv[1]);
return utimensat(AT_FDCWD, filename, ts, AT_SYMLINK_NOFOLLOW);
} else {
return utimensat(AT_FDCWD, filename, 0, AT_SYMLINK_NOFOLLOW);

View file

@ -19,6 +19,7 @@
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/errno.h"
#include "libc/limits.h"
#include "libc/str/path.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/s.h"

View file

@ -18,47 +18,45 @@
*/
#include "libc/calls/calls.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/errfuns.h"
#include "libc/temp.h"
/**
* Creates temporary directory, e.g.
*
* char path[PATH_MAX];
* snprintf(path, sizeof(path), "%s%s.XXXXXX",
* kTmpPath, program_invocation_short_name);
* printf("%s\n", mkdtemp(path));
* char path[] = "/tmp/foo.XXXXXX";
* mkdtemp(path);
* rmdir(path);
*
* @param template must end with XXXXXX which is replaced with
* nondeterministic base36 random data
* @param template must end with XXXXXX which will be replaced
* with random text on success (and not modified on error)
* @return pointer to template on success, or NULL w/ errno
* @raise EINVAL if template didn't end with XXXXXX
*/
char *mkdtemp(char *template) {
unsigned x;
int i, j, n;
if ((n = strlen(template)) >= 6 && !memcmp(template + n - 6, "XXXXXX", 6)) {
for (i = 0; i < 10; ++i) {
x = _rand64();
for (j = 0; j < 6; ++j) {
template[n - 6 + j] = "0123456789abcdefghijklmnopqrstuvwxyz"[x % 36];
x /= 36;
}
if (!mkdir(template, 0700)) {
return template;
}
if (errno != EEXIST) {
break;
}
}
for (j = 0; j < 6; ++j) {
template[n - 6 + j] = 'X';
}
} else {
int n;
if ((n = strlen(template)) < 6 ||
READ32LE(template + n - 6) != READ32LE("XXXX") ||
READ16LE(template + n - 6 + 4) != READ16LE("XX")) {
einval();
return 0;
}
for (;;) {
int x = _rand64();
for (int i = 0; i < 6; ++i) {
template[n - 6 + i] = "0123456789abcdefghikmnpqrstvwxyz"[x & 31];
x >>= 5;
}
int e = errno;
if (!mkdir(template, 0700)) {
return template;
} else if (errno != EEXIST) {
memcpy(template + n - 6, "XXXXXX", 6);
return 0;
}
errno = e;
}
return 0;
}

42
libc/calls/mkostemp.c Normal file
View file

@ -0,0 +1,42 @@
/*-*- 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/temp.h"
#include "libc/sysv/consts/at.h"
/**
* Creates temporary file name and file descriptor, e.g.
*
* char path[] = "/tmp/foo.XXXXXX";
* int fd = mkostemp(path, O_CLOEXEC);
* printf("%s is opened as %d\n", path, fd);
*
* @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 openatemp() for one temp roller to rule them all
* @see mkostemps() if you need a suffix
* @see mkstemp() if you don't need flags
* @see mktemp() if you don't need an fd
* @see tmpfd() if you don't need a path
*/
int mkostemp(char *template, unsigned flags) {
return openatemp(AT_FDCWD, template, 0, flags, 0);
}
__strong_reference(mkostemp, mkostemp64);

43
libc/calls/mkostemps.c Normal file
View file

@ -0,0 +1,43 @@
/*-*- 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/temp.h"
#include "libc/sysv/consts/at.h"
/**
* Creates temporary file name and file descriptor, e.g.
*
* char path[] = "/tmp/foo.XXXXXX";
* int fd = mkostemp(path, O_CLOEXEC);
* printf("%s is opened as %d\n", path, fd);
*
* @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 openatemp() for one temp roller to rule them all
* @see mkstemp() if you don't need suffix/flags
* @see mkstemps() if you don't need flags
* @see mkostemp() if you don't need suffix
* @see mktemp() if you don't need an fd
* @see tmpfd() if you don't need a path
*/
int mkostemps(char *template, int suffixlen, unsigned flags) {
return openatemp(AT_FDCWD, template, suffixlen, flags, 0);
}
__strong_reference(mkostemps, mkostemps64);

View file

@ -16,67 +16,27 @@
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"
int _mkstemp(char *template, int oflags) {
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 | oflags, 0600)) != -1) {
return fd;
} else if (errno == EEXIST) {
errno = e;
} else {
template[0] = 0;
return fd;
}
}
}
#include "libc/temp.h"
#include "libc/sysv/consts/at.h"
/**
* Creates temporary file name and file descriptor.
* Creates temporary file name and file descriptor, e.g.
*
* 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);
* char path[] = "/tmp/foo.XXXXXX";
* int fd = mkstemp(path);
* printf("%s is opened as %d\n", path, fd);
*
* @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 openatemp() for one temp roller to rule them all
* @see mkostemp() if you you need a `O_CLOEXEC`, `O_APPEND`, etc.
* @see mkstemps() if you you need a suffix
* @see mktemp() if you don't need an fd
* @see tmpfd() if you don't need a path
*/
int mkstemp(char *template) {
return _mkstemp(template, 0);
return openatemp(AT_FDCWD, template, 0, 0, 0);
}
__strong_reference(mkstemp, mkstemp64);

44
libc/calls/mkstemps.c Normal file
View file

@ -0,0 +1,44 @@
/*-*- 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/temp.h"
#include "libc/sysv/consts/at.h"
/**
* Creates temporary file name and file descriptor, e.g.
*
* char path[] = "/tmp/foo.XXXXXX.txt";
* int fd = mkstemps(path, 4);
* printf("%s is opened as %d\n", path, fd);
*
* @param template is mutated to replace last six X's with rng
* @param suffixlen may be nonzero to permit characters after the "XXXXXX"
* @return open file descriptor r + w exclusive or -1 w/ errno
* @raise EINVAL if `template` (less the `suffixlen` region) didn't
* end with the string "XXXXXXX"
* @see mkostemp() if you you need a `O_CLOEXEC`, `O_APPEND`, etc.
* @see openatemp() for one temp roller to rule them all
* @see mkstemp() if you don't need `suffixlen`
* @see mktemp() if you don't need an fd
* @see tmpfd() if you don't need a path
*/
int mkstemps(char *template, int suffixlen) {
return openatemp(AT_FDCWD, template, suffixlen, 0, 0);
}
__strong_reference(mkstemps, mkstemps64);

View file

@ -16,8 +16,10 @@
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/stdio/temp.h"
#include "libc/sysv/consts/at.h"
#include "libc/temp.h"
/**
* Generates temporary filename.
@ -27,15 +29,20 @@
*
* @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()
* @return always `template` which on success will have its XXXXXX bytes
* mutated to a random value, otherwise on error it'll hold an empty
* string and `errno` will be set
* @see mkstemp() if you you need a file descriptor
* @see mkstemps() if you you need a file extension
* @see openatemp() for one temp roller to rule them all
* @see mkostemp() if you you need a `O_CLOEXEC`, `O_APPEND`, etc.
*/
char *mktemp(char *template) {
int fd;
if ((fd = mkstemp(template)) != -1) {
close(fd);
return template;
if ((fd = openatemp(AT_FDCWD, template, 0, 0, 0)) != -1) {
unassert(!close(fd));
} else {
return 0;
*template = 0;
}
return template;
}

View file

@ -66,9 +66,11 @@ struct SpawnBlock {
*/
textwindows int ntspawn(
const char *prog, char *const argv[], char *const envp[],
const char *extravar, struct NtSecurityAttributes *opt_lpProcessAttributes,
struct NtSecurityAttributes *opt_lpThreadAttributes, bool32 bInheritHandles,
uint32_t dwCreationFlags, const char16_t *opt_lpCurrentDirectory,
const char *extravar,
const struct NtSecurityAttributes *opt_lpProcessAttributes,
const struct NtSecurityAttributes *opt_lpThreadAttributes,
bool32 bInheritHandles, uint32_t dwCreationFlags,
const char16_t *opt_lpCurrentDirectory,
const struct NtStartupInfo *lpStartupInfo,
struct NtProcessInformation *opt_out_lpProcessInformation) {
int rc;

View file

@ -1,5 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_NTSPAWN_H_
#define COSMOPOLITAN_LIBC_CALLS_NTSPAWN_H_
#include "libc/limits.h"
#include "libc/nt/struct/processinformation.h"
#include "libc/nt/struct/securityattributes.h"
#include "libc/nt/struct/startupinfo.h"
@ -10,8 +11,9 @@ int mkntcmdline(char16_t[ARG_MAX / 2], char *const[]);
int mkntenvblock(char16_t[ARG_MAX / 2], char *const[], const char *,
char[ARG_MAX]);
int ntspawn(const char *, char *const[], char *const[], const char *,
struct NtSecurityAttributes *, struct NtSecurityAttributes *,
bool32, uint32_t, const char16_t *, const struct NtStartupInfo *,
const struct NtSecurityAttributes *,
const struct NtSecurityAttributes *, bool32, uint32_t,
const char16_t *, const struct NtStartupInfo *,
struct NtProcessInformation *);
COSMOPOLITAN_C_END_

View file

@ -23,14 +23,17 @@
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/macros.internal.h"
#include "libc/nt/createfile.h"
#include "libc/nt/enum/accessmask.h"
#include "libc/nt/enum/creationdisposition.h"
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/enum/filesharemode.h"
#include "libc/nt/enum/filetype.h"
#include "libc/nt/files.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/fileno.h"
@ -98,6 +101,16 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path,
return kNtInvalidHandleValue;
}
if (fattr != -1u) {
// "We have been asked to create a read-only file. "If the file
// already exists, the semantics of the Unix open system call is to
// preserve the existing permissions. If we pass CREATE_ALWAYS and
// FILE_ATTRIBUTE_READONLY to CreateFile, and the file already
// exists, CreateFile will change the file permissions. Avoid that to
// preserve the Unix semantics." -Quoth GoLang syscall_windows.go
attr &= ~kNtFileAttributeReadonly;
}
// kNtTruncateExisting always returns kNtErrorInvalidParameter :'(
if (disp == kNtTruncateExisting) {
if (fattr != -1u) {
@ -107,10 +120,24 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path,
}
}
// We optimistically request some write permissions in O_RDONLY mode.
// But that might prevent opening some files. So reactively back off.
int extra_perm = 0;
if ((flags & O_ACCMODE) == O_RDONLY) {
extra_perm = kNtFileWriteAttributes | kNtFileWriteEa;
}
// open the file, following symlinks
return __fix_enotdir(CreateFile(path16, perm, share, &kNtIsInheritable, disp,
attr | extra_attr, 0),
path16);
int e = errno;
int64_t hand = CreateFile(path16, perm | extra_perm, share, &kNtIsInheritable,
disp, attr | extra_attr, 0);
if (hand == -1 && errno == EACCES && (flags & O_ACCMODE) == O_RDONLY) {
errno = e;
hand = CreateFile(path16, perm, share, &kNtIsInheritable, disp,
attr | extra_attr, 0);
}
return __fix_enotdir(hand, path16);
}
static textwindows int sys_open_nt_console(int dirfd,

View file

@ -25,6 +25,7 @@
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
@ -92,6 +93,7 @@
* - `O_APPEND` open file for appending only
* - `O_NOFOLLOW` fail with ELOOP if it's a symlink
* - `O_NONBLOCK` asks read/write to fail with `EAGAIN` rather than block
* - `O_UNLINK` delete file automatically on close
* - `O_EXEC` open file for execution only; see fexecve()
* - `O_NOCTTY` prevents `path` from becoming the controlling terminal
* - `O_DIRECTORY` advisory feature for avoiding accidentally opening files
@ -107,7 +109,7 @@
* - `O_SEQUENTIAL` hint sequential access intent (zero on non-Windows)
* - `O_COMPRESSED` ask fs to abstract compression (zero on non-Windows)
* - `O_INDEXED` turns on that slow performance (zero on non-Windows)
* - `O_TMPFILE` should not be used; use tmpfd() or tmpfile() instead
* - `O_TMPFILE` EINVALs on non-Linux; please use tmpfd() / tmpfile()
* There are three regular combinations for the above flags:
* - `O_RDONLY`: Opens existing file for reading. If it doesn't
* exist then nil is returned and errno will be `ENOENT` (or in
@ -148,6 +150,7 @@
* @raise EINTR if we needed to block and a signal was delivered instead
* @raise EEXIST if `O_CREAT|O_EXCL` are used and `path` already existed
* @raise EINVAL if ASCII control codes are used in `path` on Windows
* @raise EINVAL if `O_UNLINK` is used without `O_CREAT|O_EXCL`
* @raise EINVAL if `O_TRUNC` is specified in `O_RDONLY` mode
* @raise EINVAL if `flags` contains unsupported bits
* @raise ECANCELED if thread was cancelled in masked mode
@ -180,6 +183,15 @@ int openat(int dirfd, const char *path, int flags, ...) {
if (!path || (IsAsan() && !__asan_is_valid_str(path))) {
rc = efault();
} else if ((flags & O_UNLINK) &&
(flags & (O_CREAT | O_EXCL)) != (O_CREAT | O_EXCL)) {
// O_UNLINK is a non-standard cosmo extension; we've chosen bits for
// this magic number which we believe are unlikely to interfere with
// the bits chosen by operating systems both today and in the future
// however, due to the risks here and the irregularity of using this
// feature for anything but temporary files, we are going to prevent
// the clever use cases for now; please file an issue if you want it
rc = einval();
} else if (__isfdkind(dirfd, kFdZip)) {
rc = enotsup(); // TODO
} else if (_weaken(__zipos_open) &&
@ -190,20 +202,29 @@ int openat(int dirfd, const char *path, int flags, ...) {
rc = enotsup(); // TODO
}
} else if ((flags & O_ACCMODE) == O_RDONLY && (flags & O_TRUNC)) {
rc = einval(); // Every OS except OpenBSD actually does this D:
// Every operating system we've tested (with the notable exception
// of OpenBSD) will gladly truncate files opened in read-only mode
rc = einval();
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) {
rc = sys_openat(dirfd, path, flags, mode);
if (IsFreebsd()) {
// Address FreeBSD divergence from IEEE Std 1003.1-2008 (POSIX.1)
// in the case when O_NOFOLLOW is used, but fails due to symlink.
if (rc == -1 && errno == EMLINK) {
// openat unix userspace
rc = sys_openat(dirfd, path, flags & ~O_UNLINK, mode);
if (rc != -1) {
// openat succeeded
if (flags & O_UNLINK) {
// Implement Cosmopolitan O_UNLINK extension for UNIX
// This cannot fail since we require O_CREAT / O_EXCL
unassert(!sys_unlinkat(dirfd, path, 0));
}
} else {
// openat failed
if (IsFreebsd() && errno == EMLINK) {
// Address FreeBSD divergence from IEEE Std 1003.1-2008 (POSIX.1)
// in the case when O_NOFOLLOW is used, but fails due to symlink.
errno = ELOOP;
}
}
if (IsNetbsd()) {
// Address NetBSD divergence from IEEE Std 1003.1-2008 (POSIX.1)
// in the case when O_NOFOLLOW is used but fails due to symlink.
if (rc == -1 && errno == EFTYPE) {
if (IsNetbsd() && errno == EFTYPE) {
// Address NetBSD divergence from IEEE Std 1003.1-2008 (POSIX.1)
// in the case when O_NOFOLLOW is used but fails due to symlink.
errno = ELOOP;
}
}
@ -216,9 +237,8 @@ int openat(int dirfd, const char *path, int flags, ...) {
}
END_CANCELLATION_POINT;
STRACE("openat(%s, %#s, %s, %#o) → %d% m", DescribeDirfd(dirfd), path,
DescribeOpenFlags(flags), (flags & (O_CREAT | O_TMPFILE)) ? mode : 0,
rc);
STRACE("openat(%s, %#s, %s%s) → %d% m", DescribeDirfd(dirfd), path,
DescribeOpenFlags(flags), DescribeOpenMode(flags, mode), rc);
return rc;
}

127
libc/calls/openatemp.c Normal file
View file

@ -0,0 +1,127 @@
/*-*- 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/errno.h"
#include "libc/intrin/bits.h"
#include "libc/stdio/rand.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
/**
* Opens unique temporary file with maximum generality.
*
* This function is similar to mkstemp() in that it does two things:
*
* 1. Generate a unique filename by mutating `template`
* 2. Return a newly opened file descriptor to the name
*
* Exclusive secure access is assured even if `/tmp` is being used on a
* UNIX system like Super Dimensional Fortress or CPanel where multiple
* hostile adverserial users may exist on a single multi-tenant system.
*
* The substring XXXXXX is replaced with 30 bits of base32 entropy and a
* hundred retries are attempted in the event of collisions. The XXXXXXX
* pattern must be present at the end of the supplied template string.
*
* If the generated filename needs to have a file extension (rather than
* ending with random junk) then this API has the helpful `suffixlen` to
* specify exactly how long that suffix in the template actually is. For
* example if the template is `"/tmp/notes.XXXXXX.txt"` then `suffixlen`
* should be `4`.
*
* The flags `O_RDWR | O_CREAT | O_EXCL` are always set and don't need
* to be specified by the caller. It's a good idea to pass `O_CLOEXEC`
* and some applications may want `O_APPEND`. Cosmopolitan also offers
* `O_UNLINK` which will ensure the created file will delete itself on
* close similar to calling unlink() after this function on `template`
* which is mutated on success, except `O_UNLINK` will work right when
* running on Windows and it's polyfilled automatically on UNIX.
*
* The `mode` parameter should usually be `0600` to ensure owner-only
* read/write access. However it may be useful to set this to `0700`
* when creating executable files. Please note that sometimes `/tmp` is
* mounted by system administrators as `noexec`. It's also permissible
* to pass `0` here, since the `0600` bits are always set implicitly.
*
* ### Examples
*
* Here's an example of how to replicate the functionality of tmpfile()
* which creates an unnamed temporary file as an stdio handle, which is
* guaranteed to either not have a name (unlinked on UNIX), or shall be
* deleted once closed (will perform kNtFileFlagDeleteOnClose on WIN32)
*
* char path[] = "/tmp/XXXXXX";
* int fd = openatemp(AT_FDCWD, path, 0, O_UNLINK, 0);
* FILE *tmp = fdopen(fd, "w+");
*
* Here's an example of how to do mktemp() does, where a temporary file
* name is generated with pretty good POSIX and security best practices
*
* char path[PATH_MAX+1];
* const char *tmpdir = getenv("TMPDIR");
* strlcpy(path, tmpdir ? tmpdir : "/tmp", sizeof(path));
* strlcat(path, "/notes.XXXXXX.txt", sizeof(path));
* close(openatemp(AT_FDCWD, path, 4, O_UNLINK, 0));
* printf("you can use %s to store your notes\n", path);
*
* @param dirfd is open directory file descriptor, which is ignored if
* `template` is an absolute path; or `AT_FDCWD` to specify getcwd
* @param template is a pathname relative to current directory by default,
* that needs to have "XXXXXX" at the end of the string; this memory
* must be mutable and should be owned by the calling thread; it will
* be modified (only on success) to return the generated filename; it
* is recommended that the caller use `kTmpPath` at the beginning of
* the generated `template` path and then set `dirfd` to `AT_FDCWD`
* @param suffixlen may be nonzero to permit characters after the "XXXXXX"
* @param mode is conventionally 0600, for owner-only non-exec access
* @param flags could have O_APPEND, O_CLOEXEC, O_UNLINK, O_SYNC, etc.
* @return exclusive open file descriptor for file at the generated path
* stored to `template`, or -1 w/ errno
* @raise EINVAL if `template` (less the `suffixlen` region) didn't
* end with the string "XXXXXXX"
* @raise EINVAL if `suffixlen` was negative or too large
*/
int openatemp(int dirfd, char *template, int suffixlen, int flags, int mode) {
flags &= ~O_ACCMODE;
flags |= O_RDWR | O_CREAT | O_EXCL;
int len = strlen(template);
if (6 + suffixlen < 6 || 6 + suffixlen > len ||
READ32LE(template + len - suffixlen - 6) != READ32LE("XXXX") ||
READ16LE(template + len - suffixlen - 6 + 4) != READ16LE("XX")) {
return einval();
}
for (;;) {
int w = _rand64();
for (int i = 0; i < 6; ++i) {
template[len - suffixlen - 6 + i] =
"0123456789abcdefghikmnpqrstvwxyz"[w & 31];
w >>= 5;
}
int fd, e = errno;
if ((fd = openat(dirfd, template, flags, mode | 0600)) != -1) {
return fd;
} else if (errno == EEXIST) {
errno = e;
} else {
memcpy(template + len - suffixlen - 6, "XXXXXX", 6);
return -1;
}
}
}

View file

@ -1,7 +1,7 @@
/*-*- 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 2022 Justine Alexandra Roberts Tunney
Copyright 2023 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
@ -16,13 +16,21 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/termios.h"
#include "libc/calls/termios.h"
#include "libc/sysv/consts/termios.h"
#include "libc/calls/calls.h"
#include "libc/runtime/runtime.h"
int main(int argc, char *argv[]) {
struct termios t;
if (tcgetattr(0, &t) == -1) return 1;
t.c_lflag ^= ECHOCTL;
if (tcsetattr(0, TCSANOW, &t) == -1) return 2;
/**
* Returns environment variable, securely.
*
* This is the same as getenv() except it'll return null if current
* process is a setuid / setgid program.
*
* @param name is environment variable key name, which may not be null
*/
char *secure_getenv(const char *name) {
if (!issetugid()) {
return getenv(name);
} else {
return 0;
}
}

View file

@ -14,8 +14,8 @@ struct dirent { /* linux getdents64 abi */
struct dirstream;
typedef struct dirstream DIR;
DIR *fdopendir(int) dontdiscard;
DIR *opendir(const char *) dontdiscard;
DIR *fdopendir(int) __wur;
DIR *opendir(const char *) __wur;
int closedir(DIR *);
int dirfd(DIR *);
long telldir(DIR *);

View file

@ -25,7 +25,7 @@ int sys_nanosleep_xnu(const struct timespec *, struct timespec *);
int sys_sem_timedwait(int64_t, const struct timespec *);
int sys_utimensat(int, const char *, const struct timespec[2], int);
int sys_utimensat_nt(int, const char *, const struct timespec[2], int);
int sys_utimensat_xnu(int, const char *, const struct timespec[2], int);
int sys_utimensat_old(int, const char *, const struct timespec[2], int);
const char *DescribeTimespec(char[45], int, const struct timespec *);
#define DescribeTimespec(rc, ts) DescribeTimespec(alloca(45), rc, ts)

View file

@ -26,7 +26,7 @@ int sys_linkat_nt(int, const char *, int, const char *);
int sys_madvise_nt(void *, size_t, int);
int sys_mkdirat_nt(int, const char *, uint32_t);
int sys_msync_nt(char *, size_t, int);
int sys_open_nt(int, const char *, uint32_t, int32_t) dontdiscard;
int sys_open_nt(int, const char *, uint32_t, int32_t) __wur;
int sys_pipe_nt(int[hasatleast 2], unsigned);
int sys_renameat_nt(int, const char *, int, const char *);
int sys_sched_yield_nt(void);

View file

@ -1,5 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_SYSCALL_SUPPORT_NT_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_SYSCALL_SUPPORT_NT_INTERNAL_H_
#include "libc/limits.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_

View file

@ -15,13 +15,13 @@ int tcsetattr(int, int, const struct termios *);
int openpty(int *, int *, char *, const struct termios *,
const struct winsize *) paramsnonnull((1, 2));
int forkpty(int *, char *, const struct termios *, const struct winsize *)
paramsnonnull((1, 2)) dontdiscard;
paramsnonnull((1, 2)) __wur;
char *ptsname(int);
errno_t ptsname_r(int, char *, size_t);
int grantpt(int);
int unlockpt(int);
int posix_openpt(int) dontdiscard;
int posix_openpt(int) __wur;
int tcdrain(int);
int tcgetsid(int);

View file

@ -20,12 +20,14 @@
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/limits.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/temp.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/o.h"
#include "libc/temp.h"
#define _O_TMPFILE 000020200000
#define O_TMPFILE_LINUX 0x00410000
int _mkstemp(char *, int);
@ -79,7 +81,7 @@ int tmpfd(void) {
char path[PATH_MAX + 1];
if (IsLinux()) {
e = errno;
if ((fd = open(kTmpPath, O_RDWR | _O_TMPFILE, 0600)) != -1) {
if ((fd = open(kTmpPath, O_RDWR | O_TMPFILE_LINUX, 0600)) != -1) {
return fd;
} else {
errno = e;
@ -90,7 +92,6 @@ int tmpfd(void) {
if (!(prog = program_invocation_short_name)) prog = "tmp";
strlcat(path, prog, sizeof(path));
strlcat(path, ".XXXXXX", sizeof(path));
if ((fd = _mkstemp(path, IsWindows() ? 0x00410000 : 0)) == -1) return -1;
if (!IsWindows()) unassert(!unlink(path));
if ((fd = openatemp(AT_FDCWD, path, 0, O_UNLINK, 0)) == -1) return -1;
return fd;
}

View file

@ -32,6 +32,7 @@
#include "libc/fmt/conv.h"
#include "libc/fmt/libgen.h"
#include "libc/intrin/strace.internal.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/vendor.internal.h"
#include "libc/runtime/internal.h"
@ -175,7 +176,7 @@ static int unveil_init(void) {
.handled_access_fs = State.fs_mask,
};
// [undocumented] landlock_create_ruleset() always returns O_CLOEXEC
// assert(__sys_fcntl(rc, F_GETFD, 0) == FD_CLOEXEC);
// assert(__sys_fcntl(rc, F_GETFD) == FD_CLOEXEC);
if ((rc = landlock_create_ruleset(&attr, sizeof(attr), 0)) < 0) return -1;
// grant file descriptor a higher number that's less likely to interfere
if ((fd = __sys_fcntl(rc, F_DUPFD_CLOEXEC, 100)) == -1) {

View file

@ -48,12 +48,16 @@ int __utimens(int fd, const char *path, const struct timespec ts[2],
(path && (_weaken(__zipos_parseuri) &&
_weaken(__zipos_parseuri)(path, &zipname) != -1))) {
rc = erofs();
} else if (IsLinux() && !__is_linux_2_6_23() && fd == AT_FDCWD && !flags) {
rc = sys_utimes(path, (void *)ts); // rhel5 truncates to seconds
} else if (!IsWindows()) {
} else if (IsXnu() || (IsLinux() && !__is_linux_2_6_23())) {
rc = sys_utimensat_old(fd, path, ts, flags);
} else if (IsLinux() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) {
rc = sys_utimensat(fd, path, ts, flags);
} else {
} else if (IsWindows()) {
rc = sys_utimensat_nt(fd, path, ts, flags);
} else if (IsMetal()) {
rc = enosys();
} else {
rc = enosys();
}
return rc;
}

View file

@ -41,8 +41,14 @@ textwindows int sys_utimensat_nt(int dirfd, const char *path,
if (path) {
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
if ((fh = CreateFile(path16, kNtFileWriteAttributes, kNtFileShareRead, NULL,
kNtOpenExisting, kNtFileAttributeNormal, 0)) != -1) {
if ((fh = CreateFile(
path16, kNtFileWriteAttributes,
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, NULL,
kNtOpenExisting,
kNtFileAttributeNormal | kNtFileFlagBackupSemantics |
((flags & AT_SYMLINK_NOFOLLOW) ? kNtFileFlagOpenReparsePoint
: 0),
0)) != -1) {
closeme = fh;
} else {
return __winerr();

View file

@ -16,27 +16,78 @@
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/stat.h"
#include "libc/calls/struct/stat.internal.h"
#include "libc/calls/struct/timespec.internal.h"
#include "libc/calls/struct/timeval.h"
#include "libc/calls/struct/timeval.internal.h"
#include "libc/fmt/conv.h"
#include "libc/nexgen32e/nexgen32e.h"
#include "libc/dce.h"
#include "libc/fmt/itoa.h"
#include "libc/limits.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/utime.h"
#include "libc/sysv/errfuns.h"
#include "libc/time/time.h"
int sys_utimensat_xnu(int dirfd, const char *path, const struct timespec ts[2],
int sys_utimensat_old(int dirfd, const char *path, const struct timespec ts[2],
int flags) {
struct stat st;
struct timeval now, tv[2];
if (flags) return einval();
char buf[PATH_MAX + 1];
// validate usage
if (path) {
if (dirfd != AT_FDCWD) {
return enotsup();
}
} else {
unassert(dirfd >= 0);
}
if (flags) {
return einval();
}
// xnu has futimes(), but rhel5 only has utimes()
// on linux we'll try to get the path from procfs
if (IsLinux() && !path) {
char procpath[36];
char *p = procpath;
p = stpcpy(p, "/proc/");
p = FormatInt32(p, getpid());
p = stpcpy(p, "/fd/");
p = FormatInt32(p, dirfd);
ssize_t got;
if ((got = readlink(procpath, buf, sizeof(buf))) == -1) {
return -1;
}
if (got == sizeof(buf)) {
return enametoolong();
}
unassert(buf[0] == '/');
buf[got] = 0;
path = buf;
}
// perform preliminary system calls ahead of time
struct timeval now;
if (!ts || ts[0].tv_nsec == UTIME_NOW || ts[1].tv_nsec == UTIME_NOW) {
gettimeofday(&now, NULL);
unassert(!gettimeofday(&now, 0));
}
if (ts && (ts[0].tv_nsec == UTIME_NOW || ts[1].tv_nsec == UTIME_NOW)) {
if (fstatat(dirfd, path, &st, flags) == -1) return -1;
struct stat st;
if (ts && (ts[0].tv_nsec == UTIME_OMIT || ts[1].tv_nsec == UTIME_OMIT)) {
if (path) {
if (sys_fstatat(AT_FDCWD, path, &st, 0) == -1) {
return -1;
}
} else {
if (sys_fstat(dirfd, &st) == -1) {
return -1;
}
}
}
// change the timestamps
struct timeval tv[2];
if (ts) {
if (ts[0].tv_nsec == UTIME_NOW) {
tv[0] = now;
@ -56,17 +107,11 @@ int sys_utimensat_xnu(int dirfd, const char *path, const struct timespec ts[2],
tv[0] = now;
tv[1] = now;
}
// apply the new timestamps
if (path) {
if (dirfd == AT_FDCWD) {
return sys_utimes(path, tv);
} else {
return enosys();
}
return sys_utimes(path, tv);
} else {
if (dirfd != AT_FDCWD) {
return sys_futimes(dirfd, tv);
} else {
return einval();
}
return sys_futimes(dirfd, tv);
}
}

View file

@ -16,39 +16,37 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/struct/timespec.internal.h"
#include "libc/calls/struct/timeval.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/consts/at.h"
#include "libc/time/time.h"
#include "libc/runtime/zipos.internal.h"
int sys_utimensat(int dirfd, const char *path, const struct timespec ts[2],
int flags) {
int rc, olderr;
struct timeval tv[2];
if (!IsXnu()) {
if (!path && (IsFreebsd() || IsNetbsd() || IsOpenbsd())) {
rc = sys_futimens(dirfd, ts);
} else {
olderr = errno;
rc = __sys_utimensat(dirfd, path, ts, flags);
// TODO(jart): How does RHEL5 do futimes()?
if (rc == -1 && errno == ENOSYS && path) {
errno = olderr;
if (ts) {
tv[0] = timespec_totimeval(ts[0]);
tv[1] = timespec_totimeval(ts[1]);
rc = sys_utimes(path, tv);
} else {
rc = sys_utimes(path, NULL);
}
unassert(!IsWindows() && !IsXnu());
if (!path && (IsFreebsd() || IsNetbsd() || IsOpenbsd())) {
rc = sys_futimens(dirfd, ts);
} else {
olderr = errno;
rc = __sys_utimensat(dirfd, path, ts, flags);
// TODO(jart): How does RHEL5 do futimes()?
if (rc == -1 && errno == ENOSYS && path) {
errno = olderr;
if (ts) {
tv[0] = timespec_totimeval(ts[0]);
tv[1] = timespec_totimeval(ts[1]);
rc = sys_utimes(path, tv);
} else {
rc = sys_utimes(path, NULL);
}
}
return rc;
} else {
return sys_utimensat_xnu(dirfd, path, ts, flags);
}
return rc;
}

View file

@ -16,17 +16,10 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/asan.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timespec.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
/**
@ -43,9 +36,10 @@
* @param dirfd can be `AT_FDCWD` or an open directory
* @param path is filename whose timestamps should be modified
* @param ts is {access, modified} timestamps, or null for current time
* @param flags can have `AT_SYMLINK_NOFOLLOW` when `path` is specified
* @param flags can have `AT_SYMLINK_NOFOLLOW`
* @return 0 on success, or -1 w/ errno
* @raise EINVAL if `flags` had an unrecognized value
* @raise EINVAL on XNU or RHEL5 when any `flags` are used
* @raise EPERM if pledge() is in play without `fattr` promise
* @raise EACCES if unveil() is in play and `path` isn't unveiled
* @raise EINVAL if `ts` specifies a nanosecond value that's out of range
@ -54,23 +48,21 @@
* @raise EBADF if `dirfd` isn't a valid fd or `AT_FDCWD`
* @raise EFAULT if `path` or `ts` memory was invalid
* @raise EROFS if `path` is on read-only filesystem (e.g. zipos)
* @raise ENOSYS on bare metal or on rhel5 when `dirfd` or `flags` is used
* @raise ENOTSUP on XNU or RHEL5 when `dirfd` isn't `AT_FDCWD`
* @raise ENOSYS on bare metal
* @asyncsignalsafe
* @threadsafe
*/
int utimensat(int dirfd, const char *path, const struct timespec ts[2],
int flags) {
int rc;
if (!path) {
rc = efault(); // linux kernel abi behavior isn't supported
} else {
rc = __utimens(dirfd, path, ts, flags);
}
STRACE("utimensat(%s, %#s, {%s, %s}, %#o) → %d% m", DescribeDirfd(dirfd),
path, DescribeTimespec(0, ts), DescribeTimespec(0, ts ? ts + 1 : 0),
flags, rc);
return rc;
}

View file

@ -22,6 +22,7 @@
#include "libc/calls/struct/timeval.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
/**
* Changes last accessed/modified timestamps on file.
@ -36,7 +37,9 @@
int utimes(const char *path, const struct timeval tv[2]) {
int rc;
struct timespec ts[2];
if (tv) {
if (!path) {
rc = efault();
} else if (tv) {
ts[0] = timeval_totimespec(tv[0]);
ts[1] = timeval_totimespec(tv[1]);
rc = __utimens(AT_FDCWD, path, ts, 0);

View file

@ -1,6 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_FMT_STRTOL_H_
#define COSMOPOLITAN_LIBC_FMT_STRTOL_H_
#include "libc/errno.h"
#include "libc/str/str.h"
#define CONSUME_SPACES(t, s, c) \
if (endptr) *endptr = (t *)(s); \
@ -10,33 +11,34 @@
d = c == '-' ? -1 : 1; \
if (c == '-' || c == '+') c = *++s
#define GET_RADIX(s, c, r) \
if (!r) { \
if (c == '0') { \
t |= 1; \
c = *++s; \
if (c == 'x' || c == 'X') { \
c = *++s; \
r = 16; \
} else if (c == 'b' || c == 'B') { \
c = *++s; \
r = 2; \
} else { \
r = 8; \
} \
} else { \
r = 10; \
} \
} else if (!(2 <= r && r <= 36)) { \
errno = EINVAL; \
return 0; \
} else if (c == '0') { \
t |= 1; \
c = *++s; \
if ((r == 2 && (c == 'b' || c == 'B')) || \
(r == 16 && (c == 'x' || c == 'X'))) { \
c = *++s; \
} \
#define GET_RADIX(s, c, r) \
if (!r) { \
if (c == '0') { \
t |= 1; \
c = *++s; \
if ((c == 'x' || c == 'X') && isxdigit(s[1])) { \
c = *++s; \
r = 16; \
} else if ((c == 'b' || c == 'B') && (s[1] == '0' || s[1] == '1')) { \
c = *++s; \
r = 2; \
} else { \
r = 8; \
} \
} else { \
r = 10; \
} \
} else if (!(2 <= r && r <= 36)) { \
errno = EINVAL; \
return 0; \
} else if (c == '0') { \
t |= 1; \
c = *++s; \
if ((r == 2 && \
((c == 'b' || c == 'B') && (s[1] == '0' || s[1] == '1'))) || \
(r == 16 && ((c == 'x' || c == 'X') && isxdigit(s[1])))) { \
c = *++s; \
} \
}
#endif /* COSMOPOLITAN_LIBC_FMT_STRTOL_H_ */

View file

@ -156,7 +156,7 @@ typedef struct {
#define memcpyesque libcesque
#define strlenesque libcesque nosideeffect paramsnonnull()
#define vallocesque \
libcesque dontdiscard returnsaligned((65536)) returnspointerwithnoaliases
libcesque __wur returnsaligned((65536)) returnspointerwithnoaliases
#define reallocesque libcesque returnsaligned((16))
#define mallocesque reallocesque returnspointerwithnoaliases
#define interruptfn nocallersavedregisters forcealignargpointer
@ -268,13 +268,13 @@ typedef struct {
#endif /* __cplusplus */
#endif /* forceinline */
#ifndef dontdiscard
#ifndef __wur
#if !defined(__STRICT_ANSI__) && \
((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 304 || \
__has_attribute(__warn_unused_result__))
#define dontdiscard __attribute__((__warn_unused_result__))
#define __wur __attribute__((__warn_unused_result__))
#else
#define dontdiscard
#define __wur
#endif
#endif

View file

@ -79,15 +79,7 @@
#define _PAGESIZE 4096
#endif
#define BUFSIZ 0x1000 /* best stdio default */
#define CHAR_BIT 8 /* b/c von neumann */
#define ARG_MAX 0xfffe /* for argv and envp; see CreateProcess (32767*2) */
#define PATH_MAX 1024 /* b/c _XOPEN_PATH_MAX */
#define NAME_MAX 255 /* 511 on netbsd */
#define CHILD_MAX 16 /* only if malloc isn't linked */
#define OPEN_MAX 16 /* only if malloc isn't linked */
#define ATEXIT_MAX 32 /* only if malloc isn't linked */
#define NSIG 128 /* b/c freebsd */
#define NSIG 128 /* b/c freebsd */
#if defined(__LP64__) && !defined(__INT64_TYPE__)
#include "libc/integral/lp64.inc"

View file

@ -347,7 +347,7 @@ static void __asan_exit(void) {
_Exit(99);
}
dontdiscard static __asan_die_f *__asan_die(void) {
static __wur __asan_die_f *__asan_die(void) {
if (_weaken(__die)) {
return _weaken(__die);
} else {
@ -707,8 +707,7 @@ static const char *__asan_describe_access_poison(signed char kind) {
}
}
static dontdiscard __asan_die_f *__asan_report_invalid_pointer(
const void *addr) {
static __wur __asan_die_f *__asan_report_invalid_pointer(const void *addr) {
kprintf("\n\e[J\e[1;31masan error\e[0m: this corruption at %p shadow %p\n",
addr, SHADOW(addr));
return __asan_die();
@ -825,9 +824,9 @@ static void __asan_report_memory_origin(const unsigned char *addr, int size,
}
}
dontdiscard static __asan_die_f *__asan_report(const void *addr, int size,
const char *message,
signed char kind) {
static __wur __asan_die_f *__asan_report(const void *addr, int size,
const char *message,
signed char kind) {
int i;
wint_t c;
signed char t;
@ -940,8 +939,8 @@ void __asan_verify_str(const char *p) {
__asan_verify_failed(UNSHADOW(f.shadow), 8, f);
}
static dontdiscard __asan_die_f *__asan_report_memory_fault(
void *addr, int size, const char *message) {
static __wur __asan_die_f *__asan_report_memory_fault(void *addr, int size,
const char *message) {
return __asan_report(addr, size, message,
__asan_fault(SHADOW(addr), -128).kind);
}

View file

@ -8,9 +8,9 @@ int _bsrl(long) pureconst;
int _bsrll(long long) pureconst;
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
#define _bsr(x) (__builtin_clz(x) ^ (sizeof(int) * CHAR_BIT - 1))
#define _bsrl(x) (__builtin_clzl(x) ^ (sizeof(long) * CHAR_BIT - 1))
#define _bsrll(x) (__builtin_clzll(x) ^ (sizeof(long long) * CHAR_BIT - 1))
#define _bsr(x) (__builtin_clz(x) ^ (sizeof(int) * 8 - 1))
#define _bsrl(x) (__builtin_clzl(x) ^ (sizeof(long) * 8 - 1))
#define _bsrll(x) (__builtin_clzll(x) ^ (sizeof(long long) * 8 - 1))
#endif
COSMOPOLITAN_C_END_

View file

@ -139,6 +139,10 @@ void bzero(void *p, size_t n) {
#ifdef __x86_64__
asm("xorl\t%k0,%k0" : "=r"(x));
#else
if (1) {
memset(p, 0, n);
return;
}
x = 0;
#endif
if (n <= 16) {

View file

@ -31,11 +31,11 @@ __msabi extern typeof(CreateDirectory) *const __imp_CreateDirectoryW;
* @note this wrapper takes care of ABI, STRACE(), and __winerr()
*/
textwindows bool32 CreateDirectory(const char16_t *lpPathName,
struct NtSecurityAttributes *lpSecurity) {
const struct NtSecurityAttributes *lpSec) {
bool32 ok;
ok = __imp_CreateDirectoryW(lpPathName, lpSecurity);
ok = __imp_CreateDirectoryW(lpPathName, lpSec);
if (!ok) __winerr();
NTTRACE("CreateDirectory(%#hs, %s) → %hhhd% m", lpPathName,
DescribeNtSecurityAttributes(lpSecurity), ok);
DescribeNtSecurityAttributes(lpSec), ok);
return ok;
}

View file

@ -32,8 +32,8 @@ __msabi extern typeof(CreateProcess) *const __imp_CreateProcessW;
*/
textwindows bool32
CreateProcess(const char16_t *opt_lpApplicationName, char16_t *lpCommandLine,
struct NtSecurityAttributes *opt_lpProcessAttributes,
struct NtSecurityAttributes *opt_lpThreadAttributes,
const struct NtSecurityAttributes *opt_lpProcessAttributes,
const struct NtSecurityAttributes *opt_lpThreadAttributes,
bool32 bInheritHandles, uint32_t dwCreationFlags,
void *opt_lpEnvironment, const char16_t *opt_lpCurrentDirectory,
const struct NtStartupInfo *lpStartupInfo,

View file

@ -32,7 +32,7 @@ __msabi extern typeof(CreateThread) *const __imp_CreateThread;
* @note this wrapper takes care of ABI, STRACE()
*/
textwindows int64_t
CreateThread(struct NtSecurityAttributes *lpThreadAttributes,
CreateThread(const struct NtSecurityAttributes *lpThreadAttributes,
size_t dwStackSize, void *lpStartAddress, void *lpParameter,
uint32_t dwCreationFlags, uint32_t *opt_lpThreadId) {
int64_t hHandle;

View file

@ -46,7 +46,6 @@ dontasan int __cxa_atexit(void *fp, void *arg, void *pred) {
/* asan runtime depends on this function */
unsigned i;
struct CxaAtexitBlock *b, *b2;
_Static_assert(ATEXIT_MAX == CHAR_BIT * sizeof(b->mask), "");
__cxa_lock();
b = __cxa_blocks.p;
if (!b) b = __cxa_blocks.p = &__cxa_blocks.root;

View file

@ -14,7 +14,7 @@ struct CxaAtexitBlocks {
void *fp;
void *arg;
void *pred;
} p[ATEXIT_MAX];
} p[32];
} * p, root;
};

View file

@ -44,6 +44,7 @@ const char *DescribeNtProcAccessFlags(char[256], uint32_t);
const char *DescribeNtStartFlags(char[128], uint32_t);
const char *DescribeNtSymlinkFlags(char[64], uint32_t);
const char *DescribeOpenFlags(char[128], int);
const char *DescribeOpenMode(char[15], int, int);
const char *DescribePersonalityFlags(char[128], int);
const char *DescribePollFlags(char[64], int);
const char *DescribePrctlOperation(int);
@ -97,6 +98,7 @@ const char *DescribeWhichPrio(char[12], int);
#define DescribeNtStartFlags(x) DescribeNtStartFlags(alloca(128), x)
#define DescribeNtSymlinkFlags(x) DescribeNtSymlinkFlags(alloca(64), x)
#define DescribeOpenFlags(x) DescribeOpenFlags(alloca(128), x)
#define DescribeOpenMode(x, y) DescribeOpenMode(alloca(15), x, y)
#define DescribePersonalityFlags(p) DescribePersonalityFlags(alloca(128), p)
#define DescribePollFlags(p) DescribePollFlags(alloca(64), p)
#define DescribeProtFlags(x) DescribeProtFlags(alloca(48), x)

View file

@ -20,7 +20,7 @@
#include "libc/intrin/kprintf.h"
#include "libc/macros.internal.h"
const char *(DescribeNtOverlapped)(char b[128], struct NtOverlapped *o) {
const char *(DescribeNtOverlapped)(char b[128], const struct NtOverlapped *o) {
int i = 0, n = 128;
bool gotsome = false;
if (!o) return "NULL";

View file

@ -5,7 +5,7 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
const char *DescribeNtOverlapped(char[128], struct NtOverlapped *);
const char *DescribeNtOverlapped(char[128], const struct NtOverlapped *);
#define DescribeNtOverlapped(x) DescribeNtOverlapped(alloca(128), x)
COSMOPOLITAN_C_END_

View file

@ -21,8 +21,9 @@
#include "libc/intrin/describeflags.internal.h"
#include "libc/nt/struct/securityattributes.h"
const char *(DescribeNtSecurityAttributes)(char buf[32],
struct NtSecurityAttributes *p) {
const char *(
DescribeNtSecurityAttributes)(char buf[32],
const struct NtSecurityAttributes *p) {
if (p == &kNtIsInheritable) return "&kNtIsInheritable";
FormatInt64(buf, (uintptr_t)p);
return buf;

View file

@ -0,0 +1,40 @@
/*-*- 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 2023 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/dce.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/sysv/consts/o.h"
#define O_TMPFILE_LINUX 0x00410000
static bool IsCreatingFile(int flags) {
return (flags & O_CREAT) ||
(IsLinux() && (flags & O_TMPFILE_LINUX) == O_TMPFILE_LINUX);
}
const char *(DescribeOpenMode)(char buf[15], int flags, int mode) {
if (!IsCreatingFile(flags)) {
return "";
}
char *p = buf;
*p++ = ',';
*p++ = ' ';
FormatOctal32(p, mode, true);
return buf;
}

View file

@ -34,6 +34,8 @@
#include "libc/sysv/consts/o.h"
#include "libc/thread/thread.h"
#define OPEN_MAX 16
#ifdef __x86_64__
__static_yoink("_init_g_fds");
#endif

View file

@ -31,7 +31,6 @@
char *getenv(const char *s) {
char **p;
struct Env e;
if (!s) return 0;
if (!(p = environ)) return 0;
e = __getenv(p, s);
#if SYSDEBUG

View file

@ -19,7 +19,7 @@
#include "libc/nt/struct/securityattributes.h"
const struct NtSecurityAttributes kNtIsInheritable = {
sizeof(struct NtSecurityAttributes),
NULL,
true,
.nLength = sizeof(struct NtSecurityAttributes),
.lpSecurityDescriptor = NULL,
.bInheritHandle = true,
};

View file

@ -36,7 +36,8 @@ kOpenFlags:
.e O_TRUNC,"TRUNC" //
.e O_CLOEXEC,"CLOEXEC" //
.e O_NONBLOCK,"NONBLOCK" //
.e O_TMPFILE,"TMPFILE" // linux, windows
.e O_TMPFILE,"TMPFILE" // linux
.e O_UNLINK,"UNLINK" // windows+unix
.e O_DIRECTORY,"DIRECTORY" // order matters
.e O_DIRECT,"DIRECT" // no-op on xnu/openbsd
.e O_APPEND,"APPEND" // weird on nt

View file

@ -22,7 +22,7 @@
#include "libc/str/str.h"
#ifndef __aarch64__
typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(1)));
typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(16)));
static inline const unsigned char *memchr_pure(const unsigned char *s,
unsigned char c, size_t n) {
@ -68,14 +68,22 @@ dontasan static inline const unsigned char *memchr_sse(const unsigned char *s,
* @return is pointer to first instance of c or NULL if not found
* @asyncsignalsafe
*/
void *memchr(const void *s, int c, size_t n) {
dontasan void *memchr(const void *s, int c, size_t n) {
#if defined(__x86_64__) && !defined(__chibicc__)
const void *r;
if (IsAsan()) __asan_verify(s, n);
r = memchr_sse(s, c, n);
const unsigned char *p = (const unsigned char *)s;
while (n && ((intptr_t)p & 15)) {
if (*p == (unsigned char)c) {
return (void *)p;
}
++p;
--n;
}
r = memchr_sse(p, c, n);
return (void *)r;
#else
return memchr_pure(s, c, n);
return (void *)memchr_pure(s, c, n);
#endif
}

View file

@ -22,113 +22,34 @@
#include "libc/str/str.h"
#ifndef __aarch64__
#define PMOVMSKB(x) __builtin_ia32_pmovmskb128(x)
typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(1)));
#if defined(__x86_64__) && !defined(__chibicc__)
static dontinline antiquity int memcmp_sse(const unsigned char *p,
const unsigned char *q, size_t n) {
unsigned u;
if (n > 32) {
while (n > 16 + 16) {
if (!(u = PMOVMSKB(*(xmm_t *)p == *(xmm_t *)q) ^ 0xffff)) {
n -= 16;
p += 16;
q += 16;
} else {
u = __builtin_ctzl(u);
return p[u] - q[u];
}
}
}
if (!(u = PMOVMSKB(*(xmm_t *)p == *(xmm_t *)q) ^ 0xffff)) {
if (!(u = PMOVMSKB(*(xmm_t *)(p + n - 16) == *(xmm_t *)(q + n - 16)) ^
0xffff)) {
return 0;
} else {
u = __builtin_ctzl(u);
return p[n - 16 + u] - q[n - 16 + u];
}
} else {
u = __builtin_ctzl(u);
return p[u] - q[u];
}
}
_Microarchitecture("avx") static int memcmp_avx(const unsigned char *p,
const unsigned char *q,
size_t n) {
uint64_t w;
unsigned u;
if (n > 32) {
while (n >= 16 + 64) {
w = (uint64_t)PMOVMSKB(((xmm_t *)p)[0] == ((xmm_t *)q)[0]) << 000 |
(uint64_t)PMOVMSKB(((xmm_t *)p)[1] == ((xmm_t *)q)[1]) << 020 |
(uint64_t)PMOVMSKB(((xmm_t *)p)[2] == ((xmm_t *)q)[2]) << 040 |
(uint64_t)PMOVMSKB(((xmm_t *)p)[3] == ((xmm_t *)q)[3]) << 060;
if (w == -1) {
n -= 64;
p += 64;
q += 64;
} else {
w = __builtin_ctzll(w ^ -1);
return p[w] - q[w];
}
}
while (n > 16 + 16) {
if (!(u = PMOVMSKB(*(xmm_t *)p == *(xmm_t *)q) ^ 0xffff)) {
n -= 16;
p += 16;
q += 16;
} else {
u = __builtin_ctzl(u);
return p[u] - q[u];
}
}
}
if (!(u = PMOVMSKB(*(xmm_t *)p == *(xmm_t *)q) ^ 0xffff)) {
if (!(u = PMOVMSKB(*(xmm_t *)(p + n - 16) == *(xmm_t *)(q + n - 16)) ^
0xffff)) {
return 0;
} else {
u = __builtin_ctzl(u);
return p[n - 16 + u] - q[n - 16 + u];
}
} else {
u = __builtin_ctzl(u);
return p[u] - q[u];
}
}
#endif /* __x86_64__ */
/**
* Compares memory byte by byte.
*
* memcmp n=0 992 picoseconds
* memcmp n=1 1 ns/byte 738 mb/s
* memcmp n=2 661 ps/byte 1,476 mb/s
* memcmp n=3 551 ps/byte 1,771 mb/s
* memcmp n=4 248 ps/byte 3,936 mb/s
* memcmp n=5 198 ps/byte 4,920 mb/s
* memcmp n=6 165 ps/byte 5,904 mb/s
* memcmp n=7 141 ps/byte 6,889 mb/s
* memcmp n=8 124 ps/byte 7,873 mb/s
* memcmp n=9 110 ps/byte 8,857 mb/s
* memcmp n=15 44 ps/byte 22,143 mb/s
* memcmp n=16 41 ps/byte 23,619 mb/s
* memcmp n=17 77 ps/byte 12,547 mb/s
* memcmp n=31 42 ps/byte 22,881 mb/s
* memcmp n=32 41 ps/byte 23,619 mb/s
* memcmp n=33 60 ps/byte 16,238 mb/s
* memcmp n=80 53 ps/byte 18,169 mb/s
* memcmp n=128 38 ps/byte 25,194 mb/s
* memcmp n=256 32 ps/byte 30,233 mb/s
* memcmp n=16384 27 ps/byte 35,885 mb/s
* memcmp n=32768 29 ps/byte 32,851 mb/s
* memcmp n=131072 33 ps/byte 28,983 mb/s
* memcmp n=0 2 nanoseconds
* memcmp n=1 2 ns/byte 357 mb/s
* memcmp n=2 1 ns/byte 530 mb/s
* memcmp n=3 1 ns/byte 631 mb/s
* 𝗺𝗲𝗺𝗰𝗺𝗽 n=4 1 ns/byte 849 mb/s
* memcmp n=5 816 ps/byte 1,195 mb/s
* memcmp n=6 888 ps/byte 1,098 mb/s
* memcmp n=7 829 ps/byte 1,176 mb/s
* 𝗺𝗲𝗺𝗰𝗺𝗽 n=8 773 ps/byte 1,261 mb/s
* memcmp n=9 629 ps/byte 1,551 mb/s
* memcmp n=15 540 ps/byte 1,805 mb/s
* 𝗺𝗲𝗺𝗰𝗺𝗽 n=16 211 ps/byte 4,623 mb/s
* memcmp n=17 268 ps/byte 3,633 mb/s
* memcmp n=31 277 ps/byte 3,524 mb/s
* memcmp n=32 153 ps/byte 6,351 mb/s
* memcmp n=33 179 ps/byte 5,431 mb/s
* memcmp n=79 148 ps/byte 6,576 mb/s
* 𝗺𝗲𝗺𝗰𝗺𝗽 n=80 81 ps/byte 11 GB/s
* memcmp n=128 76 ps/byte 12 GB/s
* memcmp n=256 60 ps/byte 15 GB/s
* memcmp n=16384 51 ps/byte 18 GB/s
* memcmp n=32768 51 ps/byte 18 GB/s
* memcmp n=131072 52 ps/byte 18 GB/s
*
* @return an integer that's (1) equal to zero if `a` is equal to `b`,
* (2) less than zero if `a` is less than `b`, or (3) greater than
@ -137,62 +58,21 @@ _Microarchitecture("avx") static int memcmp_avx(const unsigned char *p,
*/
int memcmp(const void *a, const void *b, size_t n) {
int c;
unsigned u;
uint32_t k, i, j;
uint64_t w, x, y;
const unsigned char *p, *q;
if ((p = a) == (q = b) || !n) return 0;
if ((c = *p - *q)) return c;
#if defined(__x86_64__) && !defined(__chibicc__)
if (!IsTiny()) {
if (n <= 16) {
if (n >= 8) {
if (!(w = (x = ((uint64_t)p[0] << 000 | (uint64_t)p[1] << 010 |
(uint64_t)p[2] << 020 | (uint64_t)p[3] << 030 |
(uint64_t)p[4] << 040 | (uint64_t)p[5] << 050 |
(uint64_t)p[6] << 060 | (uint64_t)p[7] << 070)) ^
(y = ((uint64_t)q[0] << 000 | (uint64_t)q[1] << 010 |
(uint64_t)q[2] << 020 | (uint64_t)q[3] << 030 |
(uint64_t)q[4] << 040 | (uint64_t)q[5] << 050 |
(uint64_t)q[6] << 060 | (uint64_t)q[7] << 070)))) {
p += n - 8;
q += n - 8;
if (!(w = (x = ((uint64_t)p[0] << 000 | (uint64_t)p[1] << 010 |
(uint64_t)p[2] << 020 | (uint64_t)p[3] << 030 |
(uint64_t)p[4] << 040 | (uint64_t)p[5] << 050 |
(uint64_t)p[6] << 060 | (uint64_t)p[7] << 070)) ^
(y = ((uint64_t)q[0] << 000 | (uint64_t)q[1] << 010 |
(uint64_t)q[2] << 020 | (uint64_t)q[3] << 030 |
(uint64_t)q[4] << 040 | (uint64_t)q[5] << 050 |
(uint64_t)q[6] << 060 | (uint64_t)q[7] << 070)))) {
return 0;
}
}
u = __builtin_ctzll(w);
u = u & -8;
return ((x >> u) & 255) - ((y >> u) & 255);
} else if (n >= 4) {
if (!(k = (i = ((uint32_t)p[0] << 000 | (uint32_t)p[1] << 010 |
(uint32_t)p[2] << 020 | (uint32_t)p[3] << 030)) ^
(j = ((uint32_t)q[0] << 000 | (uint32_t)q[1] << 010 |
(uint32_t)q[2] << 020 | (uint32_t)q[3] << 030)))) {
p += n - 4;
q += n - 4;
if (!(k = (i = ((uint32_t)p[0] << 000 | (uint32_t)p[1] << 010 |
(uint32_t)p[2] << 020 | (uint32_t)p[3] << 030)) ^
(j = ((uint32_t)q[0] << 000 | (uint32_t)q[1] << 010 |
(uint32_t)q[2] << 020 | (uint32_t)q[3] << 030)))) {
return 0;
}
}
u = __builtin_ctzl(k);
u = u & -8;
return ((i >> u) & 255) - ((j >> u) & 255);
}
} else if (LIKELY(X86_HAVE(AVX))) {
return memcmp_avx(p, q, n);
unsigned u;
while (n >= 16 && (((uintptr_t)p & 0xfff) <= 0x1000 - 16 &&
((uintptr_t)q & 0xfff) <= 0x1000 - 16)) {
if (!(u = __builtin_ia32_pmovmskb128(*(xmm_t *)p == *(xmm_t *)q) ^
0xffff)) {
n -= 16;
p += 16;
q += 16;
} else {
return memcmp_sse(p, q, n);
u = __builtin_ctzl(u);
return p[u] - q[u];
}
}
#endif /* __x86_64__ */

View file

@ -18,6 +18,7 @@
*/
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/limits.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/str/str.h"
#ifndef __aarch64__
@ -75,7 +76,7 @@ void *memrchr(const void *s, int c, size_t n) {
r = memrchr_sse(s, c, n);
return (void *)r;
#else
return memrchr_pure(s, c, n);
return (void *)memrchr_pure(s, c, n);
#endif
}

View file

@ -20,7 +20,7 @@
#include "libc/macros.internal.h"
.init.start 200,_init__mmi
movb $OPEN_MAX,_mmi+8
movb $16,_mmi+8
movl $_mmi+24,_mmi+16
movb $PTHREAD_MUTEX_RECURSIVE,__mmi_lock_obj+4(%rip)
.init.end 200,_init__mmi

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/sigset.h"
#include "libc/limits.h"
#include "libc/sysv/consts/limits.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/sigset.h"
#include "libc/limits.h"
#include "libc/sysv/errfuns.h"
/**

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/sigset.h"
#include "libc/limits.h"
#include "libc/sysv/consts/limits.h"
#include "libc/sysv/errfuns.h"

View file

@ -107,14 +107,14 @@ dontasan char *strchr(const char *s, int c) {
unassert(!r || *r || !(c & 255));
return (char *)r;
#else
char *r;
const char *r;
for (c &= 255; (uintptr_t)s & 7; ++s) {
if ((*s & 255) == c) return s;
if ((*s & 255) == c) return (char *)s;
if (!*s) return NULL;
}
r = strchr_x64(s, c);
unassert(!r || *r || !c);
return r;
return (char *)r;
#endif
}

View file

@ -36,7 +36,7 @@
* @asyncsignalsafe
* @threadsafe
*/
privileged dontdiscard char *strsignal_r(int sig, char buf[21]) {
privileged char *strsignal_r(int sig, char buf[21]) {
const char *s;
if (!sig) {
return "0";

View file

@ -22,6 +22,7 @@
#include "libc/intrin/pushpop.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/limits.h"
#include "libc/log/color.internal.h"
#include "libc/log/internal.h"
#include "libc/log/libfatal.internal.h"
@ -218,7 +219,7 @@ static char *__ubsan_stpcpy(char *d, const char *s) {
}
}
dontdiscard static __ubsan_die_f *__ubsan_die(void) {
__wur static __ubsan_die_f *__ubsan_die(void) {
if (_weaken(__die)) {
return _weaken(__die);
} else {
@ -232,8 +233,8 @@ static void __ubsan_warning(const struct UbsanSourceLocation *loc,
loc->line, SUBTLE, description, RESET);
}
dontdiscard __ubsan_die_f *__ubsan_abort(const struct UbsanSourceLocation *loc,
const char *description) {
__wur __ubsan_die_f *__ubsan_abort(const struct UbsanSourceLocation *loc,
const char *description) {
kprintf("\n%s:%d: %subsan error%s: %s (tid %d)\n", loc->file, loc->line, RED2,
RESET, description, gettid());
return __ubsan_die();

View file

@ -5,6 +5,6 @@
#include "libc/fmt/fmt.h"
#include "libc/stdio/dprintf.h"
#include "libc/stdio/stdio.h"
#include "libc/stdio/temp.h"
#include "libc/temp.h"
#include "third_party/musl/tempnam.h"
#endif /* _STDIO_H */

View file

@ -10,9 +10,9 @@
#include "libc/runtime/runtime.h"
#include "libc/stdio/dprintf.h"
#include "libc/stdio/rand.h"
#include "libc/stdio/temp.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/exit.h"
#include "libc/temp.h"
#include "third_party/musl/crypt.h"
#include "third_party/musl/rand48.h"
#endif /* _STDLIB_H */

View file

@ -2,6 +2,11 @@
#define COSMOPOLITAN_LIBC_LIMITS_H_
#define __STDC_LIMIT_MACROS
#define CHAR_BIT 8
#define PATH_MAX 1024
#define NAME_MAX 255 /* 511 on netbsd */
#define ARG_MAX 0xfffe /* for argv and envp; see CreateProcess (32767*2) */
#define UCHAR_MIN 0
#define UCHAR_MAX 255

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/limits.h"
#include "libc/log/log.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"

View file

@ -18,6 +18,7 @@
*/
#include "libc/calls/calls.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/limits.h"
#include "libc/log/log.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"

View file

@ -27,7 +27,7 @@ int mergesort(void *, size_t, size_t, int (*)(const void *, const void *));
int mergesort_r(void *, size_t, size_t,
int (*)(const void *, const void *, void *), void *);
#define __algalloc returnspointerwithnoaliases dontthrow nocallback dontdiscard
#define __algalloc returnspointerwithnoaliases dontthrow nocallback __wur
int radix_sort_int32(int32_t *, size_t);
int radix_sort_int64(int64_t *, size_t);

View file

@ -30,7 +30,7 @@
* @return pointer that must be free()'d, or NULL w/ errno
* @threadsafe
*/
dontdiscard char *get_current_dir_name(void) {
char *get_current_dir_name(void) {
const char *p;
if ((p = getenv("PWD")) && _isabspath(p)) {
return strdup(p);

View file

@ -15,16 +15,16 @@ void free(void *) libcesque;
void *malloc(size_t) attributeallocsize((1)) mallocesque;
void *calloc(size_t, size_t) attributeallocsize((1, 2)) mallocesque;
void *memalign(size_t, size_t) attributeallocalign((1))
attributeallocsize((2)) returnspointerwithnoaliases libcesque dontdiscard;
attributeallocsize((2)) returnspointerwithnoaliases libcesque __wur;
void *realloc(void *, size_t) reallocesque;
void *realloc_in_place(void *, size_t) reallocesque;
void *reallocarray(void *, size_t, size_t) dontdiscard;
void *reallocarray(void *, size_t, size_t) __wur;
void *valloc(size_t) attributeallocsize((1)) vallocesque;
void *pvalloc(size_t) vallocesque;
char *strdup(const char *) paramsnonnull() mallocesque;
char *strndup(const char *, size_t) paramsnonnull() mallocesque;
void *aligned_alloc(size_t, size_t) attributeallocalign((1))
attributeallocsize((2)) returnspointerwithnoaliases libcesque dontdiscard;
attributeallocsize((2)) returnspointerwithnoaliases libcesque __wur;
int posix_memalign(void **, size_t, size_t);
int mallopt(int, int);
@ -34,7 +34,7 @@ size_t malloc_usable_size(void *);
void **independent_calloc(size_t, size_t, void **);
void **independent_comalloc(size_t, size_t *, void **);
wchar_t *wcsdup(const wchar_t *) strlenesque dontdiscard;
wchar_t *wcsdup(const wchar_t *) strlenesque __wur;
struct mallinfo {
size_t arena; /* non-mmapped space allocated from system */

View file

@ -1,48 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 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/nexgen32e/x86feature.h"
#include "libc/macros.internal.h"
.text.startup
// Initializes jump table for memset() and memcpy().
//
// @param !ZF if required cpu vector extensions are available
// @param rdi is address of 64-bit jump table
// @param rsi is address of 8-bit jump initializers
// @param rdx is address of indirect branch
// @param ecx is size of jump table
memjmpinit:
.leafprologue
setnz %r8b
shl %r8b
0: xor %eax,%eax
lodsb
add %rdx,%rax
stosq
.loop 0b
xor %eax,%eax
testb X86_HAVE(ERMS)+kCpuids(%rip)
setnz %al
or %r8b,%al
mov (%rsi,%rax),%al
add %rdx,%rax
stosq
lodsq
.leafepilogue
.endfn memjmpinit,globl,hidden

View file

@ -55,10 +55,10 @@ int64_t RegisterEventSource(const char16_t *lpUNCServerName,
const char16_t *lpSourceName);
int32_t DeregisterEventSource(uint64_t handle);
int64_t CreateEvent(struct NtSecurityAttributes *opt_lpEventAttributes,
int64_t CreateEvent(const struct NtSecurityAttributes *opt_lpEventAttributes,
bool32 bManualReset, bool32 bInitialState,
const char16_t *opt_lpName);
int64_t CreateEventEx(struct NtSecurityAttributes *lpEventAttributes,
int64_t CreateEventEx(const struct NtSecurityAttributes *lpEventAttributes,
const char16_t *lpName, uint32_t dwFlags,
uint32_t dwDesiredAccess);

View file

@ -71,7 +71,7 @@ bool32 SetCurrentDirectory(const char16_t *lpPathName);
uint32_t GetCurrentDirectory(uint32_t nBufferLength, char16_t *out_lpBuffer);
bool32 CreateDirectory(const char16_t *lpPathName,
struct NtSecurityAttributes *lpSecurityAttributes);
const struct NtSecurityAttributes *lpSecurityAttributes);
bool32 RemoveDirectory(const char16_t *lpPathName);
bool32 DuplicateHandle(int64_t hSourceProcessHandle, int64_t hSourceHandle,
@ -131,7 +131,7 @@ bool32 UnlockFileEx(int64_t hFile, uint32_t dwReserved,
bool32 CreateHardLink(const char16_t *lpFileName,
const char16_t *lpExistingFileName,
struct NtSecurityAttributes *reserved)
const struct NtSecurityAttributes *reserved)
paramsnonnull((1, 2));
bool32 CreateSymbolicLink(const char16_t *lpSymlinkFileName,
const char16_t *lpTargetPathName, uint32_t dwFlags)
@ -156,7 +156,7 @@ bool32 OpenProcessToken(int64_t hProcessHandle, uint32_t dwDesiredAccess,
bool32 DuplicateToken(int64_t hExistingTokenHandle, int dwImpersonationLevel,
int64_t *out_hDuplicateTokenHandle);
bool32 DuplicateTokenEx(int64_t hExistingToken, unsigned int dwDesiredAccess,
struct NtSecurityAttributes *lpTokenAttributes,
const struct NtSecurityAttributes *lpTokenAttributes,
int ImpersonationLevel, int TokenType,
int64_t *out_phNewToken);

View file

@ -81,12 +81,12 @@ bool32 OfferVirtualMemory(void *inout_VirtualAddress, size_t Size,
int Priority);
int64_t GetProcessHeap(void);
void *HeapAlloc(int64_t hHeap, uint32_t dwFlags, size_t dwBytes) dontdiscard;
void *HeapAlloc(int64_t hHeap, uint32_t dwFlags, size_t dwBytes) __wur;
bool32 HeapFree(int64_t hHeap, uint32_t dwFlags, void *opt_lpMem);
void *HeapReAlloc(int64_t hHeap, uint32_t dwFlags, void *lpMem,
size_t dwBytes) dontdiscard;
size_t dwBytes) __wur;
void *GlobalAlloc(uint32_t uFlags, uint64_t dwBytes) dontdiscard;
void *GlobalAlloc(uint32_t uFlags, uint64_t dwBytes) __wur;
void *GlobalFree(void *hMem);
#if ShouldUseMsabiAttribute()

View file

@ -36,8 +36,8 @@ COSMOPOLITAN_C_START_
bool32 CreateProcess(const char16_t *opt_lpApplicationName,
char16_t *lpCommandLine,
struct NtSecurityAttributes *opt_lpProcessAttributes,
struct NtSecurityAttributes *opt_lpThreadAttributes,
const struct NtSecurityAttributes *opt_lpProcessAttributes,
const struct NtSecurityAttributes *opt_lpThreadAttributes,
bool32 bInheritHandles, uint32_t dwCreationFlags,
void *opt_lpEnvironment,
const char16_t *opt_lpCurrentDirectory,

View file

@ -102,7 +102,7 @@ int RegConnectRegistryEx(const char16_t *lpMachineName, int64_t hKey,
int RegCreateKey(int64_t hKey, const char16_t *lpSubKey, int64_t *phkResult);
int RegCreateKeyEx(int64_t hKey, const char16_t *lpSubKey, uint32_t Reserved,
int16_t *lpClass, uint32_t dwOptions, int samDesired,
struct NtSecurityAttributes *lpSecurityAttributes,
const struct NtSecurityAttributes *lpSecurityAttributes,
int64_t *phkResult, uint32_t *lpdwDisposition);
int RegDeleteKey(int64_t hKey, const char16_t *lpSubKey);
int RegDeleteKeyEx(int64_t hKey, const char16_t *lpSubKey, int samDesired,
@ -127,7 +127,7 @@ int RegReplaceKey(int64_t hKey, const char16_t *lpSubKey,
const char16_t *lpNewFile, const char16_t *lpOldFile);
int RegRestoreKey(int64_t hKey, const char16_t *lpFile, uint32_t dwFlags);
int RegSaveKey(int64_t hKey, const char16_t *lpFile,
struct NtSecurityAttributes *lpSecurityAttributes);
const struct NtSecurityAttributes *lpSecurityAttributes);
int RegSetKeySecurity(int64_t hKey, uint32_t SecurityInformation,
void *pSecurityDescriptor);
int RegUnLoadKey(int64_t hKey, const char16_t *lpSubKey);

View file

@ -22,7 +22,7 @@
COSMOPOLITAN_C_START_
char16_t *GetCommandLine(void) nosideeffect;
char16_t *GetEnvironmentStrings(void) dontdiscard;
char16_t *GetEnvironmentStrings(void) __wur;
bool32 FreeEnvironmentStrings(char16_t *) paramsnonnull();
bool32 ReadFile(int64_t hFile, void *lpBuffer, uint32_t nNumberOfBytesToRead,
uint32_t *lpNumberOfBytesRead,

View file

@ -11,7 +11,7 @@ struct NtSecurityAttributes {
};
const char *DescribeNtSecurityAttributes(char[32],
struct NtSecurityAttributes *);
const struct NtSecurityAttributes *);
#define DescribeNtSecurityAttributes(x) \
DescribeNtSecurityAttributes(alloca(32), x)

View file

@ -75,15 +75,16 @@ bool32 RegisterWaitForSingleObject(int64_t *phNewWaitObject, int64_t hObject,
void *Context, uint32_t dwMilliseconds,
uint32_t dwFlags);
int64_t CreateWaitableTimer(struct NtSecurityAttributes *lpTimerAttributes,
bool32 bManualReset, const char16_t *lpTimerName);
int64_t CreateWaitableTimer(
const struct NtSecurityAttributes *lpTimerAttributes, bool32 bManualReset,
const char16_t *lpTimerName);
bool32 SetWaitableTimer(int64_t hTimer, const int64_t *lpDueTimeAsFtOrNegRela,
int32_t opt_lPeriodMs, NtTimerapcroutine opt_callback,
void *lpArgToCallback, bool32 fUnsleepSystem);
int64_t CreateSemaphore(struct NtSecurityAttributes *opt_lpSemaphoreAttributes,
uint32_t lInitialCount, uint32_t lMaximumCount,
const char16_t *opt_lpName);
int64_t CreateSemaphore(
const struct NtSecurityAttributes *opt_lpSemaphoreAttributes,
uint32_t lInitialCount, uint32_t lMaximumCount, const char16_t *opt_lpName);
int32_t ReleaseMutex(int64_t hMutex);
int32_t ReleaseSemaphore(int64_t hSemaphore, int32_t lReleaseCount,

View file

@ -30,7 +30,7 @@ COSMOPOLITAN_C_START_
cosmopolitan § new technology » threads
*/
int64_t CreateThread(struct NtSecurityAttributes *lpThreadAttributes,
int64_t CreateThread(const struct NtSecurityAttributes *lpThreadAttributes,
size_t dwStackSize, void *lpStartAddress,
void *lpParameter, uint32_t dwCreationFlags,
uint32_t *opt_lpThreadId);

View file

@ -291,7 +291,7 @@ struct NtInterfaceInfo {
*/
int32_t WSAStartup(uint16_t wVersionRequested, struct NtWsaData *lpWSAData)
paramsnonnull() dontdiscard;
paramsnonnull() __wur;
int WSACleanup(void);
int WSAGetLastError(void) nosideeffect;
@ -311,7 +311,7 @@ int __sys_select_nt(int, struct NtFdSet *, struct NtFdSet *, struct NtFdSet *,
uint64_t WSASocket(int af, int type, int protocol,
const struct NtWsaProtocolInfo *opt_lpProtocolInfo,
const uint32_t opt_group, uint32_t dwFlags) dontdiscard;
const uint32_t opt_group, uint32_t dwFlags) __wur;
int WSAConnect(uint64_t s, const struct sockaddr *name, const int namelen,
const struct NtIovec *opt_lpCallerData,
@ -340,8 +340,7 @@ bool32 WSAConnectByList(uint64_t s,
int64_t WSAAccept(uint64_t s, struct sockaddr *out_addr,
int32_t *opt_inout_addrlen,
const NtConditionProc opt_lpfnCondition,
const uint32_t *opt_dwCallbackData)
paramsnonnull((2)) dontdiscard;
const uint32_t *opt_dwCallbackData) paramsnonnull((2)) __wur;
bool32 AcceptEx(int64_t sListenSocket, int64_t sAcceptSocket,
void *out_lpOutputBuffer /*[recvlen+local+remoteaddrlen]*/,
@ -409,7 +408,7 @@ int WSANSPIoctl(int64_t hLookup, uint32_t dwControlCode,
const struct NtWsaCompletion *opt_lpCompletion)
paramsnonnull((3, 5, 7));
int64_t WSACreateEvent(void) dontdiscard;
int64_t WSACreateEvent(void) __wur;
bool32 WSACloseEvent(const int64_t hEvent);
bool32 WSAResetEvent(const int64_t hEvent);
bool32 WSASetEvent(const int64_t hEvent);

View file

@ -28,10 +28,10 @@
#include "libc/intrin/bits.h"
#include "libc/intrin/getenv.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/stdio/temp.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/lock.h"
#include "libc/sysv/consts/o.h"
@ -39,6 +39,7 @@
#include "libc/sysv/consts/s.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/timer.h"
#include "libc/temp.h"
#include "third_party/awk/cmd.h"
#include "third_party/getopt/getopt.internal.h"
#include "third_party/musl/glob.h"

Some files were not shown because too many files have changed in this diff Show more