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

Binary file not shown.

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

View file

@ -35,7 +35,6 @@ void SetUpOnce(void) {
}
TEST(access, efault) {
ASSERT_SYS(EFAULT, -1, access(0, F_OK));
if (IsWindows() || !IsAsan()) return; // not possible
ASSERT_SYS(EFAULT, -1, access((void *)77, F_OK));
}

View file

@ -71,6 +71,15 @@ TEST(read_pipe, canBeInterruptedByAlarm) {
close(fds[0]);
}
TEST(read_directory, eisdir) {
// TODO(jart): what
if (IsWindows() || IsFreebsd()) return;
ASSERT_SYS(0, 0, mkdir("boop", 0755));
ASSERT_SYS(0, 3, open("boop", O_RDONLY | O_DIRECTORY));
ASSERT_SYS(EISDIR, -1, read(3, 0, 0));
ASSERT_SYS(0, 0, close(3));
}
////////////////////////////////////////////////////////////////////////////////
BENCH(read, bench) {

View file

@ -21,6 +21,7 @@
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/metastat.internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/mem/gc.internal.h"
@ -47,13 +48,15 @@ TEST(stat_010, testEmptyFile_sizeIsZero) {
}
TEST(stat, enoent) {
ASSERT_SYS(ENOENT, -1, stat("hi", 0));
ASSERT_SYS(ENOENT, -1, stat("o/doesnotexist", 0));
struct stat st;
ASSERT_SYS(ENOENT, -1, stat("hi", &st));
ASSERT_SYS(ENOENT, -1, stat("o/doesnotexist", &st));
}
TEST(stat, enotdir) {
struct stat st;
ASSERT_SYS(0, 0, close(creat("yo", 0644)));
ASSERT_SYS(ENOTDIR, -1, stat("yo/there", 0));
ASSERT_SYS(ENOTDIR, -1, stat("yo/there", &st));
}
TEST(stat, zipos) {

View file

@ -17,9 +17,12 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/limits.h"
#include "libc/mem/gc.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/testlib/hyperion.h"
@ -53,3 +56,39 @@ TEST(zipos, test) {
for (i = 0; i < n; ++i) EXPECT_SYS(0, 0, _join(t + i));
__print_maps();
}
TEST(zipos, normpath) {
{
char s[] = "";
__zipos_normpath(s);
ASSERT_STREQ("", s);
}
{
char s[] = "usr/";
__zipos_normpath(s);
ASSERT_STREQ("usr", s);
}
{
char s[] = "usr/./";
__zipos_normpath(s);
ASSERT_STREQ("usr", s);
}
}
#if 0
TEST(zipos_O_DIRECTORY, blocksOpeningOfNormalFiles) {
ASSERT_SYS(ENOTDIR, -1,
open("/zip/libc/testlib/hyperion.txt", O_RDONLY | O_DIRECTORY));
}
#endif
TEST(zipos, readPastEof) {
char buf[512];
ASSERT_SYS(0, 3, open("/zip/libc/testlib/hyperion.txt", O_RDONLY));
EXPECT_SYS(EINVAL, -1, pread(3, buf, 512, UINT64_MAX));
EXPECT_SYS(0, 0, pread(3, buf, 512, INT64_MAX));
EXPECT_SYS(EINVAL, -1, lseek(3, UINT64_MAX, SEEK_SET));
EXPECT_SYS(0, INT64_MAX, lseek(3, INT64_MAX, SEEK_SET));
EXPECT_SYS(0, 0, read(3, buf, 512));
EXPECT_SYS(0, 0, close(3));
}

View file

@ -18,13 +18,18 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/dirent.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/mem/gc.h"
#include "libc/mem/gc.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/rand.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/dt.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/s.h"
#include "libc/testlib/testlib.h"
#include "libc/x/xasprintf.h"
#include "libc/x/xiso8601.h"
@ -61,6 +66,10 @@ TEST(opendir, enotdir) {
TEST(opendir, zipTest_fake) {
ASSERT_NE(NULL, (dir = opendir("/zip")));
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ(".", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("..", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("echo.com", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("usr", ent->d_name);
@ -70,6 +79,10 @@ TEST(opendir, zipTest_fake) {
EXPECT_EQ(0, closedir(dir));
ASSERT_NE(NULL, (dir = opendir("/zip/")));
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ(".", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("..", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("echo.com", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("usr", ent->d_name);
@ -79,11 +92,19 @@ TEST(opendir, zipTest_fake) {
EXPECT_EQ(0, closedir(dir));
ASSERT_NE(NULL, (dir = opendir("/zip/usr")));
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ(".", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("..", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("share", ent->d_name);
EXPECT_EQ(NULL, (ent = readdir(dir)));
EXPECT_EQ(0, closedir(dir));
ASSERT_NE(NULL, (dir = opendir("/zip/usr/")));
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ(".", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("..", ent->d_name);
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_STREQ("share", ent->d_name);
EXPECT_EQ(NULL, (ent = readdir(dir)));
EXPECT_EQ(0, closedir(dir));
@ -91,6 +112,28 @@ TEST(opendir, zipTest_fake) {
EXPECT_EQ(NULL, (dir = opendir("/zip/us/")));
}
TEST(opendir, openSyntheticDirEntry) {
struct stat st;
ASSERT_SYS(0, 3, open("/zip", O_RDONLY | O_DIRECTORY));
ASSERT_SYS(0, 0, fstat(3, &st));
ASSERT_TRUE(S_ISDIR(st.st_mode));
EXPECT_SYS(EISDIR, -1, read(3, 0, 0));
ASSERT_NE(NULL, (dir = fdopendir(3)));
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_EQ(0, closedir(dir));
}
TEST(opendir, openRealDirEntry) {
struct stat st;
ASSERT_SYS(0, 3, open("/zip/usr/share/zoneinfo", O_RDONLY | O_DIRECTORY));
ASSERT_SYS(0, 0, fstat(3, &st));
ASSERT_TRUE(S_ISDIR(st.st_mode));
EXPECT_SYS(EISDIR, -1, read(3, 0, 0));
ASSERT_NE(NULL, (dir = fdopendir(3)));
EXPECT_NE(NULL, (ent = readdir(dir)));
EXPECT_EQ(0, closedir(dir));
}
TEST(dirstream, testDots) {
int hasdot = 0;
int hasdotdot = 0;
@ -115,9 +158,9 @@ TEST(dirstream, test) {
bool hasfoo = false;
bool hasbar = false;
char *dpath, *file1, *file2;
dpath = _gc(xasprintf("%s.%d", "dirstream", rand()));
file1 = _gc(xasprintf("%s/%s", dpath, "foo"));
file2 = _gc(xasprintf("%s/%s", dpath, "bar"));
dpath = gc(xasprintf("%s.%d", "dirstream", rand()));
file1 = gc(xasprintf("%s/%s", dpath, "foo"));
file2 = gc(xasprintf("%s/%s", dpath, "bar"));
EXPECT_NE(-1, mkdir(dpath, 0755));
EXPECT_NE(-1, touch(file1, 0644));
EXPECT_NE(-1, touch(file2, 0644));
@ -143,12 +186,11 @@ TEST(dirstream, test) {
TEST(dirstream, zipTest) {
bool foundNewYork = false;
const char *path = "/zip/usr/share/zoneinfo/";
ASSERT_NE(0, _gc(xiso8601ts(NULL)));
ASSERT_NE(NULL, (dir = opendir(path)));
while ((ent = readdir(dir))) {
foundNewYork |= !strcmp(ent->d_name, "New_York");
}
closedir(dir);
EXPECT_SYS(0, 0, closedir(dir));
EXPECT_TRUE(foundNewYork);
}
@ -156,9 +198,9 @@ TEST(rewinddir, test) {
bool hasfoo = false;
bool hasbar = false;
char *dpath, *file1, *file2;
dpath = _gc(xasprintf("%s.%d", "dirstream", rand()));
file1 = _gc(xasprintf("%s/%s", dpath, "foo"));
file2 = _gc(xasprintf("%s/%s", dpath, "bar"));
dpath = gc(xasprintf("%s.%d", "dirstream", rand()));
file1 = gc(xasprintf("%s/%s", dpath, "foo"));
file2 = gc(xasprintf("%s/%s", dpath, "bar"));
EXPECT_NE(-1, mkdir(dpath, 0755));
EXPECT_NE(-1, touch(file1, 0644));
EXPECT_NE(-1, touch(file2, 0644));
@ -183,3 +225,28 @@ TEST(dirstream, zipTest_notDir) {
ASSERT_EQ(NULL, opendir("/zip/usr/share/zoneinfo/New_York"));
ASSERT_EQ(ENOTDIR, errno);
}
TEST(dirstream, seek) {
if (IsNetbsd()) return; // omg
ASSERT_SYS(0, 0, mkdir("boop", 0755));
EXPECT_SYS(0, 0, touch("boop/a", 0644));
EXPECT_SYS(0, 0, touch("boop/b", 0644));
EXPECT_SYS(0, 0, touch("boop/c", 0644));
ASSERT_NE(NULL, (dir = opendir("boop")));
ASSERT_NE(NULL, (ent = readdir(dir))); // #1
ASSERT_NE(NULL, (ent = readdir(dir))); // #2
long pos = telldir(dir);
ASSERT_NE(NULL, (ent = readdir(dir))); // #3
char name[32];
strlcpy(name, ent->d_name, sizeof(name));
ASSERT_NE(NULL, (ent = readdir(dir))); // #4
ASSERT_NE(NULL, (ent = readdir(dir))); // #5
ASSERT_EQ(NULL, (ent = readdir(dir))); // eod
seekdir(dir, pos);
ASSERT_NE(NULL, (ent = readdir(dir))); // #2
ASSERT_STREQ(name, ent->d_name);
ASSERT_NE(NULL, (ent = readdir(dir))); // #3
ASSERT_NE(NULL, (ent = readdir(dir))); // #4
ASSERT_EQ(NULL, (ent = readdir(dir))); // eod
ASSERT_SYS(0, 0, closedir(dir));
}

View file

@ -0,0 +1,69 @@
/*-*- 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/calls/struct/dirent.h"
#include "libc/mem/gc.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/dt.h"
#include "libc/testlib/testlib.h"
__static_yoink("zipos");
__static_yoink("libc/testlib/hyperion.txt");
__static_yoink("libc/testlib/moby.txt");
__static_yoink("usr/share/zoneinfo/New_York");
DIR *dir;
struct dirent *ent;
TEST(zipdir, test) {
const char *path = "/zip/libc/testlib///";
ASSERT_NE(NULL, (dir = opendir(path)));
ASSERT_EQ(0, telldir(dir));
ASSERT_NE(NULL, (ent = readdir(dir)));
ASSERT_EQ(0, strcmp(ent->d_name, "."));
ASSERT_EQ(DT_DIR, ent->d_type);
ASSERT_NE(NULL, (ent = readdir(dir)));
ASSERT_EQ(0, strcmp(ent->d_name, ".."));
ASSERT_EQ(DT_DIR, ent->d_type);
ASSERT_NE(NULL, (ent = readdir(dir)));
ASSERT_EQ(0, strcmp(ent->d_name, "hyperion.txt"));
ASSERT_EQ(DT_REG, ent->d_type);
long pos = telldir(dir);
ASSERT_NE(NULL, (ent = readdir(dir)));
ASSERT_EQ(0, strcmp(ent->d_name, "moby.txt"));
ASSERT_EQ(DT_REG, ent->d_type);
ASSERT_EQ(NULL, (ent = readdir(dir)));
seekdir(dir, pos);
ASSERT_NE(NULL, (ent = readdir(dir)));
ASSERT_EQ(0, strcmp(ent->d_name, "moby.txt"));
ASSERT_EQ(DT_REG, ent->d_type);
ASSERT_EQ(NULL, (ent = readdir(dir)));
ASSERT_SYS(0, 0, closedir(dir));
}
TEST(dirstream, hasDirectoryEntry) {
bool gotsome = false;
const char *path = "/zip/usr/share/zoneinfo";
ASSERT_NE(NULL, (dir = opendir(path)));
while ((ent = readdir(dir))) {
gotsome = true;
}
ASSERT_SYS(0, 0, closedir(dir));
EXPECT_TRUE(gotsome);
}

View file

@ -1330,7 +1330,7 @@ static int find_ecrec64(__G__ searchlen) /* return PK-class error */
if (memcmp((char *)byterec, end_central64_sig, 4) ) {
/* Zip64 EOCD Record not found */
/* Since we already have seen the Zip64 EOCD Locator, it's
possible we got here because there are bytes prepended
possible we got h-ere because there are bytes prepended
to the archive, like the sfx prefix. */
/* Make a guess as to where the Zip64 EOCD Record might be */

View file

@ -22,8 +22,11 @@
#include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/mem/gc.h"
#include "libc/mem/gc.internal.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/crc32.h"
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/stdio/rand.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/s.h"
@ -130,7 +133,7 @@ static void EmitZipCdirHdr(unsigned char *p, const void *name, size_t namesize,
/**
* Embeds zip file in elf object.
*/
void elfwriter_zip(struct ElfWriter *elf, const char *symbol, const char *name,
void elfwriter_zip(struct ElfWriter *elf, const char *symbol, const char *cname,
size_t namesize, const void *data, size_t size,
uint32_t mode, struct timespec mtim, struct timespec atim,
struct timespec ctim, bool nocompress) {
@ -144,6 +147,13 @@ void elfwriter_zip(struct ElfWriter *elf, const char *symbol, const char *name,
CHECK_NE(0, mtim.tv_sec);
char *name = gc(strndup(cname, namesize));
namesize = __zipos_normpath(name);
if (S_ISDIR(mode) && namesize && name[namesize - 1] != '/') {
name[namesize++] = '/';
name[namesize] = 0;
}
gflags = 0;
iattrs = 0;
compsize = size;