Rewrite ZipOS

This reduces the virtual memory usage of Emacs for me by 30%. We now
have a simpler implementation that uses read(), rather mmap()ing the
whole executable.
This commit is contained in:
Justine Tunney 2023-10-03 07:27:25 -07:00
parent ff77f2a6af
commit b01282e23e
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
21 changed files with 408 additions and 421 deletions

View file

@ -12,6 +12,7 @@
#include "dsp/tty/tty.h"
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/dirent.h"
#include "libc/calls/struct/itimerval.h"
#include "libc/calls/struct/winsize.h"
#include "libc/calls/termios.h"
@ -28,7 +29,6 @@
#include "libc/mem/arraylist2.internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sock/sock.h"
#include "libc/sock/struct/pollfd.h"
#include "libc/stdio/stdio.h"
@ -1808,19 +1808,12 @@ void GetOpts(int argc, char* argv[]) {
}
size_t FindZipGames(void) {
char* name;
size_t i, cf;
struct Zipos* zipos;
if ((zipos = __zipos_get())) {
for (i = 0, cf = ZIP_CDIR_OFFSET(zipos->cdir);
i < ZIP_CDIR_RECORDS(zipos->cdir);
++i, cf += ZIP_CFILE_HDRSIZE(zipos->map + cf)) {
if (ZIP_CFILE_NAMESIZE(zipos->map + cf) > 4 &&
!memcmp((ZIP_CFILE_NAME(zipos->map + cf) +
ZIP_CFILE_NAMESIZE(zipos->map + cf) - 4),
".nes", 4) &&
(name = xasprintf("/zip/%.*s", ZIP_CFILE_NAMESIZE(zipos->map + cf),
ZIP_CFILE_NAME(zipos->map + cf)))) {
DIR* dir;
if ((dir = opendir("/zip/usr/share/rom"))) {
struct dirent* ent;
while ((ent = readdir(dir))) {
if (endswith(ent->d_name, ".nes")) {
char* name = xasprintf("/zip/usr/share/rom/%s", ent->d_name);
APPEND(&zipgames_.p, &zipgames_.i, &zipgames_.n, &name);
}
}

View file

@ -28,9 +28,14 @@
#include "libc/sysv/consts/rlim.h"
#include "libc/sysv/consts/rlimit.h"
// Hack for guessing boundaries of _start()'s stack
// Hack for guessing the unknowable boundaries of _start()'s stack
//
// Every UNIX system in our support vector creates arg blocks like:
// This code always guesses correctly on Windows because WinMain()
// is written to allocate a stack ourself. Local testing on Linux,
// XNU, FreeBSD, OpenBSD, and NetBSD says that accuracy is ±1 page
// and that error rate applies to both beginning and end addresses
//
// Every UNIX system in our support vector creates arg blocks like
//
// <HIGHEST-STACK-ADDRESS>
// last environ string
@ -55,11 +60,6 @@
// up to the microprocessor page size (this computes the top addr)
// and the bottom is computed by subtracting RLIMIT_STACK rlim_cur
// It's simple but gets tricky if we consider environ can be empty
//
// This code always guesses correctly on Windows because WinMain()
// is written to allocate a stack ourself. Local testing on Linux,
// XNU, FreeBSD, OpenBSD, and NetBSD says that accuracy is ±1 page
// and that error rate applies to both beginning and end addresses
static char *__get_last(char **list) {
char *res = 0;

View file

@ -135,7 +135,6 @@ __msabi extern typeof(WriteFile) *const __imp_WriteFile;
// clang-format on
long __klog_handle;
extern struct SymbolTable *__symtab;
__funline char *kadvance(char *p, char *e, long n) {
intptr_t t = (intptr_t)p;
@ -755,11 +754,11 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt,
// can be manually consulted to look up the faulting code.
int idx;
x = va_arg(va, intptr_t);
if (_weaken(__symtab) && *_weaken(__symtab) &&
if (_weaken(__symtab) && _weaken(__symtab)->st &&
(idx = _weaken(__get_symbol)(0, x)) != -1) {
if (p + 1 <= e) *p++ = '&';
s = (*_weaken(__symtab))->name_base +
(*_weaken(__symtab))->names[idx];
s = _weaken(__symtab)->st->name_base +
_weaken(__symtab)->st->names[idx];
goto FormatString;
}
base = 4;

View file

@ -19,8 +19,6 @@
#include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.internal.h"
extern struct SymbolTable *__symtab;
/**
* Returns low index into symbol table for address.
*
@ -33,8 +31,8 @@ privileged int __get_symbol(struct SymbolTable *t, intptr_t a) {
// we don't want function tracing because:
// function tracing depends on this function via kprintf
unsigned l, m, r, n, k;
if (!t && __symtab) {
t = __symtab;
if (!t && __symtab.st) {
t = __symtab.st;
}
if (t) {
l = 0;

View file

@ -16,127 +16,89 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/intrin/bits.h"
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/cosmo.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/promises.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.internal.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h"
#include "libc/x/x.h"
#include "libc/zip.internal.h"
#include "third_party/puff/puff.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
__static_yoink("__get_symbol");
static pthread_spinlock_t g_lock;
struct SymbolTable *__symtab; // for kprintf
struct SymbolTableLoader __symtab;
static ssize_t GetZipFile(struct Zipos *zipos, const char *name) {
size_t i, n, c, z;
z = strlen(name);
c = GetZipCdirOffset(zipos->cdir);
n = GetZipCdirRecords(zipos->cdir);
for (i = 0; i < n; ++i, c += ZIP_CFILE_HDRSIZE(zipos->map + c)) {
if (ZIP_CFILE_NAMESIZE(zipos->map + c) == z &&
!memcmp(ZIP_CFILE_NAME(zipos->map + c), name, z)) {
return c;
}
}
return -1;
}
/**
* Reads symbol table from zip directory.
* @note This code can't depend on dlmalloc()
*/
static struct SymbolTable *GetSymbolTableFromZip(struct Zipos *zipos) {
ssize_t cf, lf;
size_t size, size2;
static struct SymbolTable *GetSymbolTableFromZip(void) {
int fd;
struct SymbolTable *res = 0;
if ((cf = GetZipFile(zipos, ".symtab." _ARCH_NAME)) != -1 ||
(cf = GetZipFile(zipos, ".symtab")) != -1) {
lf = GetZipCfileOffset(zipos->map + cf);
size = GetZipLfileUncompressedSize(zipos->map + lf);
size2 = ROUNDUP(size, FRAMESIZE);
if ((res = _mapanon(size2))) {
switch (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
case kZipCompressionNone:
memcpy(res, (void *)ZIP_LFILE_CONTENT(zipos->map + lf), size);
break;
case kZipCompressionDeflate:
if (__inflate((void *)res, size,
(void *)ZIP_LFILE_CONTENT(zipos->map + lf),
GetZipLfileCompressedSize(zipos->map + lf))) {
munmap(res, size2);
res = 0;
}
break;
default:
munmap(res, size2);
res = 0;
break;
}
if ((fd = open("/zip/.symtab." _ARCH_NAME, O_RDONLY)) != -1 ||
(fd = open("/zip/.symtab", O_RDONLY)) != -1) {
void *map;
ssize_t size;
if ((size = lseek(fd, 0, SEEK_END)) != -1 &&
(map = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)) !=
MAP_FAILED) {
res = map;
}
close(fd);
}
STRACE("GetSymbolTableFromZip() → %p", res);
return res;
}
/**
* Reads symbol table from .com.dbg file.
* @note This code can't depend on dlmalloc()
*/
static struct SymbolTable *GetSymbolTableFromElf(void) {
const char *s;
if (PLEDGED(RPATH) && (s = FindDebugBinary())) {
return OpenSymbolTable(s);
const char *path;
if ((path = FindDebugBinary())) {
return OpenSymbolTable(path);
} else {
return 0;
}
}
static void GetSymbolTableInit(void) {
if (!PLEDGED(RPATH)) return;
int e = errno;
if ((__symtab.st = GetSymbolTableFromZip())) {
__symtab.st->names =
(uint32_t *)((char *)__symtab.st + __symtab.st->names_offset);
__symtab.st->name_base =
(char *)((char *)__symtab.st + __symtab.st->name_base_offset);
}
if (!__symtab.st) {
__symtab.st = GetSymbolTableFromElf();
}
errno = e;
}
/**
* Returns symbol table singleton.
*
* This uses multiple strategies to find the symbol table. The first
* strategy, depends on whether or not the following is linked:
*
* __static_yoink("__zipos_get");
* __static_yoink("zipos");
*
* In that case, the symbol table may be read from `/zip/.symtab` which
* is generated by `o//tool/build/symtab.com`. The second strategy is to
* look for the concomitant `.com.dbg` executable, which may very well
* be the one currently executing, or it could be placed in the same
* folder as your `.com` binary, or lastly, it could be explicitly
* specified via the `COMDBG` environment variable.
* In that case, the symbol table may be read from `/zip/.symtab.ARCH`
* or `/zip/.symtab` which are generated by `o//tool/build/symtab.com`
* or `o//tool/build/apelink.com`.
*
* Function tracing is disabled throughout the duration of this call.
* Backtraces and other core runtime functionality depend on this.
* The second strategy is to look for the ELF executable for the current
* program. If you're running a .com binary, it'll look for the .com.dbg
* file. If it's running the .com.dbg or .elf file, then it'll just read
* the symbols from itself. In other cases, you can explicitly specify a
* debug symbol binary via the `COMDBG` environment variable.
*
* When using pledge() security, it's recommended that this function get
* called *before* calling pledge() if it lacks the rpath promise, so it
* can load the symbols into memory before the filesystem goes away.
*
* @return symbol table, or NULL if not found
*/
struct SymbolTable *GetSymbolTable(void) {
struct Zipos *z;
if (pthread_spin_trylock(&g_lock)) return 0;
if (!__symtab && !__isworker) {
if (_weaken(__zipos_get) && (z = _weaken(__zipos_get)())) {
if ((__symtab = GetSymbolTableFromZip(z))) {
__symtab->names =
(uint32_t *)((char *)__symtab + __symtab->names_offset);
__symtab->name_base =
(char *)((char *)__symtab + __symtab->name_base_offset);
}
}
if (!__symtab) {
__symtab = GetSymbolTableFromElf();
}
}
pthread_spin_unlock(&g_lock);
return __symtab;
cosmo_once(&__symtab.once, GetSymbolTableInit);
return __symtab.st;
}

View file

@ -26,6 +26,12 @@ struct SymbolTable {
struct Symbol symbols[]; /* sorted and non-overlapping intervals */
};
struct SymbolTableLoader {
_Atomic(unsigned) once;
struct SymbolTable *st;
};
extern struct SymbolTableLoader __symtab;
struct SymbolTable *GetSymbolTable(void);
const char *FindComBinary(void);
const char *FindDebugBinary(void);

View file

@ -39,14 +39,14 @@ int __zipos_access(struct ZiposUri *name, int amode) {
return enoexec();
}
ssize_t cf;
int cf;
if ((cf = __zipos_find(z, name)) == -1) {
return -1;
}
int mode;
if (cf != ZIPOS_SYNTHETIC_DIRECTORY) {
mode = GetZipCfileMode(z->map + cf);
mode = GetZipCfileMode(z->cdir + cf);
} else {
mode = S_IFDIR | 0555;
}

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
@ -30,16 +31,14 @@
* @vforksafe
*/
int __zipos_close(int fd) {
int rc;
struct ZiposHandle *h;
h = (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle;
if (!IsWindows()) {
rc = sys_close(fd);
} else {
rc = 0; // no system file descriptor needed on nt
}
if (!__vforked) {
__zipos_free(h);
if (__vforked) {
sys_close(fd);
return 0;
}
struct ZiposHandle *h = (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle;
g_fds.p[fd].handle = h->handle;
g_fds.p[fd].kind = kFdFile;
int rc = close(fd);
__zipos_free(h);
return rc;
}

View file

@ -24,11 +24,11 @@
#include "libc/sysv/errfuns.h"
#include "libc/zip.internal.h"
static ssize_t __zipos_match(struct Zipos *z, struct ZiposUri *name, int len,
int i) {
size_t cfile = z->index[i];
const char *zname = ZIP_CFILE_NAME(z->map + cfile);
int zsize = ZIP_CFILE_NAMESIZE(z->map + cfile);
static int __zipos_match(struct Zipos *z, struct ZiposUri *name, int len,
int i) {
int cfile = z->index[i];
const char *zname = ZIP_CFILE_NAME(z->cdir + cfile);
int zsize = ZIP_CFILE_NAMESIZE(z->cdir + cfile);
if ((len == zsize || (len + 1 == zsize && zname[len] == '/')) &&
!memcmp(name->path, zname, len)) {
return cfile;
@ -40,7 +40,7 @@ static ssize_t __zipos_match(struct Zipos *z, struct ZiposUri *name, int len,
}
}
ssize_t __zipos_scan(struct Zipos *zipos, struct ZiposUri *name) {
int __zipos_scan(struct Zipos *zipos, struct ZiposUri *name) {
// strip trailing slash from search name
int len = name->len;
@ -55,12 +55,12 @@ ssize_t __zipos_scan(struct Zipos *zipos, struct ZiposUri *name) {
// binary search for leftmost name in central directory
int l = 0;
int r = zipos->records;
int r = zipos->cnt;
while (l < r) {
int m = (l & r) + ((l ^ r) >> 1); // floor((a+b)/2)
const char *xp = ZIP_CFILE_NAME(zipos->map + zipos->index[m]);
const char *xp = ZIP_CFILE_NAME(zipos->cdir + zipos->index[m]);
const char *yp = name->path;
int xn = ZIP_CFILE_NAMESIZE(zipos->map + zipos->index[m]);
int xn = ZIP_CFILE_NAMESIZE(zipos->cdir + zipos->index[m]);
int yn = len;
int n = MIN(xn, yn);
int c;
@ -78,25 +78,25 @@ ssize_t __zipos_scan(struct Zipos *zipos, struct ZiposUri *name) {
}
}
if (l < zipos->records) {
if (l < zipos->cnt) {
int dx;
size_t cfile = zipos->index[l];
const char *zname = ZIP_CFILE_NAME(zipos->map + cfile);
int zsize = ZIP_CFILE_NAMESIZE(zipos->map + cfile);
int cfile = zipos->index[l];
const char *zname = ZIP_CFILE_NAME(zipos->cdir + cfile);
int zsize = ZIP_CFILE_NAMESIZE(zipos->cdir + cfile);
if (zsize > len && (dx = '/' - (zname[len] & 255))) {
// since the index is asciibetical, we need to specially handle
// the case where, when searching for a directory, regular files
// exist whose names share the same prefix as the directory name.
dx = dx > +1 ? +1 : dx;
dx = dx < -1 ? -1 : dx;
for (l += dx; 0 <= l && l < zipos->records; l += dx) {
ssize_t cf;
for (l += dx; 0 <= l && l < zipos->cnt; l += dx) {
int cf;
if ((cf = __zipos_match(zipos, name, len, l)) != -1) {
return cf;
}
cfile = zipos->index[l];
zname = ZIP_CFILE_NAME(zipos->map + cfile);
zsize = ZIP_CFILE_NAMESIZE(zipos->map + cfile);
zname = ZIP_CFILE_NAME(zipos->cdir + cfile);
zsize = ZIP_CFILE_NAMESIZE(zipos->cdir + cfile);
if (zsize < len || (len && zname[len - 1] != name->path[len - 1])) {
break;
}
@ -112,8 +112,8 @@ ssize_t __zipos_scan(struct Zipos *zipos, struct ZiposUri *name) {
}
// support code for open(), stat(), and access()
ssize_t __zipos_find(struct Zipos *zipos, struct ZiposUri *name) {
ssize_t cf;
int __zipos_find(struct Zipos *zipos, struct ZiposUri *name) {
int cf;
if ((cf = __zipos_scan(zipos, name)) == -1) {
// test if parent component exists that isn't a directory
char *p;
@ -121,7 +121,7 @@ ssize_t __zipos_find(struct Zipos *zipos, struct ZiposUri *name) {
name->path[name->len = p - name->path] = 0;
if ((cf = __zipos_scan(zipos, name)) != -1 &&
cf != ZIPOS_SYNTHETIC_DIRECTORY &&
!S_ISDIR(GetZipCfileMode(zipos->map + cf))) {
!S_ISDIR(GetZipCfileMode(zipos->cdir + cf))) {
return enotdir();
}
}
@ -130,7 +130,7 @@ ssize_t __zipos_find(struct Zipos *zipos, struct ZiposUri *name) {
// test if we're opening "foo/" and "foo" isn't a directory
if (cf != ZIPOS_SYNTHETIC_DIRECTORY && //
name->len && name->path[name->len - 1] == '/' &&
!S_ISDIR(GetZipCfileMode(zipos->map + cf))) {
!S_ISDIR(GetZipCfileMode(zipos->cdir + cf))) {
return enotdir();
}
return cf;

View file

@ -16,25 +16,17 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/metalfile.internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/cosmo.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/promises.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
#include "libc/limits.h"
#include "libc/mem/alg.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/posix.h"
#include "libc/sysv/consts/prot.h"
#include "libc/thread/thread.h"
#include "libc/zip.internal.h"
@ -42,118 +34,162 @@
__static_yoink(APE_COM_NAME);
#endif
struct ZiposPlanner {
uint8_t buf[kZipLookbehindBytes];
struct stat st;
};
static struct Zipos __zipos;
static atomic_uint __zipos_once;
static void __zipos_dismiss(uint8_t *map, const uint8_t *cdir, long pg) {
uint64_t i, n, c, ef, lf, mo, lo, hi;
// determine the byte range of zip file content (excluding central dir)
c = GetZipCdirOffset(cdir);
n = GetZipCdirRecords(cdir);
for (lo = c, hi = i = 0; i < n; ++i, c += ZIP_CFILE_HDRSIZE(map + c)) {
lf = GetZipCfileOffset(map + c);
if (lf < lo) lo = lf;
ef = lf + ZIP_LFILE_HDRSIZE(map + lf) + GetZipLfileCompressedSize(map + lf);
if (ef > hi) hi = ef;
}
// unmap the executable portion beneath the local files
mo = ROUNDDOWN(lo, FRAMESIZE);
if (mo) munmap(map, mo);
// this is supposed to reduce our rss usage but does it really?
lo = ROUNDDOWN(lo, pg);
hi = MIN(ROUNDUP(hi, pg), ROUNDDOWN(c, pg));
if (hi > lo) {
posix_madvise(map + lo, hi - lo, POSIX_MADV_DONTNEED);
}
static void __zipos_wipe(void) {
pthread_mutex_init(&__zipos.lock, 0);
}
static int __zipos_compare_names(const void *a, const void *b, void *c) {
const size_t *x = (const size_t *)a;
const size_t *y = (const size_t *)b;
struct Zipos *z = (struct Zipos *)c;
int xn = ZIP_CFILE_NAMESIZE(z->map + *x);
int yn = ZIP_CFILE_NAMESIZE(z->map + *y);
void __zipos_lock(void) {
pthread_mutex_lock(&__zipos.lock);
}
void __zipos_unlock(void) {
pthread_mutex_unlock(&__zipos.lock);
}
static int __zipos_compare(const void *a, const void *b, void *c) {
uint8_t *cdir = (uint8_t *)c;
const int *x = (const int *)a;
const int *y = (const int *)b;
int xn = ZIP_CFILE_NAMESIZE(cdir + *x);
int yn = ZIP_CFILE_NAMESIZE(cdir + *y);
int n = MIN(xn, yn);
if (n) {
int res =
memcmp(ZIP_CFILE_NAME(z->map + *x), ZIP_CFILE_NAME(z->map + *y), n);
int res = memcmp(ZIP_CFILE_NAME(cdir + *x), ZIP_CFILE_NAME(cdir + *y), n);
if (res) return res;
}
return xn - yn; // xn and yn are 16-bit
}
// creates binary searchable array of file offsets to cdir records
static void __zipos_generate_index(struct Zipos *zipos) {
size_t c, i;
zipos->records = GetZipCdirRecords(zipos->cdir);
zipos->index = _mapanon(zipos->records * sizeof(size_t));
for (i = 0, c = GetZipCdirOffset(zipos->cdir); i < zipos->records;
++i, c += ZIP_CFILE_HDRSIZE(zipos->map + c)) {
zipos->index[i] = c;
static dontinline int __zipos_plan(int fd, struct ZiposPlanner *p) {
// get file size and dev/inode
// this might fail if a bad fd was passed via environment
if (fstat(fd, &p->st)) {
return kZipErrorOpenFailed;
}
// read the last 64kb of file
// the zip file format magic can be anywhere in there
int amt;
int64_t off;
if (p->st.st_size <= kZipLookbehindBytes) {
off = 0;
amt = p->st.st_size;
} else {
off = p->st.st_size - kZipLookbehindBytes;
amt = p->st.st_size - off;
}
if (pread(fd, p->buf, amt, off) != amt) {
return kZipErrorReadFailed;
}
// search backwards for the end-of-central-directory record
// the eocd (cdir) says where the central directory (cfile) array is located
// we consistency check some legacy fields, to be extra sure that it is eocd
int cnt = 0;
for (int i = amt - MIN(kZipCdirHdrMinSize, kZipCdir64LocatorSize); i; --i) {
uint32_t magic = READ32LE(p->buf + i);
if (magic == kZipCdir64LocatorMagic && i + kZipCdir64LocatorSize <= amt &&
pread(fd, p->buf, kZipCdirHdrMinSize,
ZIP_LOCATE64_OFFSET(p->buf + i)) == kZipCdirHdrMinSize &&
READ32LE(p->buf) == kZipCdir64HdrMagic &&
ZIP_CDIR64_RECORDS(p->buf) == ZIP_CDIR64_RECORDSONDISK(p->buf) &&
ZIP_CDIR64_RECORDS(p->buf) && ZIP_CDIR64_SIZE(p->buf) <= INT_MAX) {
cnt = ZIP_CDIR64_RECORDS(p->buf);
off = ZIP_CDIR64_OFFSET(p->buf);
amt = ZIP_CDIR64_SIZE(p->buf);
break;
}
if (magic == kZipCdirHdrMagic && i + kZipCdirHdrMinSize <= amt &&
ZIP_CDIR_RECORDS(p->buf + i) == ZIP_CDIR_RECORDSONDISK(p->buf + i) &&
ZIP_CDIR_RECORDS(p->buf + i) && ZIP_CDIR_SIZE(p->buf + i) <= INT_MAX) {
cnt = ZIP_CDIR_RECORDS(p->buf + i);
off = ZIP_CDIR_OFFSET(p->buf + i);
amt = ZIP_CDIR_SIZE(p->buf + i);
break;
}
}
if (cnt <= 0) {
return kZipErrorEocdNotFound;
}
// we'll store the entire central directory in memory
// in addition to a file name index that's bisectable
void *memory;
int cdirsize = amt;
size_t indexsize = cnt * sizeof(int);
size_t memorysize = indexsize + cdirsize;
if (!(memory = _mapanon(memorysize))) {
return kZipErrorMapFailed;
}
int *index = memory;
uint8_t *cdir = (uint8_t *)memory + indexsize;
// read the central directory
if (pread(fd, cdir, cdirsize, off) != cdirsize) {
return kZipErrorReadFailed;
}
// generate our file name index
// smoothsort() isn't the fastest algorithm, but it guarantees
// o(logn), won't smash the stack and doesn't depend on malloc
smoothsort_r(zipos->index, zipos->records, sizeof(size_t),
__zipos_compare_names, zipos);
int entry_index, entry_offset;
for (entry_index = entry_offset = 0;
entry_index < cnt && entry_offset + kZipCfileHdrMinSize <= cdirsize &&
entry_offset + ZIP_CFILE_HDRSIZE(cdir + entry_offset) <= cdirsize;
++entry_index, entry_offset += ZIP_CFILE_HDRSIZE(cdir + entry_offset)) {
index[entry_index] = entry_offset;
}
if (cnt != entry_index) {
return kZipErrorZipCorrupt;
}
smoothsort_r(index, cnt, sizeof(int), __zipos_compare, cdir);
// finally populate the global singleton
__zipos.cnt = cnt;
__zipos.cdir = cdir;
__zipos.index = index;
__zipos.dev = p->st.st_ino;
__zipos.cdirsize = cdirsize;
return kZipOk;
}
static dontinline int __zipos_setup(int fd) {
// allocates 64kb on the stack as scratch memory
// this should only be used from the main thread
#pragma GCC push_options
#pragma GCC diagnostic ignored "-Wframe-larger-than="
struct ZiposPlanner p;
CheckLargeStackAllocation(&p, sizeof(p));
#pragma GCC pop_options
return __zipos_plan(fd, &p);
}
static void __zipos_init(void) {
char *endptr;
const char *s;
struct stat st;
int x, fd, err, msg;
uint8_t *map, *cdir;
const char *progpath;
if (!(s = getenv("COSMOPOLITAN_DISABLE_ZIPOS"))) {
// this environment variable may be a filename or file descriptor
if ((progpath = getenv("COSMOPOLITAN_INIT_ZIPOS")) &&
(x = strtol(progpath, &endptr, 10)) >= 0 && !*endptr) {
fd = x;
} else {
fd = -1;
}
if (fd != -1 || PLEDGED(RPATH)) {
if (fd == -1) {
if (!progpath) {
progpath = GetProgramExecutableName();
}
fd = open(progpath, O_RDONLY);
}
if (fd != -1) {
if (!fstat(fd, &st) && (map = mmap(0, st.st_size, PROT_READ, MAP_SHARED,
fd, 0)) != MAP_FAILED) {
if ((cdir = GetZipEocd(map, st.st_size, &err))) {
long pagesz = getauxval(AT_PAGESZ);
__zipos_dismiss(map, cdir, pagesz);
__zipos.map = map;
__zipos.cdir = cdir;
__zipos.dev = st.st_ino;
__zipos.pagesz = pagesz;
__zipos_generate_index(&__zipos);
msg = kZipOk;
} else {
munmap(map, st.st_size);
msg = !cdir ? err : kZipErrorRaceCondition;
}
} else {
msg = kZipErrorMapFailed;
}
close(fd);
} else {
msg = kZipErrorOpenFailed;
}
} else {
msg = -666;
}
} else {
progpath = 0;
msg = -777;
int status;
if (getenv("COSMOPOLITAN_DISABLE_ZIPOS")) return;
if (!(__zipos.progpath = getenv("COSMOPOLITAN_INIT_ZIPOS"))) {
__zipos.progpath = GetProgramExecutableName();
}
(void)msg;
STRACE("__zipos_get(%#s) → %d% m", progpath, msg);
int fd = open(__zipos.progpath, O_RDONLY);
if (fd != -1) {
if (!(status = __zipos_setup(fd))) {
__zipos_wipe();
pthread_atfork(__zipos_lock, __zipos_unlock, __zipos_wipe);
}
close(fd);
} else {
status = kZipErrorOpenFailed;
}
(void)status;
STRACE("__zipos_init(%#s) → %d% m", __zipos.progpath, status);
}
/**
@ -161,6 +197,6 @@ static void __zipos_init(void) {
* @asyncsignalsafe
*/
struct Zipos *__zipos_get(void) {
cosmo_once(&__zipos_once, __zipos_init);
return __zipos.cdir ? &__zipos : 0;
cosmo_once(&__zipos.once, __zipos_init);
return __zipos.cnt ? &__zipos : 0;
}

View file

@ -33,7 +33,6 @@ static uint64_t __zipos_fnv(const char *s, int len) {
uint64_t __zipos_inode(struct Zipos *zipos, int64_t cfile, //
const void *name, size_t namelen) {
unassert(cfile >= 0);
if (cfile == ZIPOS_SYNTHETIC_DIRECTORY) {
if (namelen && ((char *)name)[namelen - 1] == '/') --namelen;
cfile = INT64_MIN | __zipos_fnv(name, namelen);

View file

@ -50,7 +50,7 @@
* @return virtual base address of new mapping, or MAP_FAILED w/ errno
*/
void *__zipos_mmap(void *addr, size_t size, int prot, int flags,
struct ZiposHandle *h, int64_t off) {
struct ZiposHandle *h, int64_t off) {
if (off < 0) {
STRACE("negative zipos mmap offset");
@ -58,7 +58,7 @@ void *__zipos_mmap(void *addr, size_t size, int prot, int flags,
}
if (h->cfile == ZIPOS_SYNTHETIC_DIRECTORY ||
S_ISDIR(GetZipCfileMode(h->zipos->map + h->cfile))) {
S_ISDIR(GetZipCfileMode(h->zipos->cdir + h->cfile))) {
return VIP(eisdir());
}

View file

@ -16,68 +16,56 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/blocksigs.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#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/calls/struct/fd.internal.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/asancodes.h"
#include "libc/intrin/atomic.h"
#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/str/str.h"
#include "libc/sysv/consts/at.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/thread/tls.h"
#include "libc/zip.internal.h"
#include "third_party/zlib/zconf.h"
#include "third_party/zlib/zlib.h"
static char *__zipos_mapend;
static size_t __zipos_maptotal;
static pthread_mutex_t __zipos_lock_obj;
static void __zipos_wipe(void) {
pthread_mutex_init(&__zipos_lock_obj, 0);
static bool __zipos_method_is_supported(int method) {
switch (method) {
case kZipCompressionNone:
case kZipCompressionDeflate:
return true;
default:
return false;
}
}
static void __zipos_lock(void) {
pthread_mutex_lock(&__zipos_lock_obj);
}
static void __zipos_unlock(void) {
pthread_mutex_unlock(&__zipos_lock_obj);
}
static void *__zipos_mmap_space(size_t mapsize) {
static void *__zipos_mmap_space(struct Zipos *zipos, size_t mapsize) {
char *start;
size_t offset;
unassert(mapsize);
offset = __zipos_maptotal;
__zipos_maptotal += mapsize;
offset = zipos->maptotal;
zipos->maptotal += mapsize;
start = (char *)kMemtrackZiposStart;
if (!__zipos_mapend) __zipos_mapend = start;
__zipos_mapend = _extend(start, __zipos_maptotal, __zipos_mapend, MAP_PRIVATE,
kMemtrackZiposStart + kMemtrackZiposSize);
if (!zipos->mapend) zipos->mapend = start;
zipos->mapend = _extend(start, zipos->maptotal, zipos->mapend, MAP_PRIVATE,
kMemtrackZiposStart + kMemtrackZiposSize);
return start + offset;
}
void __zipos_free(struct ZiposHandle *h) {
if (!h) return;
if (IsAsan()) {
__asan_poison((char *)h + sizeof(struct ZiposHandle),
h->mapsize - sizeof(struct ZiposHandle), kAsanHeapFree);
@ -105,7 +93,7 @@ StartOver:
ph = &h->next;
}
if (!h) {
h = __zipos_mmap_space(mapsize);
h = __zipos_mmap_space(zipos, mapsize);
}
__zipos_unlock();
if (IsAsan()) {
@ -122,91 +110,85 @@ StartOver:
return h;
}
static int __zipos_mkfd(int minfd) {
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;
}
}
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].extra = 0;
__fds_unlock();
return fd;
}
static int __zipos_load(struct Zipos *zipos, size_t cf, int flags,
struct ZiposUri *name) {
size_t lf;
size_t size;
int fd, minfd;
static int __zipos_load_fd(struct Zipos *zipos, int cf, int fd,
struct ZiposUri *name,
struct ZiposHandle **out_handle) {
uint64_t size;
struct ZiposHandle *h;
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:
uint8_t lfile[kZipLfileHdrMinSize];
uint64_t lf = GetZipCfileOffset(zipos->cdir + cf);
int compressed = ZIP_CFILE_COMPRESSIONMETHOD(zipos->cdir + cf);
if (!__zipos_method_is_supported(compressed) ||
pread(fd, lfile, kZipLfileHdrMinSize, lf) != kZipLfileHdrMinSize ||
ZIP_LFILE_MAGIC(lfile) != kZipLfileHdrMagic) {
return eio(); // this corruption
}
size = GetZipCfileUncompressedSize(zipos->cdir + cf);
if (!(h = __zipos_alloc(zipos, size))) return -1;
uint64_t off = lf + ZIP_LFILE_HDRSIZE(lfile);
if (!compressed) {
if (pread(fd, h->data, size, off) != size) {
__zipos_free(h);
return eio();
}
} else {
struct ZiposHandle *h2;
uint64_t compsize = GetZipCfileCompressedSize(zipos->cdir + cf);
if (!(h2 = __zipos_alloc(zipos, compsize))) {
__zipos_free(h);
return -1;
}
if (pread(fd, h2->data, compsize, off) != compsize ||
__inflate(h->data, size, h2->data, compsize)) {
__zipos_free(h2);
__zipos_free(h);
return eio();
}
__zipos_free(h2);
}
}
h->pos = 0;
h->cfile = cf;
h->size = size;
if (h->mem) {
minfd = 3;
__fds_lock();
TryAgain:
if (IsWindows() || IsMetal()) {
if ((fd = __reservefd_unlocked(-1)) != -1) {
return __zipos_setfd(fd, h, flags);
*out_handle = h;
return 0;
}
static int __zipos_load(struct Zipos *zipos, int cf, int flags,
struct ZiposUri *name) {
int fd;
if ((fd = openat(AT_FDCWD, zipos->progpath,
O_RDONLY | O_CLOEXEC |
(flags & (O_NONBLOCK | O_RANDOM | O_SEQUENTIAL)),
0)) != -1) {
struct ZiposHandle *h = 0;
if (__zipos_load_fd(zipos, cf, fd, name, &h) != -1) {
if (!IsWindows() && !IsMetal()) {
// unix doesn't use the g_fds table by default, so we need to
// explicitly ensure an entry exists for our new open()d file
__ensurefds(fd);
}
} else if ((fd = __zipos_mkfd(minfd)) != -1) {
if (__ensurefds_unlocked(fd) != -1) {
if (g_fds.p[fd].kind) {
sys_close(fd);
minfd = fd + 1;
goto TryAgain;
}
return __zipos_setfd(fd, h, flags);
}
sys_close(fd);
// turn it from a file fd to a zip fd
// layer the handle on windows so it can be restored on close
h->handle = g_fds.p[fd].handle;
g_fds.p[fd].kind = kFdZip;
g_fds.p[fd].handle = (intptr_t)h;
g_fds.p[fd].flags &= ~O_CLOEXEC;
g_fds.p[fd].flags |= flags & O_CLOEXEC;
return fd;
} else {
close(fd);
return -1;
}
__fds_unlock();
} else {
return -1;
}
__zipos_free(h);
return -1;
}
/**
@ -231,6 +213,10 @@ int __zipos_open(struct ZiposUri *name, int flags) {
(flags & O_ACCMODE) != O_RDONLY) {
return erofs();
}
if ((flags & ~(O_CLOEXEC | O_NONBLOCK | O_NOFOLLOW | O_NOCTTY | O_DIRECTORY |
O_NOATIME | O_RANDOM | O_SEQUENTIAL))) {
return einval();
}
// get the zipos global singleton
struct Zipos *zipos;
@ -242,15 +228,12 @@ int __zipos_open(struct ZiposUri *name, int flags) {
// majority of these calls will return ENOENT or ENOTDIR. we need to
// perform two extremely costly sigprocmask() calls below. thanks to
// zipos being a read-only filesystem, we can avoid it in many cases
ssize_t cf;
int cf;
if ((cf = __zipos_find(zipos, name)) == -1) {
return -1;
}
if (flags & O_EXCL) {
return eexist();
}
if (cf != ZIPOS_SYNTHETIC_DIRECTORY) {
int mode = GetZipCfileMode(zipos->map + cf);
int mode = GetZipCfileMode(zipos->cdir + cf);
if ((flags & O_DIRECTORY) && !S_ISDIR(mode)) {
return enotdir();
}
@ -265,8 +248,3 @@ int __zipos_open(struct ZiposUri *name, int flags) {
ALLOW_SIGNALS;
return rc;
}
__attribute__((__constructor__)) static void __zipos_ctor(void) {
__zipos_wipe();
pthread_atfork(__zipos_lock, __zipos_unlock, __zipos_wipe);
}

View file

@ -29,7 +29,7 @@ static ssize_t __zipos_read_impl(struct ZiposHandle *h, const struct iovec *iov,
int i;
int64_t b, x, y;
if (h->cfile == ZIPOS_SYNTHETIC_DIRECTORY ||
S_ISDIR(GetZipCfileMode(h->zipos->map + h->cfile))) {
S_ISDIR(GetZipCfileMode(h->zipos->cdir + h->cfile))) {
return eisdir();
}
if (opt_offset == -1) {
@ -39,7 +39,7 @@ static ssize_t __zipos_read_impl(struct ZiposHandle *h, const struct iovec *iov,
}
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 (b) memcpy(iov[i].iov_base, h->data + y, b);
}
if (opt_offset == -1) {
h->pos = y;

View file

@ -25,8 +25,7 @@
#include "libc/sysv/consts/s.h"
#include "libc/zip.internal.h"
int __zipos_stat_impl(struct Zipos *zipos, size_t cf, struct stat *st) {
size_t lf;
int __zipos_stat_impl(struct Zipos *zipos, int cf, struct stat *st) {
bzero(st, sizeof(*st));
st->st_nlink = 1;
st->st_dev = zipos->dev;
@ -35,12 +34,11 @@ int __zipos_stat_impl(struct Zipos *zipos, size_t cf, struct stat *st) {
st->st_mode = S_IFDIR | (0555 & ~atomic_load_explicit(
&__umask, memory_order_acquire));
} else {
lf = GetZipCfileOffset(zipos->map + cf);
st->st_mode = GetZipCfileMode(zipos->map + cf);
st->st_size = GetZipLfileUncompressedSize(zipos->map + lf);
st->st_mode = GetZipCfileMode(zipos->cdir + cf);
st->st_size = GetZipCfileUncompressedSize(zipos->cdir + cf);
st->st_blocks =
roundup(GetZipLfileCompressedSize(zipos->map + lf), 512) / 512;
GetZipCfileTimestamps(zipos->map + cf, &st->st_mtim, &st->st_atim,
roundup(GetZipCfileCompressedSize(zipos->cdir + cf), 512) / 512;
GetZipCfileTimestamps(zipos->cdir + cf, &st->st_mtim, &st->st_atim,
&st->st_ctim, 0);
st->st_birthtim = st->st_ctim;
}

View file

@ -27,7 +27,7 @@
* @asyncsignalsafe
*/
int __zipos_stat(struct ZiposUri *name, struct stat *st) {
ssize_t cf;
int cf;
struct Zipos *zipos;
if (!(zipos = __zipos_get())) return enoexec();
if ((cf = __zipos_find(zipos, name)) == -1) return -1;

View file

@ -1,11 +1,12 @@
#ifndef COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_
#define COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_
#ifndef COSMOPOLITAN_ZIPOS_H_
#define COSMOPOLITAN_ZIPOS_H_
#include "libc/thread/thread.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define ZIPOS_PATH_MAX 1024
#define ZIPOS_SYNTHETIC_DIRECTORY 0
#define ZIPOS_SYNTHETIC_DIRECTORY -2
struct stat;
struct iovec;
@ -19,37 +20,43 @@ struct ZiposUri {
struct ZiposHandle {
struct ZiposHandle *next;
struct Zipos *zipos;
size_t size;
size_t mapsize;
size_t pos;
size_t cfile;
uint8_t *mem;
uint8_t data[];
int64_t handle; /* original upstream open()'d handle */
uint64_t size; /* accessible bytes stored at data[] */
uint64_t mapsize; /* the full byte size of this struct */
uint64_t pos; /* the file position, relative start */
int cfile; /* byte offset into zipos->cdir ents */
uint8_t data[]; /* original file content or dir name */
};
struct Zipos {
long pagesz;
uint8_t *map;
uint8_t *cdir;
uint64_t dev;
size_t *index;
size_t records;
_Atomic(unsigned) once;
int cnt; /* element count, accessible via `index` */
int cdirsize; /* number of bytes accessible via `cdir` */
uint64_t dev; /* remembers st_dev, of zipos image file */
int *index; /* points to cdirsize+cnt*4 mmap'd bytes */
uint8_t *cdir; /* points after index, in the above mmap */
const char *progpath;
struct ZiposHandle *freelist;
char *mapend;
size_t maptotal;
pthread_mutex_t lock;
};
int __zipos_close(int);
void __zipos_lock(void);
void __zipos_unlock(void);
void __zipos_free(struct ZiposHandle *);
struct Zipos *__zipos_get(void) pureconst;
size_t __zipos_normpath(char *, const char *, size_t);
ssize_t __zipos_find(struct Zipos *, struct ZiposUri *);
ssize_t __zipos_scan(struct Zipos *, struct ZiposUri *);
int __zipos_find(struct Zipos *, struct ZiposUri *);
int __zipos_scan(struct Zipos *, struct ZiposUri *);
ssize_t __zipos_parseuri(const char *, struct ZiposUri *);
uint64_t __zipos_inode(struct Zipos *, int64_t, const void *, size_t);
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 *);
int __zipos_stat_impl(struct Zipos *, int, struct stat *);
ssize_t __zipos_read(struct ZiposHandle *, const struct iovec *, size_t,
ssize_t);
int64_t __zipos_seek(struct ZiposHandle *, int64_t, unsigned);
@ -60,4 +67,4 @@ void *__zipos_mmap(void *, uint64_t, int32_t, int32_t, struct ZiposHandle *,
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_ */
#endif /* COSMOPOLITAN_ZIPOS_H_ */

View file

@ -77,8 +77,8 @@ struct dirstream {
struct {
struct Zipos *zipos;
uint64_t inode;
uint64_t offset;
uint64_t records;
int offset;
int records;
struct ZiposUri prefix;
struct critbit0 found;
} zip;
@ -298,7 +298,7 @@ DIR *fdopendir(int fd) {
// 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))) {
!S_ISDIR(GetZipCfileMode(h->zipos->cdir + h->cfile))) {
free(dir);
enotdir();
return 0;
@ -308,8 +308,8 @@ DIR *fdopendir(int fd) {
size_t len;
const 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);
len = ZIP_CFILE_NAMESIZE(h->zipos->cdir + h->cfile);
name = ZIP_CFILE_NAME(h->zipos->cdir + h->cfile);
} else {
len = h->size;
name = (const char *)h->data;
@ -328,8 +328,8 @@ DIR *fdopendir(int fd) {
// setup state values for directory iterator
dir->zip.zipos = h->zipos;
dir->zip.offset = GetZipCdirOffset(h->zipos->cdir);
dir->zip.records = GetZipCdirRecords(h->zipos->cdir);
dir->zip.offset = 0;
dir->zip.records = h->zipos->cnt;
dir->zip.inode = __zipos_inode(h->zipos, h->cfile, dir->zip.prefix.path,
dir->zip.prefix.len);
@ -397,8 +397,8 @@ static struct dirent *readdir_zipos(DIR *dir) {
ent->d_ino = __zipos_inode(
dir->zip.zipos, __zipos_scan(dir->zip.zipos, &p), p.path, p.len);
} else {
const char *s = ZIP_CFILE_NAME(dir->zip.zipos->map + dir->zip.offset);
size_t n = ZIP_CFILE_NAMESIZE(dir->zip.zipos->map + dir->zip.offset);
const char *s = ZIP_CFILE_NAME(dir->zip.zipos->cdir + dir->zip.offset);
size_t n = ZIP_CFILE_NAMESIZE(dir->zip.zipos->cdir + dir->zip.offset);
if (n > dir->zip.prefix.len &&
!memcmp(dir->zip.prefix.path, s, dir->zip.prefix.len)) {
s += dir->zip.prefix.len;
@ -408,7 +408,7 @@ static struct dirent *readdir_zipos(DIR *dir) {
if (p) {
n = p - s;
d_type = DT_DIR;
} else if (S_ISDIR(GetZipCfileMode(dir->zip.zipos->map +
} else if (S_ISDIR(GetZipCfileMode(dir->zip.zipos->cdir +
dir->zip.offset))) {
d_type = DT_DIR;
} else {
@ -425,7 +425,7 @@ static struct dirent *readdir_zipos(DIR *dir) {
}
}
dir->zip.offset +=
ZIP_CFILE_HDRSIZE(dir->zip.zipos->map + dir->zip.offset);
ZIP_CFILE_HDRSIZE(dir->zip.zipos->cdir + dir->zip.offset);
}
dir->tell++;
}
@ -611,7 +611,7 @@ void rewinddir(DIR *dir) {
if (dir->iszip) {
critbit0_clear(&dir->zip.found);
dir->tell = 0;
dir->zip.offset = GetZipCdirOffset(dir->zip.zipos->cdir);
dir->zip.offset = 0;
} else if (!IsWindows()) {
if (!lseek(dir->fd, 0, SEEK_SET)) {
dir->buf_pos = dir->buf_end = 0;
@ -637,7 +637,7 @@ void seekdir(DIR *dir, long tell) {
if (dir->iszip) {
critbit0_clear(&dir->zip.found);
dir->tell = 0;
dir->zip.offset = GetZipCdirOffset(dir->zip.zipos->cdir);
dir->zip.offset = 0;
while (dir->tell < tell) {
if (!readdir_zipos(dir)) {
break;

View file

@ -36,6 +36,8 @@
#define kZipErrorRaceCondition _ZE(-12)
#define kZipErrorMapFailed _ZE(-13)
#define kZipErrorOpenFailed _ZE(-14)
#define kZipErrorReadFailed _ZE(-15)
#define kZipErrorZipCorrupt _ZE(-16)
#define kZipCosmopolitanVersion kZipEra2001
@ -70,6 +72,8 @@
#define kZipCompressionNone 0
#define kZipCompressionDeflate 8
#define kZipLookbehindBytes 65536
#define kZipCdirHdrMagic ZM_(0x06054b50) /* PK♣♠ "PK\5\6" */
#define kZipCdirHdrMagicTodo ZM_(0x19184b50) /* PK♣♠ "PK\30\31" */
#define kZipCdirHdrMinSize 22

View file

@ -139,9 +139,9 @@ TEST(unveil, canBeUsedAgainAfterVfork) {
TEST(unveil, rwc_createExecutableFile_isAllowedButCantBeRun) {
SPAWN(fork);
ASSERT_SYS(0, 0, mkdir("folder", 0755));
testlib_extract("/zip/life.elf", "folder/life.elf", 0755);
ASSERT_SYS(0, 0, unveil("folder", "rwc"));
ASSERT_SYS(0, 0, unveil(0, 0));
testlib_extract("/zip/life.elf", "folder/life.elf", 0755);
SPAWN(fork);
ASSERT_SYS(0, 0, stat("folder/life.elf", &st));
ASSERT_SYS(EACCES, -1, execl("folder/life.elf", "folder/life.elf", 0));
@ -152,9 +152,9 @@ TEST(unveil, rwc_createExecutableFile_isAllowedButCantBeRun) {
TEST(unveil, rwcx_createExecutableFile_canAlsoBeRun) {
SPAWN(fork);
ASSERT_SYS(0, 0, mkdir("folder", 0755));
testlib_extract("/zip/life.elf", "folder/life.elf", 0755);
ASSERT_SYS(0, 0, unveil("folder", "rwcx"));
ASSERT_SYS(0, 0, unveil(0, 0));
testlib_extract("/zip/life.elf", "folder/life.elf", 0755);
SPAWN(fork);
ASSERT_SYS(0, 0, stat("folder/life.elf", &st));
execl("folder/life.elf", "folder/life.elf", 0);

View file

@ -52,7 +52,7 @@ void *Worker(void *arg) {
}
TEST(zipos, test) {
int i, n = 16;
int i, n = 20;
pthread_t *t = gc(malloc(sizeof(pthread_t) * n));
for (i = 0; i < n; ++i) {
ASSERT_SYS(0, 0, pthread_create(t + i, 0, Worker, 0));
@ -86,6 +86,14 @@ TEST(zipos, readPastEof) {
EXPECT_SYS(0, 0, close(3));
}
TEST(zipos, simple) {
char buf[31] = {0};
ASSERT_SYS(0, 3, open("/zip/libc/testlib/hyperion.txt", O_RDONLY));
ASSERT_SYS(0, 30, read(3, buf, 30));
ASSERT_STREQ("The fall of Hyperion - a Dream", buf);
ASSERT_SYS(0, 0, close(3));
}
TEST(zipos_O_DIRECTORY, blocksOpeningOfNormalFiles) {
ASSERT_SYS(ENOTDIR, -1,
open("/zip/libc/testlib/hyperion.txt", O_RDONLY | O_DIRECTORY));