Revert "Rewrite ZipOS"

This reverts commit b01282e23e. Some tests
are broken. It's not clear how it'll impact metal yet. Let's revisit the
memory optimization benefits of this change again sometime soon.
This commit is contained in:
Justine Tunney 2023-10-03 14:40:03 -07:00
parent ee8a861635
commit ff250a0c10
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
21 changed files with 420 additions and 407 deletions

View file

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

View file

@ -28,14 +28,9 @@
#include "libc/sysv/consts/rlim.h" #include "libc/sysv/consts/rlim.h"
#include "libc/sysv/consts/rlimit.h" #include "libc/sysv/consts/rlimit.h"
// Hack for guessing the unknowable boundaries of _start()'s stack // Hack for guessing boundaries of _start()'s stack
// //
// This code always guesses correctly on Windows because WinMain() // Every UNIX system in our support vector creates arg blocks like:
// 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> // <HIGHEST-STACK-ADDRESS>
// last environ string // last environ string
@ -60,6 +55,11 @@
// up to the microprocessor page size (this computes the top addr) // up to the microprocessor page size (this computes the top addr)
// and the bottom is computed by subtracting RLIMIT_STACK rlim_cur // and the bottom is computed by subtracting RLIMIT_STACK rlim_cur
// It's simple but gets tricky if we consider environ can be empty // 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) { static char *__get_last(char **list) {
char *res = 0; char *res = 0;

View file

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

View file

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

View file

@ -16,89 +16,127 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/atomic.h" #include "libc/assert.h"
#include "libc/calls/calls.h" #include "libc/intrin/bits.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/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/runtime.h"
#include "libc/runtime/symbols.internal.h" #include "libc/runtime/symbols.internal.h"
#include "libc/sysv/consts/map.h" #include "libc/runtime/zipos.internal.h"
#include "libc/sysv/consts/o.h" #include "libc/str/str.h"
#include "libc/sysv/consts/prot.h" #include "libc/thread/thread.h"
#include "libc/x/x.h"
#include "libc/zip.internal.h"
#include "third_party/puff/puff.h"
__static_yoink("__get_symbol"); __static_yoink("__get_symbol");
struct SymbolTableLoader __symtab; static pthread_spinlock_t g_lock;
struct SymbolTable *__symtab; // for kprintf
static struct SymbolTable *GetSymbolTableFromZip(void) { static ssize_t GetZipFile(struct Zipos *zipos, const char *name) {
int fd; size_t i, n, c, z;
struct SymbolTable *res = 0; z = strlen(name);
if ((fd = open("/zip/.symtab." _ARCH_NAME, O_RDONLY)) != -1 || c = GetZipCdirOffset(zipos->cdir);
(fd = open("/zip/.symtab", O_RDONLY)) != -1) { n = GetZipCdirRecords(zipos->cdir);
void *map; for (i = 0; i < n; ++i, c += ZIP_CFILE_HDRSIZE(zipos->map + c)) {
ssize_t size; if (ZIP_CFILE_NAMESIZE(zipos->map + c) == z &&
if ((size = lseek(fd, 0, SEEK_END)) != -1 && !memcmp(ZIP_CFILE_NAME(zipos->map + c), name, z)) {
(map = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)) != return c;
MAP_FAILED) {
res = map;
} }
close(fd);
} }
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;
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;
}
}
}
STRACE("GetSymbolTableFromZip() → %p", res);
return res; return res;
} }
/**
* Reads symbol table from .com.dbg file.
* @note This code can't depend on dlmalloc()
*/
static struct SymbolTable *GetSymbolTableFromElf(void) { static struct SymbolTable *GetSymbolTableFromElf(void) {
const char *path; const char *s;
if ((path = FindDebugBinary())) { if (PLEDGED(RPATH) && (s = FindDebugBinary())) {
return OpenSymbolTable(path); return OpenSymbolTable(s);
} else { } else {
return 0; 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. * Returns symbol table singleton.
* *
* This uses multiple strategies to find the symbol table. The first * This uses multiple strategies to find the symbol table. The first
* strategy, depends on whether or not the following is linked: * strategy, depends on whether or not the following is linked:
* *
* __static_yoink("zipos"); * __static_yoink("__zipos_get");
* *
* In that case, the symbol table may be read from `/zip/.symtab.ARCH` * In that case, the symbol table may be read from `/zip/.symtab` which
* or `/zip/.symtab` which are generated by `o//tool/build/symtab.com` * is generated by `o//tool/build/symtab.com`. The second strategy is to
* or `o//tool/build/apelink.com`. * 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.
* *
* The second strategy is to look for the ELF executable for the current * Function tracing is disabled throughout the duration of this call.
* program. If you're running a .com binary, it'll look for the .com.dbg * Backtraces and other core runtime functionality depend on this.
* 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 * @return symbol table, or NULL if not found
*/ */
struct SymbolTable *GetSymbolTable(void) { struct SymbolTable *GetSymbolTable(void) {
cosmo_once(&__symtab.once, GetSymbolTableInit); struct Zipos *z;
return __symtab.st; 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;
} }

View file

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

View file

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

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/state.internal.h" #include "libc/calls/state.internal.h"
#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall-sysv.internal.h"
@ -31,14 +30,16 @@
* @vforksafe * @vforksafe
*/ */
int __zipos_close(int fd) { int __zipos_close(int fd) {
if (__vforked) { int rc;
sys_close(fd); struct ZiposHandle *h;
return 0; 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);
} }
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; return rc;
} }

View file

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

View file

@ -16,17 +16,25 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/atomic.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/metalfile.internal.h" #include "libc/calls/metalfile.internal.h"
#include "libc/calls/struct/stat.h" #include "libc/calls/struct/stat.h"
#include "libc/cosmo.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/intrin/strace.internal.h"
#include "libc/limits.h" #include "libc/macros.internal.h"
#include "libc/mem/alg.h" #include "libc/mem/alg.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/runtime/zipos.internal.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/o.h"
#include "libc/sysv/consts/posix.h"
#include "libc/sysv/consts/prot.h"
#include "libc/thread/thread.h" #include "libc/thread/thread.h"
#include "libc/zip.internal.h" #include "libc/zip.internal.h"
@ -34,162 +42,118 @@
__static_yoink(APE_COM_NAME); __static_yoink(APE_COM_NAME);
#endif #endif
struct ZiposPlanner {
uint8_t buf[kZipLookbehindBytes];
struct stat st;
};
static struct Zipos __zipos; static struct Zipos __zipos;
static atomic_uint __zipos_once;
static void __zipos_wipe(void) { static void __zipos_dismiss(uint8_t *map, const uint8_t *cdir, long pg) {
pthread_mutex_init(&__zipos.lock, 0); 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);
}
} }
void __zipos_lock(void) { static int __zipos_compare_names(const void *a, const void *b, void *c) {
pthread_mutex_lock(&__zipos.lock); const size_t *x = (const size_t *)a;
} const size_t *y = (const size_t *)b;
struct Zipos *z = (struct Zipos *)c;
void __zipos_unlock(void) { int xn = ZIP_CFILE_NAMESIZE(z->map + *x);
pthread_mutex_unlock(&__zipos.lock); int yn = ZIP_CFILE_NAMESIZE(z->map + *y);
}
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); int n = MIN(xn, yn);
if (n) { if (n) {
int res = memcmp(ZIP_CFILE_NAME(cdir + *x), ZIP_CFILE_NAME(cdir + *y), n); int res =
memcmp(ZIP_CFILE_NAME(z->map + *x), ZIP_CFILE_NAME(z->map + *y), n);
if (res) return res; if (res) return res;
} }
return xn - yn; // xn and yn are 16-bit return xn - yn; // xn and yn are 16-bit
} }
static dontinline int __zipos_plan(int fd, struct ZiposPlanner *p) { // creates binary searchable array of file offsets to cdir records
static void __zipos_generate_index(struct Zipos *zipos) {
// get file size and dev/inode size_t c, i;
// this might fail if a bad fd was passed via environment zipos->records = GetZipCdirRecords(zipos->cdir);
if (fstat(fd, &p->st)) { zipos->index = _mapanon(zipos->records * sizeof(size_t));
return kZipErrorOpenFailed; for (i = 0, c = GetZipCdirOffset(zipos->cdir); i < zipos->records;
++i, c += ZIP_CFILE_HDRSIZE(zipos->map + c)) {
zipos->index[i] = c;
} }
// 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 // smoothsort() isn't the fastest algorithm, but it guarantees
// o(logn), won't smash the stack and doesn't depend on malloc // o(logn), won't smash the stack and doesn't depend on malloc
int entry_index, entry_offset; smoothsort_r(zipos->index, zipos->records, sizeof(size_t),
for (entry_index = entry_offset = 0; __zipos_compare_names, zipos);
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) { static void __zipos_init(void) {
int status; char *endptr;
if (getenv("COSMOPOLITAN_DISABLE_ZIPOS")) return; const char *s;
if (!(__zipos.progpath = getenv("COSMOPOLITAN_INIT_ZIPOS"))) { struct stat st;
__zipos.progpath = GetProgramExecutableName(); int x, fd, err, msg;
} uint8_t *map, *cdir;
int fd = open(__zipos.progpath, O_RDONLY); const char *progpath;
if (fd != -1) { if (!(s = getenv("COSMOPOLITAN_DISABLE_ZIPOS"))) {
if (!(status = __zipos_setup(fd))) { // this environment variable may be a filename or file descriptor
__zipos_wipe(); if ((progpath = getenv("COSMOPOLITAN_INIT_ZIPOS")) &&
pthread_atfork(__zipos_lock, __zipos_unlock, __zipos_wipe); (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;
} }
close(fd);
} else { } else {
status = kZipErrorOpenFailed; progpath = 0;
msg = -777;
} }
(void)status; (void)msg;
STRACE("__zipos_init(%#s) → %d% m", __zipos.progpath, status); STRACE("__zipos_get(%#s) → %d% m", progpath, msg);
} }
/** /**
@ -197,6 +161,6 @@ static void __zipos_init(void) {
* @asyncsignalsafe * @asyncsignalsafe
*/ */
struct Zipos *__zipos_get(void) { struct Zipos *__zipos_get(void) {
cosmo_once(&__zipos.once, __zipos_init); cosmo_once(&__zipos_once, __zipos_init);
return __zipos.cnt ? &__zipos : 0; return __zipos.cdir ? &__zipos : 0;
} }

View file

@ -33,6 +33,7 @@ static uint64_t __zipos_fnv(const char *s, int len) {
uint64_t __zipos_inode(struct Zipos *zipos, int64_t cfile, // uint64_t __zipos_inode(struct Zipos *zipos, int64_t cfile, //
const void *name, size_t namelen) { const void *name, size_t namelen) {
unassert(cfile >= 0);
if (cfile == ZIPOS_SYNTHETIC_DIRECTORY) { if (cfile == ZIPOS_SYNTHETIC_DIRECTORY) {
if (namelen && ((char *)name)[namelen - 1] == '/') --namelen; if (namelen && ((char *)name)[namelen - 1] == '/') --namelen;
cfile = INT64_MIN | __zipos_fnv(name, 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 * @return virtual base address of new mapping, or MAP_FAILED w/ errno
*/ */
void *__zipos_mmap(void *addr, size_t size, int prot, int flags, 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) { if (off < 0) {
STRACE("negative zipos mmap offset"); 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 || if (h->cfile == ZIPOS_SYNTHETIC_DIRECTORY ||
S_ISDIR(GetZipCfileMode(h->zipos->cdir + h->cfile))) { S_ISDIR(GetZipCfileMode(h->zipos->map + h->cfile))) {
return VIP(eisdir()); return VIP(eisdir());
} }

View file

@ -16,56 +16,68 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/assert.h"
#include "libc/calls/blocksigs.internal.h" #include "libc/calls/blocksigs.internal.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/struct/fd.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/errno.h" #include "libc/errno.h"
#include "libc/intrin/asan.internal.h" #include "libc/intrin/asan.internal.h"
#include "libc/intrin/asancodes.h"
#include "libc/intrin/atomic.h" #include "libc/intrin/atomic.h"
#include "libc/intrin/cmpxchg.h" #include "libc/intrin/cmpxchg.h"
#include "libc/intrin/directmap.internal.h"
#include "libc/intrin/extend.internal.h" #include "libc/intrin/extend.internal.h"
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h" #include "libc/intrin/weaken.h"
#include "libc/runtime/internal.h" #include "libc/runtime/internal.h"
#include "libc/runtime/memtrack.internal.h" #include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/zipos.internal.h" #include "libc/runtime/zipos.internal.h"
#include "libc/str/str.h" #include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/at.h" #include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/map.h" #include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/s.h" #include "libc/sysv/consts/s.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h" #include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include "libc/zip.internal.h" #include "libc/zip.internal.h"
#include "third_party/zlib/zconf.h"
#include "third_party/zlib/zlib.h"
static bool __zipos_method_is_supported(int method) { static char *__zipos_mapend;
switch (method) { static size_t __zipos_maptotal;
case kZipCompressionNone: static pthread_mutex_t __zipos_lock_obj;
case kZipCompressionDeflate:
return true; static void __zipos_wipe(void) {
default: pthread_mutex_init(&__zipos_lock_obj, 0);
return false;
}
} }
static void *__zipos_mmap_space(struct Zipos *zipos, size_t mapsize) { 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) {
char *start; char *start;
size_t offset; size_t offset;
offset = zipos->maptotal; unassert(mapsize);
zipos->maptotal += mapsize; offset = __zipos_maptotal;
__zipos_maptotal += mapsize;
start = (char *)kMemtrackZiposStart; start = (char *)kMemtrackZiposStart;
if (!zipos->mapend) zipos->mapend = start; if (!__zipos_mapend) __zipos_mapend = start;
zipos->mapend = _extend(start, zipos->maptotal, zipos->mapend, MAP_PRIVATE, __zipos_mapend = _extend(start, __zipos_maptotal, __zipos_mapend, MAP_PRIVATE,
kMemtrackZiposStart + kMemtrackZiposSize); kMemtrackZiposStart + kMemtrackZiposSize);
return start + offset; return start + offset;
} }
void __zipos_free(struct ZiposHandle *h) { void __zipos_free(struct ZiposHandle *h) {
if (!h) return;
if (IsAsan()) { if (IsAsan()) {
__asan_poison((char *)h + sizeof(struct ZiposHandle), __asan_poison((char *)h + sizeof(struct ZiposHandle),
h->mapsize - sizeof(struct ZiposHandle), kAsanHeapFree); h->mapsize - sizeof(struct ZiposHandle), kAsanHeapFree);
@ -93,7 +105,7 @@ StartOver:
ph = &h->next; ph = &h->next;
} }
if (!h) { if (!h) {
h = __zipos_mmap_space(zipos, mapsize); h = __zipos_mmap_space(mapsize);
} }
__zipos_unlock(); __zipos_unlock();
if (IsAsan()) { if (IsAsan()) {
@ -110,85 +122,91 @@ StartOver:
return h; return h;
} }
static int __zipos_load_fd(struct Zipos *zipos, int cf, int fd, static int __zipos_mkfd(int minfd) {
struct ZiposUri *name, int fd, e = errno;
struct ZiposHandle **out_handle) { if ((fd = __sys_fcntl(2, F_DUPFD_CLOEXEC, minfd)) != -1) {
uint64_t size; 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;
struct ZiposHandle *h; struct ZiposHandle *h;
if (cf == ZIPOS_SYNTHETIC_DIRECTORY) { if (cf == ZIPOS_SYNTHETIC_DIRECTORY) {
size = name->len; size = name->len;
if (!(h = __zipos_alloc(zipos, size + 1))) return -1; if (!(h = __zipos_alloc(zipos, size + 1))) return -1;
if (size) memcpy(h->data, name->path, size); if (size) memcpy(h->data, name->path, size);
h->data[size] = 0; h->data[size] = 0;
h->mem = h->data;
} else { } else {
uint8_t lfile[kZipLfileHdrMinSize]; lf = GetZipCfileOffset(zipos->map + cf);
uint64_t lf = GetZipCfileOffset(zipos->cdir + cf); npassert((ZIP_LFILE_MAGIC(zipos->map + lf) == kZipLfileHdrMagic));
int compressed = ZIP_CFILE_COMPRESSIONMETHOD(zipos->cdir + cf); size = GetZipLfileUncompressedSize(zipos->map + lf);
if (!__zipos_method_is_supported(compressed) || switch (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
pread(fd, lfile, kZipLfileHdrMinSize, lf) != kZipLfileHdrMinSize || case kZipCompressionNone:
ZIP_LFILE_MAGIC(lfile) != kZipLfileHdrMagic) { if (!(h = __zipos_alloc(zipos, 0))) return -1;
return eio(); // this corruption h->mem = ZIP_LFILE_CONTENT(zipos->map + lf);
} break;
size = GetZipCfileUncompressedSize(zipos->cdir + cf); case kZipCompressionDeflate:
if (!(h = __zipos_alloc(zipos, size))) return -1; if (!(h = __zipos_alloc(zipos, size))) return -1;
uint64_t off = lf + ZIP_LFILE_HDRSIZE(lfile); if (!__inflate(h->data, size, ZIP_LFILE_CONTENT(zipos->map + lf),
if (!compressed) { GetZipLfileCompressedSize(zipos->map + lf))) {
if (pread(fd, h->data, size, off) != size) { h->mem = h->data;
__zipos_free(h); } else {
h->mem = 0;
eio();
}
break;
default:
return eio(); 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->pos = 0;
h->cfile = cf; h->cfile = cf;
h->size = size; h->size = size;
*out_handle = h; if (h->mem) {
return 0; minfd = 3;
} __fds_lock();
TryAgain:
static int __zipos_load(struct Zipos *zipos, int cf, int flags, if (IsWindows() || IsMetal()) {
struct ZiposUri *name) { if ((fd = __reservefd_unlocked(-1)) != -1) {
int fd; return __zipos_setfd(fd, h, flags);
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);
} }
// turn it from a file fd to a zip fd } else if ((fd = __zipos_mkfd(minfd)) != -1) {
// layer the handle on windows so it can be restored on close if (__ensurefds_unlocked(fd) != -1) {
h->handle = g_fds.p[fd].handle; if (g_fds.p[fd].kind) {
g_fds.p[fd].kind = kFdZip; sys_close(fd);
g_fds.p[fd].handle = (intptr_t)h; minfd = fd + 1;
g_fds.p[fd].flags &= ~O_CLOEXEC; goto TryAgain;
g_fds.p[fd].flags |= flags & O_CLOEXEC; }
return fd; return __zipos_setfd(fd, h, flags);
} else { }
close(fd); sys_close(fd);
return -1;
} }
} else { __fds_unlock();
return -1;
} }
__zipos_free(h);
return -1;
} }
/** /**
@ -213,10 +231,6 @@ int __zipos_open(struct ZiposUri *name, int flags) {
(flags & O_ACCMODE) != O_RDONLY) { (flags & O_ACCMODE) != O_RDONLY) {
return erofs(); 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 // get the zipos global singleton
struct Zipos *zipos; struct Zipos *zipos;
@ -228,12 +242,15 @@ int __zipos_open(struct ZiposUri *name, int flags) {
// majority of these calls will return ENOENT or ENOTDIR. we need to // majority of these calls will return ENOENT or ENOTDIR. we need to
// perform two extremely costly sigprocmask() calls below. thanks to // perform two extremely costly sigprocmask() calls below. thanks to
// zipos being a read-only filesystem, we can avoid it in many cases // zipos being a read-only filesystem, we can avoid it in many cases
int cf; ssize_t cf;
if ((cf = __zipos_find(zipos, name)) == -1) { if ((cf = __zipos_find(zipos, name)) == -1) {
return -1; return -1;
} }
if (flags & O_EXCL) {
return eexist();
}
if (cf != ZIPOS_SYNTHETIC_DIRECTORY) { if (cf != ZIPOS_SYNTHETIC_DIRECTORY) {
int mode = GetZipCfileMode(zipos->cdir + cf); int mode = GetZipCfileMode(zipos->map + cf);
if ((flags & O_DIRECTORY) && !S_ISDIR(mode)) { if ((flags & O_DIRECTORY) && !S_ISDIR(mode)) {
return enotdir(); return enotdir();
} }
@ -248,3 +265,8 @@ int __zipos_open(struct ZiposUri *name, int flags) {
ALLOW_SIGNALS; ALLOW_SIGNALS;
return rc; 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; int i;
int64_t b, x, y; int64_t b, x, y;
if (h->cfile == ZIPOS_SYNTHETIC_DIRECTORY || if (h->cfile == ZIPOS_SYNTHETIC_DIRECTORY ||
S_ISDIR(GetZipCfileMode(h->zipos->cdir + h->cfile))) { S_ISDIR(GetZipCfileMode(h->zipos->map + h->cfile))) {
return eisdir(); return eisdir();
} }
if (opt_offset == -1) { 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) { for (i = 0; i < iovlen && y < h->size; ++i, y += b) {
b = MIN(iov[i].iov_len, h->size - y); b = MIN(iov[i].iov_len, h->size - y);
if (b) memcpy(iov[i].iov_base, h->data + y, b); if (b) memcpy(iov[i].iov_base, h->mem + y, b);
} }
if (opt_offset == -1) { if (opt_offset == -1) {
h->pos = y; h->pos = y;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -52,7 +52,7 @@ void *Worker(void *arg) {
} }
TEST(zipos, test) { TEST(zipos, test) {
int i, n = 20; int i, n = 16;
pthread_t *t = gc(malloc(sizeof(pthread_t) * n)); pthread_t *t = gc(malloc(sizeof(pthread_t) * n));
for (i = 0; i < n; ++i) { for (i = 0; i < n; ++i) {
ASSERT_SYS(0, 0, pthread_create(t + i, 0, Worker, 0)); ASSERT_SYS(0, 0, pthread_create(t + i, 0, Worker, 0));
@ -86,14 +86,6 @@ TEST(zipos, readPastEof) {
EXPECT_SYS(0, 0, close(3)); 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) { TEST(zipos_O_DIRECTORY, blocksOpeningOfNormalFiles) {
ASSERT_SYS(ENOTDIR, -1, ASSERT_SYS(ENOTDIR, -1,
open("/zip/libc/testlib/hyperion.txt", O_RDONLY | O_DIRECTORY)); open("/zip/libc/testlib/hyperion.txt", O_RDONLY | O_DIRECTORY));