Make ZipOS and Qemu work better

This change improves the dirstream library in a lot of respects,
especially for /zip/... files. Also turn off MAP_STACK on Aarch64
because Qemu seems to implement it differently than Linux and it's
probably responsible for a lot of mysterious crashes.
This commit is contained in:
Justine Tunney 2023-08-15 18:24:53 -07:00
parent 4658ae539f
commit 110559ce6a
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
48 changed files with 748 additions and 500 deletions

View file

@ -29,9 +29,9 @@
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/log/libfatal.internal.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
#include "libc/runtime/zipos.internal.h"
/**
* Replaces current process with program.
@ -74,7 +74,7 @@ int execve(const char *prog, char *const argv[], char *const envp[]) {
if (!rc) {
if (_weaken(__zipos_parseuri) &&
(_weaken(__zipos_parseuri)(prog, &uri) != -1)) {
rc = _weaken(__zipos_open)(&uri, O_RDONLY | O_CLOEXEC, 0);
rc = _weaken(__zipos_open)(&uri, O_RDONLY | O_CLOEXEC);
if (rc != -1) {
const int zipFD = rc;
strace_enabled(-1);

View file

@ -26,9 +26,9 @@
#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"
#include "libc/runtime/zipos.internal.h"
/**
* Checks if effective user can access path in particular ways.
@ -51,7 +51,7 @@
int faccessat(int dirfd, const char *path, int amode, int flags) {
int e, rc;
struct ZiposUri zipname;
if (!path || (IsAsan() && !__asan_is_valid_str(path))) {
if (IsAsan() && !__asan_is_valid_str(path)) {
rc = efault();
} else if (__isfdkind(dirfd, kFdZip)) {
rc = enotsup();

View file

@ -24,8 +24,8 @@
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/sysv/errfuns.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/errfuns.h"
/**
* Returns information about file, via open()'d descriptor.
@ -38,7 +38,9 @@
*/
int fstat(int fd, struct stat *st) {
int rc;
if (__isfdkind(fd, kFdZip)) {
if (IsAsan() && !__asan_is_valid(st, sizeof(*st))) {
rc = efault();
} else if (__isfdkind(fd, kFdZip)) {
rc = _weaken(__zipos_fstat)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, st);
} else if (!IsWindows() && !IsMetal()) {

View file

@ -29,10 +29,10 @@
#include "libc/intrin/weaken.h"
#include "libc/log/log.h"
#include "libc/mem/alloca.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
#include "libc/runtime/zipos.internal.h"
static inline const char *__strace_fstatat_flags(char buf[12], int flags) {
if (flags == AT_SYMLINK_NOFOLLOW) return "AT_SYMLINK_NOFOLLOW";
@ -56,7 +56,9 @@ int fstatat(int dirfd, const char *path, struct stat *st, int flags) {
/* execve() depends on this */
int rc;
struct ZiposUri zipname;
if (__isfdkind(dirfd, kFdZip)) {
if (IsAsan() && !__asan_is_valid(st, sizeof(*st))) {
rc = efault();
} else if (__isfdkind(dirfd, kFdZip)) {
STRACE("zipos dirfd not supported yet");
rc = einval();
} else if (_weaken(__zipos_stat) &&

View file

@ -25,8 +25,8 @@
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/log/backtrace.internal.h"
#include "libc/sysv/errfuns.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/errfuns.h"
/**
* Changes current position of file descriptor, e.g.

View file

@ -29,11 +29,11 @@
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/log/log.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
#include "libc/runtime/zipos.internal.h"
/**
* Opens file.
@ -166,7 +166,7 @@ int openat(int dirfd, const char *file, int flags, ...) {
if (_weaken(__zipos_open) &&
_weaken(__zipos_parseuri)(file, &zipname) != -1) {
if (!__vforked && dirfd == AT_FDCWD) {
rc = _weaken(__zipos_open)(&zipname, flags, mode);
rc = _weaken(__zipos_open)(&zipname, flags);
} else {
rc = enotsup(); /* TODO */
}

View file

@ -742,14 +742,6 @@ void abort(void) wontreturn;
#pragma GCC diagnostic error "-Wtrampolines"
#if __GNUC__ >= 6
#pragma GCC diagnostic error "-Wnonnull-compare"
#if defined(_COSMO_SOURCE) && !defined(MODE_DBG) && \
!defined(STACK_FRAME_UNLIMITED)
#pragma GCC diagnostic error "-Wframe-larger-than=131072"
#if __GNUC__ >= 9
// #pragma GCC diagnostic error "-Walloca-larger-than=1024"
// #pragma GCC diagnostic error "-Wvla-larger-than=1024"
#endif /* GCC 9+ */
#endif /* STACK_FRAME_UNLIMITED */
#elif __GNUC__ >= 9
#pragma GCC diagnostic error /* e.g. fabs not abs */ "-Wabsolute-value"
#endif /* GCC 6+ */

View file

@ -74,7 +74,7 @@
#define __BIGGEST_ALIGNMENT__ 16
#endif
#define APE_STACKSIZE 4194304 /* default 4mb stack */
#define APE_STACKSIZE 8388608 /* default 8mb stack */
#define APE_PAGESIZE 0x10000 /* i386+ */
#ifdef _COSMO_SOURCE
#define FRAMESIZE 0x10000

View file

@ -25,7 +25,7 @@
* @param delta is added to enabled state
* @return enabled state before `delta` was applied
*/
dontinstrument int ftrace_enabled(int delta) {
dontasan dontubsan dontinstrument int ftrace_enabled(int delta) {
int res;
struct CosmoTib *tib;
if (__tls_enabled) {

View file

@ -25,7 +25,7 @@
* @param delta is added to enabled state
* @return enabled state before `delta` was applied
*/
dontinstrument int strace_enabled(int delta) {
dontasan dontubsan dontinstrument int strace_enabled(int delta) {
int res;
struct CosmoTib *tib;
if (__tls_enabled) {

View file

@ -164,7 +164,7 @@ wontreturn textstartup void cosmo(long *sp, struct Syslib *m1) {
__switch_stacks(argc, argv, envp, auxv, cosmo2,
(char *)mmap(ape_stack_vaddr, (uintptr_t)ape_stack_memsz,
MAP_FIXED | PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_STACK, -1, 0) +
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) +
(uintptr_t)ape_stack_memsz);
}

View file

@ -54,7 +54,6 @@
#include "libc/sysv/consts/ss.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
#include "libc/zipos/zipos.internal.h"
#define MAP_ANONYMOUS_linux 0x00000020
#define MAP_ANONYMOUS_openbsd 0x00001000

View file

@ -18,10 +18,10 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/errfuns.h"
#include "libc/zip.internal.h"
#include "libc/runtime/zipos.internal.h"
// TODO: this should check parent directory components
@ -31,37 +31,30 @@
* @param uri is obtained via __zipos_parseuri()
* @asyncsignalsafe
*/
int __zipos_access(const struct ZiposUri *name, int amode) {
ssize_t cf;
int rc, mode;
int __zipos_access(struct ZiposUri *name, int amode) {
struct Zipos *z;
if ((z = __zipos_get()) && (cf = __zipos_find(z, name)) != -1) {
mode = GetZipCfileMode(z->map + cf);
if (amode == F_OK) {
rc = 0;
} else if (amode == R_OK) {
if (mode & 0444) {
rc = 0;
} else {
rc = eacces();
}
} else if (amode == W_OK) {
if (mode & 0222) {
rc = 0;
} else {
rc = eacces();
}
} else if (amode == X_OK) {
if (mode & 0111) {
rc = 0;
} else {
rc = eacces();
}
} else {
rc = einval();
}
} else {
rc = enoent();
if (!(z = __zipos_get())) {
return enoexec();
}
return rc;
ssize_t cf;
if ((cf = __zipos_find(z, name)) == -1) {
return enoent();
}
int mode = GetZipCfileMode(z->map + cf);
if (amode == F_OK) {
return 0;
}
if (amode & ~(R_OK | W_OK | X_OK)) {
return einval();
}
if (((amode & X_OK) && !(mode & 0111)) ||
((amode & W_OK) && !(mode & 0222)) ||
((amode & R_OK) && !(mode & 0444))) {
return eacces();
}
return 0;
}

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
@ -38,10 +37,10 @@ int __zipos_close(int fd) {
if (!IsWindows()) {
rc = sys_close(fd);
} else {
rc = 0; /* no system file descriptor needed on nt */
rc = 0; // no system file descriptor needed on nt
}
if (!__vforked) {
__zipos_free(__zipos_get(), h);
__zipos_free(h);
}
return rc;
}

View file

@ -17,34 +17,28 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
#include "libc/zip.internal.h"
#include "libc/runtime/zipos.internal.h"
#define ZIPOS __zipos_get()
#define HANDLE ((struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle)
int __zipos_fcntl(int fd, int cmd, uintptr_t arg) {
int rc;
if (cmd == F_GETFD) {
if (g_fds.p[fd].flags & O_CLOEXEC) {
rc = FD_CLOEXEC;
return FD_CLOEXEC;
} else {
rc = 0;
return 0;
}
} else if (cmd == F_SETFD) {
if (arg & FD_CLOEXEC) {
g_fds.p[fd].flags |= O_CLOEXEC;
rc = FD_CLOEXEC;
return FD_CLOEXEC;
} else {
g_fds.p[fd].flags &= ~O_CLOEXEC;
rc = 0;
return 0;
}
} else {
rc = einval();
return einval();
}
return rc;
}

View file

@ -16,39 +16,30 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "ape/relocations.h"
#include "libc/assert.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/zip.internal.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/zip.internal.h"
// TODO(jart): improve time complexity here
ssize_t __zipos_find(struct Zipos *zipos, const struct ZiposUri *name) {
const char *zname;
size_t i, n, c, znamesize;
ssize_t __zipos_find(struct Zipos *zipos, struct ZiposUri *name) {
if (!name->len) {
return 0;
return ZIPOS_SYNTHETIC_DIRECTORY;
}
c = GetZipCdirOffset(zipos->cdir);
n = GetZipCdirRecords(zipos->cdir);
for (i = 0; i < n; ++i, c += ZIP_CFILE_HDRSIZE(zipos->map + c)) {
npassert(ZIP_CFILE_MAGIC(zipos->map + c) == kZipCfileHdrMagic);
zname = ZIP_CFILE_NAME(zipos->map + c);
znamesize = ZIP_CFILE_NAMESIZE(zipos->map + c);
if ((name->len == znamesize && !memcmp(name->path, zname, name->len)) ||
(name->len + 1 == znamesize && !memcmp(name->path, zname, name->len) &&
zname[name->len] == '/')) {
bool found_subfile = false;
size_t c = GetZipCdirOffset(zipos->cdir);
size_t n = GetZipCdirRecords(zipos->cdir);
for (size_t i = 0; i < n; ++i, c += ZIP_CFILE_HDRSIZE(zipos->map + c)) {
const char *zname = ZIP_CFILE_NAME(zipos->map + c);
size_t zsize = ZIP_CFILE_NAMESIZE(zipos->map + c);
if ((name->len == zsize ||
(name->len + 1 == zsize && zname[name->len] == '/')) &&
!memcmp(name->path, zname, name->len)) {
return c;
} else if ((name->len < znamesize &&
!memcmp(name->path, zname, name->len) &&
zname[name->len - 1] == '/') ||
(name->len + 1 < znamesize &&
!memcmp(name->path, zname, name->len) &&
zname[name->len] == '/')) {
return 0;
} else if (name->len + 1 < zsize && zname[name->len] == '/' &&
!memcmp(name->path, zname, name->len)) {
found_subfile = true;
}
}
if (found_subfile) {
return ZIPOS_SYNTHETIC_DIRECTORY;
}
return -1;
}

View file

@ -16,9 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/sysv/errfuns.h"
#include "libc/zip.internal.h"
#include "libc/runtime/zipos.internal.h"
/**
@ -27,12 +24,6 @@
* @param uri is obtained via __zipos_parseuri()
* @asyncsignalsafe
*/
int __zipos_fstat(const struct ZiposHandle *h, struct stat *st) {
int rc;
if (st) {
rc = __zipos_stat_impl(__zipos_get(), h->cfile, st);
} else {
rc = efault();
}
return rc;
int __zipos_fstat(struct ZiposHandle *h, struct stat *st) {
return __zipos_stat_impl(h->zipos, h->cfile, st);
}

View file

@ -20,16 +20,17 @@
#include "libc/calls/metalfile.internal.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/promises.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/thread/thread.h"
#include "libc/zip.internal.h"
#include "libc/runtime/zipos.internal.h"
#ifdef __x86_64__
__static_yoink(APE_COM_NAME);
@ -81,7 +82,9 @@ struct Zipos *__zipos_get(void) {
}
if (fd != -1 || PLEDGED(RPATH)) {
if (fd == -1) {
progpath = GetProgramExecutableName();
if (!progpath) {
progpath = GetProgramExecutableName();
}
fd = open(progpath, O_RDONLY);
}
if (fd != -1) {

View file

@ -17,10 +17,39 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/stdckdint.h"
#include "libc/sysv/consts/s.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
#include "libc/zip.internal.h"
#include "libc/runtime/zipos.internal.h"
static int64_t __zipos_lseek_impl(struct ZiposHandle *h, int64_t offset,
unsigned whence) {
int64_t pos;
if (h->cfile == ZIPOS_SYNTHETIC_DIRECTORY ||
S_ISDIR(GetZipCfileMode(h->zipos->map + h->cfile))) {
return eisdir();
}
switch (whence) {
case SEEK_SET:
return offset;
case SEEK_CUR:
if (!ckd_add(&pos, h->pos, offset)) {
return pos;
} else {
return eoverflow();
}
case SEEK_END:
if (!ckd_sub(&pos, h->size, offset)) {
return pos;
} else {
return eoverflow();
}
default:
return einval();
}
}
/**
* Changes current position of zip file handle.
@ -31,27 +60,12 @@
* @asyncsignalsafe
*/
int64_t __zipos_lseek(struct ZiposHandle *h, int64_t offset, unsigned whence) {
int64_t rc;
int64_t pos;
if (offset < 0) return einval();
pthread_mutex_lock(&h->lock);
switch (whence) {
case SEEK_SET:
rc = offset;
break;
case SEEK_CUR:
rc = h->pos + offset;
break;
case SEEK_END:
rc = h->size - offset;
break;
default:
rc = -1;
break;
}
if (rc >= 0) {
h->pos = rc;
} else {
rc = einval();
if ((pos = __zipos_lseek_impl(h, offset, whence)) != -1) {
h->pos = pos;
}
pthread_mutex_unlock(&h->lock);
return rc;
return pos;
}

View file

@ -20,14 +20,15 @@
#include "libc/calls/struct/iovec.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/likely.h"
#include "libc/intrin/strace.internal.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/s.h"
#include "libc/sysv/errfuns.h"
#include "libc/zip.internal.h"
#define IP(X) (intptr_t)(X)
#define VIP(X) (void *)IP(X)
@ -50,25 +51,36 @@
*/
dontasan void *__zipos_mmap(void *addr, size_t size, int prot, int flags,
struct ZiposHandle *h, int64_t off) {
if (!(flags & MAP_PRIVATE) ||
(flags & ~(MAP_PRIVATE | MAP_FILE | MAP_FIXED | MAP_FIXED_NOREPLACE)) ||
(!!(flags & MAP_FIXED) ^ !!(flags & MAP_FIXED_NOREPLACE))) {
STRACE(
"zipos mappings currently only support MAP_PRIVATE with select flags");
if (off < 0) {
STRACE("negative zipos mmap offset");
return VIP(einval());
}
if (VERY_UNLIKELY(off < 0)) {
STRACE("neg off");
if (h->cfile == ZIPOS_SYNTHETIC_DIRECTORY ||
S_ISDIR(GetZipCfileMode(h->zipos->map + h->cfile))) {
return VIP(eisdir());
}
if (flags & (MAP_SHARED | MAP_ANONYMOUS)) {
STRACE("ZipOS bad flags");
return VIP(einval());
}
if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) {
STRACE("ZipOS bad protection");
return VIP(einval());
}
flags &= MAP_FIXED | MAP_FIXED_NOREPLACE;
flags |= MAP_PRIVATE | MAP_ANONYMOUS;
const int tempProt = !IsXnu() ? prot | PROT_WRITE : PROT_WRITE;
void *outAddr = __mmap_unlocked(addr, size, tempProt,
(flags & (~MAP_FILE)) | MAP_ANONYMOUS, -1, 0);
void *outAddr = __mmap_unlocked(addr, size, tempProt, flags, -1, 0);
if (outAddr == MAP_FAILED) {
return MAP_FAILED;
}
do {
if (__zipos_read(h, &(struct iovec){outAddr, size}, 1, off) == -1) {
strace_enabled(-1);
@ -82,6 +94,7 @@ dontasan void *__zipos_mmap(void *addr, size_t size, int prot, int flags,
}
return outAddr;
} while (0);
const int e = errno;
__munmap_unlocked(outAddr, size);
errno = e;

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 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
@ -16,22 +16,65 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/asancodes.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h"
#include "libc/runtime/zipos.internal.h"
void __zipos_free(struct Zipos *z, struct ZiposHandle *h) {
if (IsAsan()) {
__asan_poison((char *)h + sizeof(struct ZiposHandle),
h->mapsize - sizeof(struct ZiposHandle), kAsanHeapFree);
static size_t __zipos_trimpath(char *s, int *isabs) {
char *p = s, *q = s;
for (; *q; ++q) {
if (*q == '/') {
while (q[1] == '/') ++q;
if (q[1] == '.' && (q[2] == '/' || q[2] == '\0')) {
++q;
} else {
*p++ = '/';
}
} else {
*p++ = *q;
}
}
pthread_mutex_destroy(&h->lock);
__zipos_lock();
do h->next = z->freelist;
while (!_cmpxchg(&z->freelist, h->next, h));
__zipos_unlock();
if (s < p && p[-1] == '.' && p[-2] == '.' && (p - 2 == s || p[-3] == '/')) {
*p++ = '/';
}
*p = '\0';
if (isabs) {
*isabs = *s == '/';
}
return p - s;
}
size_t __zipos_normpath(char *s) {
int isabs;
char *p = s, *q = s;
__zipos_trimpath(s, &isabs);
if (!*s) return 0;
for (; *q != '\0'; ++q) {
if (q[0] == '/' && q[1] == '.' && q[2] == '.' &&
(q[3] == '/' || q[3] == '\0')) {
char *ep = p;
while (s < ep && *--ep != '/') donothing;
if (ep != p &&
(p[-1] != '.' || p[-2] != '.' || (s < p - 3 && p[-3] != '/'))) {
p = ep;
q += 2;
continue;
} else if (ep == s && isabs) {
q += 2;
continue;
}
}
if (q[0] != '/' || p != s || isabs) {
*p++ = *q;
}
}
if (p == s) {
*p++ = isabs ? '/' : '.';
}
if (p == s + 1 && s[0] == '.') {
*p++ = '/';
}
while (p - s > 1 && p[-1] == '/') {
--p;
}
*p = '\0';
return p - s;
}

View file

@ -23,6 +23,7 @@
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
@ -30,19 +31,21 @@
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/directmap.internal.h"
#include "libc/intrin/extend.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/s.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
#include "libc/zip.internal.h"
#include "libc/runtime/zipos.internal.h"
static char *mapend;
static size_t maptotal;
@ -60,6 +63,18 @@ static void *__zipos_mmap_space(size_t mapsize) {
return start + offset;
}
void __zipos_free(struct ZiposHandle *h) {
if (IsAsan()) {
__asan_poison((char *)h + sizeof(struct ZiposHandle),
h->mapsize - sizeof(struct ZiposHandle), kAsanHeapFree);
}
pthread_mutex_destroy(&h->lock);
__zipos_lock();
do h->next = h->zipos->freelist;
while (!_cmpxchg(&h->zipos->freelist, h->next, h));
__zipos_unlock();
}
static struct ZiposHandle *__zipos_alloc(struct Zipos *zipos, size_t size) {
size_t mapsize;
struct ZiposHandle *h, **ph;
@ -88,6 +103,7 @@ StartOver:
}
if (h) {
h->size = size;
h->zipos = zipos;
h->mapsize = mapsize;
pthread_mutex_init(&h->lock, 0);
}
@ -95,65 +111,63 @@ StartOver:
}
static int __zipos_mkfd(int minfd) {
int e, fd;
static bool demodernize;
if (!demodernize) {
e = errno;
if ((fd = __sys_fcntl(2, F_DUPFD_CLOEXEC, minfd)) != -1) {
return fd;
} else if (errno == EINVAL) {
demodernize = true;
errno = e;
} else {
return fd;
}
int fd, e = errno;
if ((fd = __sys_fcntl(2, F_DUPFD_CLOEXEC, minfd)) != -1) {
return fd;
} else if (errno == EINVAL) {
errno = e;
return __fixupnewfd(__sys_fcntl(2, F_DUPFD, minfd), O_CLOEXEC);
} else {
return fd;
}
if ((fd = __sys_fcntl(2, F_DUPFD, minfd)) != -1) {
__sys_fcntl(fd, F_SETFD, FD_CLOEXEC);
}
return fd;
}
static int __zipos_setfd(int fd, struct ZiposHandle *h, unsigned flags,
int mode) {
static int __zipos_setfd(int fd, struct ZiposHandle *h, unsigned flags) {
int want = fd;
atomic_compare_exchange_strong_explicit(
&g_fds.f, &want, fd + 1, memory_order_release, memory_order_relaxed);
g_fds.p[fd].kind = kFdZip;
g_fds.p[fd].handle = (intptr_t)h;
g_fds.p[fd].flags = flags | O_CLOEXEC;
g_fds.p[fd].mode = mode;
g_fds.p[fd].extra = 0;
__fds_unlock();
return fd;
}
static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
int mode) {
static int __zipos_load(struct Zipos *zipos, size_t cf, int flags,
struct ZiposUri *name) {
size_t lf;
size_t size;
int rc, fd, minfd;
struct ZiposHandle *h;
lf = GetZipCfileOffset(zipos->map + cf);
npassert((ZIP_LFILE_MAGIC(zipos->map + lf) == kZipLfileHdrMagic));
size = GetZipLfileUncompressedSize(zipos->map + lf);
switch (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
case kZipCompressionNone:
if (!(h = __zipos_alloc(zipos, 0))) return -1;
h->mem = ZIP_LFILE_CONTENT(zipos->map + lf);
break;
case kZipCompressionDeflate:
if (!(h = __zipos_alloc(zipos, size))) return -1;
if (!__inflate(h->data, size, ZIP_LFILE_CONTENT(zipos->map + lf),
GetZipLfileCompressedSize(zipos->map + lf))) {
h->mem = h->data;
} else {
h->mem = 0;
eio();
}
break;
default:
return eio();
if (cf == ZIPOS_SYNTHETIC_DIRECTORY) {
size = name->len;
if (!(h = __zipos_alloc(zipos, size + 1))) return -1;
if (size) memcpy(h->data, name->path, size);
h->data[size] = 0;
h->mem = h->data;
} else {
lf = GetZipCfileOffset(zipos->map + cf);
npassert((ZIP_LFILE_MAGIC(zipos->map + lf) == kZipLfileHdrMagic));
size = GetZipLfileUncompressedSize(zipos->map + lf);
switch (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
case kZipCompressionNone:
if (!(h = __zipos_alloc(zipos, 0))) return -1;
h->mem = ZIP_LFILE_CONTENT(zipos->map + lf);
break;
case kZipCompressionDeflate:
if (!(h = __zipos_alloc(zipos, size))) return -1;
if (!__inflate(h->data, size, ZIP_LFILE_CONTENT(zipos->map + lf),
GetZipLfileCompressedSize(zipos->map + lf))) {
h->mem = h->data;
} else {
h->mem = 0;
eio();
}
break;
default:
return eio();
}
}
h->pos = 0;
h->cfile = cf;
@ -164,7 +178,7 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
TryAgain:
if (IsWindows() || IsMetal()) {
if ((fd = __reservefd_unlocked(-1)) != -1) {
return __zipos_setfd(fd, h, flags, mode);
return __zipos_setfd(fd, h, flags);
}
} else if ((fd = __zipos_mkfd(minfd)) != -1) {
if (__ensurefds_unlocked(fd) != -1) {
@ -173,16 +187,45 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
minfd = fd + 1;
goto TryAgain;
}
return __zipos_setfd(fd, h, flags, mode);
return __zipos_setfd(fd, h, flags);
}
sys_close(fd);
}
__fds_unlock();
}
__zipos_free(zipos, h);
__zipos_free(h);
return -1;
}
static int __zipos_open_impl(struct ZiposUri *name, int flags) {
struct Zipos *zipos;
if (!(zipos = __zipos_get())) {
return enoexec();
}
ssize_t cf;
if ((cf = __zipos_find(zipos, name)) == -1) {
return enoent();
}
if ((flags & O_ACCMODE) != O_RDONLY || (flags & O_TRUNC)) {
return erofs();
}
if (flags & O_EXCL) {
return eexist();
}
if (cf != ZIPOS_SYNTHETIC_DIRECTORY) {
int mode = GetZipCfileMode(zipos->map + cf);
#if 0
if ((flags & O_DIRECTORY) && !S_ISDIR(mode)) {
return enotdir();
}
#endif
if (!(mode & 0444)) {
return eacces();
}
}
return __zipos_load(zipos, cf, flags, name);
}
/**
* Loads compressed file from αcτµαlly pδrταblε εxεcµταblε object store.
*
@ -190,33 +233,15 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
* @asyncsignalsafe
* @threadsafe
*/
int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
int __zipos_open(struct ZiposUri *name, int flags) {
int rc;
ssize_t cf;
struct Zipos *zipos;
if (_weaken(pthread_testcancel_np) &&
(rc = _weaken(pthread_testcancel_np)())) {
errno = rc;
return -1;
}
BLOCK_SIGNALS;
if ((flags & O_ACCMODE) == O_RDONLY) {
if ((zipos = __zipos_get())) {
if ((cf = __zipos_find(zipos, name)) != -1) {
rc = __zipos_load(zipos, cf, flags, mode);
assert(rc != 0);
} else {
rc = enoent();
assert(rc != 0);
}
} else {
rc = enoexec();
assert(rc != 0);
}
} else {
rc = einval();
assert(rc != 0);
}
rc = __zipos_open_impl(name, flags);
ALLOW_SIGNALS;
return rc;
}

View file

@ -16,19 +16,21 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/str/str.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/str/str.h"
/**
* Extracts information about ZIP URI if it is one.
*/
ssize_t __zipos_parseuri(const char *uri, struct ZiposUri *out) {
size_t len;
if ((uri[0] == '/' && uri[1] == 'z' && uri[2] == 'i' && uri[3] == 'p' &&
if ((uri[0] == '/' && //
uri[1] == 'z' && //
uri[2] == 'i' && //
uri[3] == 'p' && //
(!uri[4] || uri[4] == '/')) &&
(len = strlen(uri)) < PATH_MAX) {
out->path = uri + 4 + !!uri[4];
return (out->len = len - 4 - !!uri[4]);
strlcpy(out->path, uri + 4 + !!uri[4], ZIPOS_PATH_MAX) < ZIPOS_PATH_MAX) {
return (out->len = __zipos_normpath(out->path));
} else {
return -1;
}

View file

@ -18,11 +18,10 @@
*/
#include "libc/assert.h"
#include "libc/calls/struct/iovec.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h"
#include "libc/zip.internal.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/consts/s.h"
#include "libc/sysv/errfuns.h"
#include "libc/zip.internal.h"
static size_t GetIovSize(const struct iovec *iov, size_t iovlen) {
size_t i, r;
@ -30,6 +29,29 @@ static size_t GetIovSize(const struct iovec *iov, size_t iovlen) {
return r;
}
static ssize_t __zipos_read_impl(struct ZiposHandle *h, const struct iovec *iov,
size_t iovlen, ssize_t opt_offset) {
int i;
int64_t b, x, y;
if (h->cfile == ZIPOS_SYNTHETIC_DIRECTORY ||
S_ISDIR(GetZipCfileMode(h->zipos->map + h->cfile))) {
return eisdir();
}
if (opt_offset == -1) {
x = y = h->pos;
} else {
x = y = opt_offset;
}
for (i = 0; i < iovlen && y < h->size; ++i, y += b) {
b = MIN(iov[i].iov_len, h->size - y);
if (b) memcpy(iov[i].iov_base, h->mem + y, b);
}
if (opt_offset == -1) {
h->pos = y;
}
return y - x;
}
/**
* Reads data from zip store object.
*
@ -39,14 +61,10 @@ static size_t GetIovSize(const struct iovec *iov, size_t iovlen) {
*/
ssize_t __zipos_read(struct ZiposHandle *h, const struct iovec *iov,
size_t iovlen, ssize_t opt_offset) {
size_t i, b, x, y;
ssize_t rc;
unassert(opt_offset >= 0 || opt_offset == -1);
pthread_mutex_lock(&h->lock);
x = y = opt_offset != -1 ? opt_offset : h->pos;
for (i = 0; i < iovlen && y < h->size; ++i, y += b) {
b = min(iov[i].iov_len, h->size - y);
if (b) memcpy(iov[i].iov_base, h->mem + y, b);
}
if (opt_offset == -1) h->pos = y;
rc = __zipos_read_impl(h, iov, iovlen, opt_offset);
pthread_mutex_unlock(&h->lock);
return y - x;
return rc;
}

View file

@ -18,17 +18,19 @@
*/
#include "libc/calls/struct/stat.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/s.h"
#include "libc/sysv/errfuns.h"
#include "libc/zip.internal.h"
#include "libc/runtime/zipos.internal.h"
int __zipos_stat_impl(struct Zipos *zipos, size_t cf, struct stat *st) {
size_t lf;
if (zipos && st) {
bzero(st, sizeof(*st));
if (cf) {
if (cf == ZIPOS_SYNTHETIC_DIRECTORY) {
st->st_mode = S_IFDIR | 0555;
} else {
lf = GetZipCfileOffset(zipos->map + cf);
st->st_mode = GetZipCfileMode(zipos->map + cf);
st->st_size = GetZipLfileUncompressedSize(zipos->map + lf);
@ -37,8 +39,6 @@ int __zipos_stat_impl(struct Zipos *zipos, size_t cf, struct stat *st) {
GetZipCfileTimestamps(zipos->map + cf, &st->st_mtim, &st->st_atim,
&st->st_ctim, 0);
st->st_birthtim = st->st_ctim;
} else {
st->st_mode = 0444 | S_IFDIR | 0111;
}
return 0;
} else {

View file

@ -16,9 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/stdio/stdio.h"
#include "libc/sysv/errfuns.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/errfuns.h"
/**
* Reads file metadata from αcτµαlly pδrταblε εxεcµταblε object store.
@ -26,22 +25,10 @@
* @param uri is obtained via __zipos_parseuri()
* @asyncsignalsafe
*/
int __zipos_stat(const struct ZiposUri *name, struct stat *st) {
int rc;
int __zipos_stat(struct ZiposUri *name, struct stat *st) {
ssize_t cf;
struct Zipos *zipos;
if (st) {
if ((zipos = __zipos_get())) {
if ((cf = __zipos_find(zipos, name)) != -1) {
rc = __zipos_stat_impl(zipos, cf, st);
} else {
rc = enoent();
}
} else {
rc = enoexec();
}
} else {
rc = efault();
}
return rc;
if (!(zipos = __zipos_get())) return enoexec();
if ((cf = __zipos_find(zipos, name)) == -1) return enoent();
return __zipos_stat_impl(zipos, cf, st);
}

View file

@ -6,23 +6,29 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define ZIPOS_PATH_MAX 1024
#define ZIPOS_SYNTHETIC_DIRECTORY 0
struct stat;
struct iovec;
struct Zipos;
struct ZiposUri {
const char *path;
size_t len;
uint32_t len;
char path[ZIPOS_PATH_MAX];
};
struct ZiposHandle {
struct ZiposHandle *next;
pthread_mutex_t lock;
size_t size; /* byte length of `mem` */
size_t mapsize; /* total size of this struct */
size_t pos; /* read/write byte offset state */
uint32_t cfile; /* central directory entry rva */
uint8_t *mem; /* points to inflated data or uncompressed image */
uint8_t data[]; /* uncompressed file memory */
struct Zipos *zipos;
size_t size;
size_t mapsize;
size_t pos;
size_t cfile;
uint8_t *mem;
uint8_t data[];
};
struct Zipos {
@ -31,17 +37,18 @@ struct Zipos {
struct ZiposHandle *freelist;
};
int __zipos_close(int);
void __zipos_lock(void);
void __zipos_unlock(void);
int __zipos_close(int);
size_t __zipos_normpath(char *);
struct Zipos *__zipos_get(void) pureconst;
void __zipos_free(struct Zipos *, struct ZiposHandle *);
void __zipos_free(struct ZiposHandle *);
ssize_t __zipos_parseuri(const char *, struct ZiposUri *);
ssize_t __zipos_find(struct Zipos *, const struct ZiposUri *);
int __zipos_open(const struct ZiposUri *, unsigned, int);
int __zipos_access(const struct ZiposUri *, int);
int __zipos_stat(const struct ZiposUri *, struct stat *);
int __zipos_fstat(const struct ZiposHandle *, struct stat *);
ssize_t __zipos_find(struct Zipos *, struct ZiposUri *);
int __zipos_open(struct ZiposUri *, int);
int __zipos_access(struct ZiposUri *, int);
int __zipos_stat(struct ZiposUri *, struct stat *);
int __zipos_fstat(struct ZiposHandle *, struct stat *);
int __zipos_stat_impl(struct Zipos *, size_t, struct stat *);
ssize_t __zipos_read(struct ZiposHandle *, const struct iovec *, size_t,
ssize_t);

View file

@ -22,8 +22,9 @@
#include "libc/calls/struct/dirent.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/nopl.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
@ -32,6 +33,7 @@
#include "libc/nt/enum/filetype.h"
#include "libc/nt/files.h"
#include "libc/nt/struct/win32finddata.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/dt.h"
#include "libc/sysv/consts/o.h"
@ -39,7 +41,6 @@
#include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
#include "libc/zip.internal.h"
#include "libc/runtime/zipos.internal.h"
/**
* @fileoverview Directory Streams for Linux+Mac+Windows+FreeBSD+OpenBSD.
@ -57,15 +58,17 @@ int sys_getdents(unsigned, void *, unsigned, long *);
* Directory stream object.
*/
struct dirstream {
int fd;
bool iszip;
int64_t fd;
int64_t hand;
int64_t tell;
char16_t *name;
pthread_mutex_t lock;
struct {
uint64_t offset;
uint64_t records;
uint8_t *prefix;
size_t prefixlen;
uint8_t prefix[ZIPOS_PATH_MAX];
} zip;
struct dirent ent;
union {
@ -76,7 +79,6 @@ struct dirstream {
};
struct {
bool isdone;
char16_t *name;
struct NtWin32FindData windata;
};
};
@ -148,7 +150,7 @@ static textwindows DIR *opendir_nt_impl(char16_t *name, size_t len) {
}
name[len] = u'\0';
if ((res = calloc(1, sizeof(DIR)))) {
if ((res->fd = FindFirstFile(name, &res->windata)) != -1) {
if ((res->hand = FindFirstFile(name, &res->windata)) != -1) {
return res;
}
__fix_enotdir(-1, name);
@ -160,35 +162,17 @@ static textwindows DIR *opendir_nt_impl(char16_t *name, size_t len) {
return NULL;
}
static textwindows dontinline DIR *opendir_nt(const char *path) {
int len;
DIR *res;
char16_t *name;
if (*path) {
if ((name = malloc(PATH_MAX * 2))) {
if ((len = __mkntpath(path, name)) != -1 &&
(res = opendir_nt_impl(name, len))) {
res->name = name;
return res;
}
free(name);
}
} else {
enoent();
}
return NULL;
}
static textwindows dontinline DIR *fdopendir_nt(int fd) {
DIR *res;
char16_t *name;
if (__isfdkind(fd, kFdFile)) {
if ((name = malloc(PATH_MAX * 2))) {
if ((name = calloc(1, PATH_MAX * 2))) {
if ((res = opendir_nt_impl(
name, GetFinalPathNameByHandle(
g_fds.p[fd].handle, name, PATH_MAX,
kNtFileNameNormalized | kNtVolumeNameDos)))) {
res->name = name;
res->fd = -1;
close(fd);
return res;
}
@ -235,86 +219,13 @@ static textwindows dontinline struct dirent *readdir_nt(DIR *dir) {
}
}
dir->ent.d_type = GetNtDirentType(&dir->windata);
dir->isdone = !FindNextFile(dir->fd, &dir->windata);
dir->isdone = !FindNextFile(dir->hand, &dir->windata);
return &dir->ent;
} else {
return NULL;
}
}
/**
* Opens directory, e.g.
*
* DIR *d;
* struct dirent *e;
* CHECK((d = opendir(path)));
* while ((e = readdir(d))) {
* printf("%s/%s\n", path, e->d_name);
* }
* LOGIFNEG1(closedir(d));
*
* @returns newly allocated DIR object, or NULL w/ errno
* @errors ENOENT, ENOTDIR, EACCES, EMFILE, ENFILE, ENOMEM
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if we needed to block and a signal was delivered instead
* @cancellationpoint
* @see glob()
*/
DIR *opendir(const char *name) {
DIR *res;
int fd, rc;
struct stat st;
struct Zipos *zip;
struct ZiposUri zipname;
if (_weaken(pthread_testcancel_np) &&
(rc = _weaken(pthread_testcancel_np)())) {
errno = rc;
return 0;
}
if (!name || (IsAsan() && !__asan_is_valid_str(name))) {
efault();
res = 0;
} else if (_weaken(__zipos_get) &&
_weaken(__zipos_parseuri)(name, &zipname) != -1) {
if (_weaken(__zipos_stat)(&zipname, &st) != -1) {
if (S_ISDIR(st.st_mode)) {
zip = _weaken(__zipos_get)();
if ((res = calloc(1, sizeof(DIR)))) {
res->iszip = true;
res->fd = -1;
res->zip.offset = GetZipCdirOffset(zip->cdir);
res->zip.records = GetZipCdirRecords(zip->cdir);
res->zip.prefix = malloc(zipname.len + 2);
if (zipname.len) {
memcpy(res->zip.prefix, zipname.path, zipname.len);
}
if (zipname.len && res->zip.prefix[zipname.len - 1] != '/') {
res->zip.prefix[zipname.len++] = '/';
}
res->zip.prefix[zipname.len] = '\0';
res->zip.prefixlen = zipname.len;
}
} else {
enotdir();
res = 0;
}
} else {
res = 0;
}
} else if (!IsWindows()) {
res = 0;
if ((fd = open(name, O_RDONLY | O_NOCTTY | O_DIRECTORY | O_CLOEXEC)) !=
-1) {
if (!(res = fdopendir(fd))) {
close(fd);
}
}
} else {
res = opendir_nt(name);
}
return res;
}
/**
* Creates directory object for file descriptor.
*
@ -324,19 +235,98 @@ DIR *opendir(const char *name) {
* @errors ENOMEM and fd is closed
*/
DIR *fdopendir(int fd) {
// allocate directory iterator object
DIR *dir;
if (!IsWindows()) {
if (!(dir = calloc(1, sizeof(*dir)))) return NULL;
dir->fd = fd;
} else {
dir = fdopendir_nt(fd);
if (!(dir = calloc(1, sizeof(*dir)))) {
return NULL;
}
// on unix, file descriptor isn't required to be tracked
dir->fd = fd;
if (!__isfdkind(fd, kFdZip)) {
if (IsWindows()) {
free(dir);
return fdopendir_nt(fd);
}
return dir;
}
dir->iszip = true;
// ensure open /zip/... file is a directory
struct ZiposHandle *h = (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle;
if (h->cfile != ZIPOS_SYNTHETIC_DIRECTORY &&
!S_ISDIR(GetZipCfileMode(h->zipos->map + h->cfile))) {
free(dir);
enotdir();
return 0;
}
// get path of this file descriptor and ensure trailing slash
size_t len;
char *name;
if (h->cfile != ZIPOS_SYNTHETIC_DIRECTORY) {
len = ZIP_CFILE_NAMESIZE(h->zipos->map + h->cfile);
name = ZIP_CFILE_NAME(h->zipos->map + h->cfile);
} else {
len = h->size;
name = (char *)h->data;
}
if (len + 2 > ZIPOS_PATH_MAX) {
free(dir);
enametoolong();
return 0;
}
if (len) memcpy(dir->zip.prefix, name, len);
if (len && dir->zip.prefix[len - 1] != '/') {
dir->zip.prefix[len++] = '/';
}
dir->zip.prefix[len] = 0;
dir->zip.prefixlen = len;
// setup state values for directory iterator
dir->zip.offset = GetZipCdirOffset(h->zipos->cdir);
dir->zip.records = GetZipCdirRecords(h->zipos->cdir);
return dir;
}
/**
* Opens directory, e.g.
*
* DIR *d;
* struct dirent *e;
* d = opendir(path);
* while ((e = readdir(d))) {
* printf("%s/%s\n", path, e->d_name);
* }
* closedir(d);
*
* @returns newly allocated DIR object, or NULL w/ errno
* @errors ENOENT, ENOTDIR, EACCES, EMFILE, ENFILE, ENOMEM
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if we needed to block and a signal was delivered instead
* @cancellationpoint
* @see glob()
*/
DIR *opendir(const char *name) {
int rc;
if (_weaken(pthread_testcancel_np) &&
(rc = _weaken(pthread_testcancel_np)())) {
errno = rc;
return 0;
}
int fd;
if ((fd = open(name, O_RDONLY | O_DIRECTORY | O_NOCTTY | O_CLOEXEC)) == -1) {
return 0;
}
DIR *res = fdopendir(fd);
if (!res) close(fd);
return res;
}
static struct dirent *readdir_impl(DIR *dir) {
size_t n;
long basep;
int rc, mode;
uint8_t *s, *p;
struct Zipos *zip;
@ -348,53 +338,68 @@ static struct dirent *readdir_impl(DIR *dir) {
if (dir->iszip) {
ent = 0;
zip = _weaken(__zipos_get)();
while (!ent && dir->tell < dir->zip.records) {
npassert(ZIP_CFILE_MAGIC(zip->map + dir->zip.offset) ==
kZipCfileHdrMagic);
s = ZIP_CFILE_NAME(zip->map + dir->zip.offset);
n = ZIP_CFILE_NAMESIZE(zip->map + dir->zip.offset);
if (dir->zip.prefixlen < n &&
!memcmp(dir->zip.prefix, s, dir->zip.prefixlen)) {
s += dir->zip.prefixlen;
n -= dir->zip.prefixlen;
p = memchr(s, '/', n);
if (!p || p + 1 - s == n) {
if (p + 1 - s == n) --n;
mode = GetZipCfileMode(zip->map + dir->zip.offset);
ent = (struct dirent *)dir->buf;
ent->d_ino++;
ent->d_off = dir->zip.offset;
ent->d_reclen = MIN(n, 255);
ent->d_type = S_ISDIR(mode) ? DT_DIR : DT_REG;
if (ent->d_reclen) {
memcpy(ent->d_name, s, ent->d_reclen);
}
ent->d_name[ent->d_reclen] = 0;
} else {
lastent = (struct dirent *)dir->buf;
n = p - s;
n = MIN(n, 255);
if (!lastent->d_ino || (n != lastent->d_reclen) ||
memcmp(lastent->d_name, s, n)) {
ent = lastent;
while (!ent && dir->tell < dir->zip.records + 2) {
if (!dir->tell) {
ent = (struct dirent *)dir->buf;
ent->d_ino++;
ent->d_off = dir->tell;
ent->d_reclen = 1;
ent->d_type = DT_DIR;
strcpy(ent->d_name, ".");
} else if (dir->tell == 1) {
ent = (struct dirent *)dir->buf;
ent->d_ino++;
ent->d_off = dir->tell;
ent->d_reclen = 2;
ent->d_type = DT_DIR;
strcpy(ent->d_name, "..");
} else {
s = ZIP_CFILE_NAME(zip->map + dir->zip.offset);
n = ZIP_CFILE_NAMESIZE(zip->map + dir->zip.offset);
if (dir->zip.prefixlen < n &&
!memcmp(dir->zip.prefix, s, dir->zip.prefixlen)) {
s += dir->zip.prefixlen;
n -= dir->zip.prefixlen;
p = memchr(s, '/', n);
if (!p || p + 1 - s == n) {
if (p + 1 - s == n) --n;
mode = GetZipCfileMode(zip->map + dir->zip.offset);
ent = (struct dirent *)dir->buf;
ent->d_ino++;
ent->d_off = -1;
ent->d_reclen = n;
ent->d_type = DT_DIR;
ent->d_off = dir->tell;
ent->d_reclen = MIN(n, 255);
ent->d_type = S_ISDIR(mode) ? DT_DIR : DT_REG;
if (ent->d_reclen) {
memcpy(ent->d_name, s, ent->d_reclen);
}
ent->d_name[ent->d_reclen] = 0;
} else {
lastent = (struct dirent *)dir->buf;
n = p - s;
n = MIN(n, 255);
if (!lastent->d_ino || (n != lastent->d_reclen) ||
memcmp(lastent->d_name, s, n)) {
ent = lastent;
mode = GetZipCfileMode(zip->map + dir->zip.offset);
ent->d_ino++;
ent->d_off = dir->tell;
ent->d_reclen = n;
ent->d_type = S_ISDIR(mode) ? DT_DIR : DT_REG;
if (ent->d_reclen) {
memcpy(ent->d_name, s, ent->d_reclen);
}
ent->d_name[ent->d_reclen] = 0;
}
}
}
dir->zip.offset += ZIP_CFILE_HDRSIZE(zip->map + dir->zip.offset);
}
dir->zip.offset += ZIP_CFILE_HDRSIZE(zip->map + dir->zip.offset);
dir->tell++;
}
return ent;
} else if (!IsWindows()) {
if (dir->buf_pos >= dir->buf_end) {
basep = dir->tell; /* TODO(jart): what does xnu do */
long basep = dir->tell;
rc = sys_getdents(dir->fd, dir->buf, sizeof(dir->buf) - 256, &basep);
STRACE("sys_getdents(%d) → %d% m", dir->fd, rc);
if (!rc || rc == -1) return NULL;
@ -408,6 +413,7 @@ static struct dirent *readdir_impl(DIR *dir) {
} else if (IsOpenbsd()) {
obsd = (struct dirent_openbsd *)((char *)dir->buf + dir->buf_pos);
dir->buf_pos += obsd->d_reclen;
dir->tell = obsd->d_off;
ent = &dir->ent;
ent->d_ino = obsd->d_fileno;
ent->d_off = obsd->d_off;
@ -419,7 +425,7 @@ static struct dirent *readdir_impl(DIR *dir) {
dir->buf_pos += nbsd->d_reclen;
ent = &dir->ent;
ent->d_ino = nbsd->d_fileno;
ent->d_off = dir->tell++;
ent->d_off = (dir->tell += nbsd->d_reclen);
ent->d_reclen = nbsd->d_reclen;
ent->d_type = nbsd->d_type;
memcpy(ent->d_name, nbsd->d_name, MAX(256, nbsd->d_namlen + 1));
@ -428,7 +434,7 @@ static struct dirent *readdir_impl(DIR *dir) {
dir->buf_pos += bsd->d_reclen;
ent = &dir->ent;
ent->d_ino = bsd->d_fileno;
ent->d_off = IsXnu() ? (dir->tell = basep) : dir->tell++;
ent->d_off = dir->tell++;
ent->d_reclen = bsd->d_reclen;
ent->d_type = bsd->d_type;
memcpy(ent->d_name, bsd->d_name, bsd->d_namlen + 1);
@ -450,9 +456,14 @@ static struct dirent *readdir_impl(DIR *dir) {
*/
struct dirent *readdir(DIR *dir) {
struct dirent *e;
_lockdir(dir);
e = readdir_impl(dir);
_unlockdir(dir);
if (dir) {
_lockdir(dir);
e = readdir_impl(dir);
_unlockdir(dir);
} else {
efault();
e = 0;
}
return e;
}
@ -496,20 +507,18 @@ errno_t readdir_r(DIR *dir, struct dirent *output, struct dirent **result) {
* @return 0 on success or -1 w/ errno
*/
int closedir(DIR *dir) {
int rc;
int rc = 0;
if (dir) {
if (dir->iszip) {
free(dir->zip.prefix);
rc = 0;
} else if (!IsWindows()) {
rc = close(dir->fd);
} else {
free(dir->name);
rc = FindClose(dir->fd) ? 0 : __winerr();
if (dir->fd != -1) {
rc |= close(dir->fd);
}
free(dir->name);
if (IsWindows() && !dir->iszip) {
if (!FindClose(dir->hand)) {
rc = __winerr();
}
}
free(dir);
} else {
rc = 0;
}
return rc;
}
@ -533,13 +542,7 @@ long telldir(DIR *dir) {
int dirfd(DIR *dir) {
int rc;
_lockdir(dir);
if (dir->iszip) {
rc = eopnotsupp();
} else if (IsWindows()) {
rc = eopnotsupp();
} else {
rc = dir->fd;
}
rc = dir->fd;
_unlockdir(dir);
return rc;
}
@ -559,8 +562,8 @@ void rewinddir(DIR *dir) {
dir->tell = 0;
}
} else {
FindClose(dir->fd);
if ((dir->fd = FindFirstFile(dir->name, &dir->windata)) != -1) {
FindClose(dir->hand);
if ((dir->hand = FindFirstFile(dir->name, &dir->windata)) != -1) {
dir->isdone = false;
dir->tell = 0;
} else {
@ -582,11 +585,27 @@ void seekdir(DIR *dir, long off) {
if (dir->iszip) {
dir->zip.offset = GetZipCdirOffset(_weaken(__zipos_get)()->cdir);
for (i = 0; i < off && i < dir->zip.records; ++i) {
dir->zip.offset += ZIP_CFILE_HDRSIZE(zip->map + dir->zip.offset);
if (i >= 2) {
dir->zip.offset += ZIP_CFILE_HDRSIZE(zip->map + dir->zip.offset);
}
}
} else {
} else if (!IsWindows()) {
i = lseek(dir->fd, off, SEEK_SET);
dir->buf_pos = dir->buf_end = 0;
} else {
i = 0;
dir->isdone = false;
FindClose(dir->hand);
if ((dir->hand = FindFirstFile(dir->name, &dir->windata)) != -1) {
for (; i < off; ++i) {
if (!FindNextFile(dir->hand, &dir->windata)) {
dir->isdone = true;
break;
}
}
} else {
dir->isdone = true;
}
}
dir->tell = i;
_unlockdir(dir);

View file

@ -957,18 +957,6 @@ syscon pf PF_VSOCK 40 40 0 0 0 0 0 0
syscon pf PF_WANPIPE 25 25 0 0 0 0 0 0
syscon pf PF_X25 9 9 0 0 0 0 0 0
# getdents() constants
#
# group name GNU/Systemd GNU/Systemd (Aarch64) XNU's Not UNIX! MacOS (Arm64) FreeBSD OpenBSD NetBSD The New Technology Commentary
syscon dt DT_UNKNOWN 0 0 0 0 0 0 0 0 # consensus
syscon dt DT_FIFO 1 1 1 1 1 1 1 1 # unix consensus & faked nt
syscon dt DT_CHR 2 2 2 2 2 2 2 2 # unix consensus & faked nt
syscon dt DT_DIR 4 4 4 4 4 4 4 4 # unix consensus & faked nt
syscon dt DT_BLK 6 6 6 6 6 6 6 6 # unix consensus & faked nt
syscon dt DT_REG 8 8 8 8 8 8 8 8 # unix consensus & faked nt
syscon dt DT_LNK 10 10 10 10 10 10 10 10 # unix consensus & faked nt
syscon dt DT_SOCK 12 12 12 12 12 12 12 12 # unix consensus & faked nt
# msync() flags
#
# group name GNU/Systemd GNU/Systemd (Aarch64) XNU's Not UNIX! MacOS (Arm64) FreeBSD OpenBSD NetBSD The New Technology Commentary

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon dt,DT_BLK,6,6,6,6,6,6,6,6

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon dt,DT_CHR,2,2,2,2,2,2,2,2

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon dt,DT_DIR,4,4,4,4,4,4,4,4

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon dt,DT_FIFO,1,1,1,1,1,1,1,1

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon dt,DT_LNK,10,10,10,10,10,10,10,10

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon dt,DT_REG,8,8,8,8,8,8,8,8

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon dt,DT_SOCK,12,12,12,12,12,12,12,12

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon dt,DT_UNKNOWN,0,0,0,0,0,0,0,0

View file

@ -1,19 +1,5 @@
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_DT_H_
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_DT_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const uint8_t DT_UNKNOWN;
extern const uint8_t DT_FIFO;
extern const uint8_t DT_CHR;
extern const uint8_t DT_DIR;
extern const uint8_t DT_BLK;
extern const uint8_t DT_REG;
extern const uint8_t DT_LNK;
extern const uint8_t DT_SOCK;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#define DT_UNKNOWN 0
#define DT_FIFO 1