mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
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:
parent
ee8a861635
commit
ff250a0c10
21 changed files with 420 additions and 407 deletions
|
@ -12,7 +12,6 @@
|
|||
#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"
|
||||
|
@ -29,6 +28,7 @@
|
|||
#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,12 +1808,19 @@ void GetOpts(int argc, char* argv[]) {
|
|||
}
|
||||
|
||||
size_t FindZipGames(void) {
|
||||
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);
|
||||
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)))) {
|
||||
APPEND(&zipgames_.p, &zipgames_.i, &zipgames_.n, &name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,14 +28,9 @@
|
|||
#include "libc/sysv/consts/rlim.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()
|
||||
// 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
|
||||
// Every UNIX system in our support vector creates arg blocks like:
|
||||
//
|
||||
// <HIGHEST-STACK-ADDRESS>
|
||||
// last environ string
|
||||
|
@ -60,6 +55,11 @@
|
|||
// 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;
|
||||
|
|
|
@ -135,6 +135,7 @@ __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;
|
||||
|
@ -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.
|
||||
int idx;
|
||||
x = va_arg(va, intptr_t);
|
||||
if (_weaken(__symtab) && _weaken(__symtab)->st &&
|
||||
if (_weaken(__symtab) && *_weaken(__symtab) &&
|
||||
(idx = _weaken(__get_symbol)(0, x)) != -1) {
|
||||
if (p + 1 <= e) *p++ = '&';
|
||||
s = _weaken(__symtab)->st->name_base +
|
||||
_weaken(__symtab)->st->names[idx];
|
||||
s = (*_weaken(__symtab))->name_base +
|
||||
(*_weaken(__symtab))->names[idx];
|
||||
goto FormatString;
|
||||
}
|
||||
base = 4;
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
|
||||
extern struct SymbolTable *__symtab;
|
||||
|
||||
/**
|
||||
* 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:
|
||||
// function tracing depends on this function via kprintf
|
||||
unsigned l, m, r, n, k;
|
||||
if (!t && __symtab.st) {
|
||||
t = __symtab.st;
|
||||
if (!t && __symtab) {
|
||||
t = __symtab;
|
||||
}
|
||||
if (t) {
|
||||
l = 0;
|
||||
|
|
|
@ -16,89 +16,127 @@
|
|||
│ 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/cosmo.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/intrin/bits.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/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.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"
|
||||
|
||||
__static_yoink("__get_symbol");
|
||||
|
||||
struct SymbolTableLoader __symtab;
|
||||
static pthread_spinlock_t g_lock;
|
||||
struct SymbolTable *__symtab; // for kprintf
|
||||
|
||||
static struct SymbolTable *GetSymbolTableFromZip(void) {
|
||||
int fd;
|
||||
struct SymbolTable *res = 0;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads symbol table from .com.dbg file.
|
||||
* @note This code can't depend on dlmalloc()
|
||||
*/
|
||||
static struct SymbolTable *GetSymbolTableFromElf(void) {
|
||||
const char *path;
|
||||
if ((path = FindDebugBinary())) {
|
||||
return OpenSymbolTable(path);
|
||||
const char *s;
|
||||
if (PLEDGED(RPATH) && (s = FindDebugBinary())) {
|
||||
return OpenSymbolTable(s);
|
||||
} 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");
|
||||
* __static_yoink("__zipos_get");
|
||||
*
|
||||
* 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`.
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* Function tracing is disabled throughout the duration of this call.
|
||||
* Backtraces and other core runtime functionality depend on this.
|
||||
*
|
||||
* @return symbol table, or NULL if not found
|
||||
*/
|
||||
struct SymbolTable *GetSymbolTable(void) {
|
||||
cosmo_once(&__symtab.once, GetSymbolTableInit);
|
||||
return __symtab.st;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -26,12 +26,6 @@ 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);
|
||||
|
|
|
@ -39,14 +39,14 @@ int __zipos_access(struct ZiposUri *name, int amode) {
|
|||
return enoexec();
|
||||
}
|
||||
|
||||
int cf;
|
||||
ssize_t cf;
|
||||
if ((cf = __zipos_find(z, name)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mode;
|
||||
if (cf != ZIPOS_SYNTHETIC_DIRECTORY) {
|
||||
mode = GetZipCfileMode(z->cdir + cf);
|
||||
mode = GetZipCfileMode(z->map + cf);
|
||||
} else {
|
||||
mode = S_IFDIR | 0555;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
|
@ -31,14 +30,16 @@
|
|||
* @vforksafe
|
||||
*/
|
||||
int __zipos_close(int fd) {
|
||||
if (__vforked) {
|
||||
sys_close(fd);
|
||||
return 0;
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -24,11 +24,11 @@
|
|||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/zip.internal.h"
|
||||
|
||||
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);
|
||||
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);
|
||||
if ((len == zsize || (len + 1 == zsize && zname[len] == '/')) &&
|
||||
!memcmp(name->path, zname, len)) {
|
||||
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
|
||||
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
|
||||
int l = 0;
|
||||
int r = zipos->cnt;
|
||||
int r = zipos->records;
|
||||
while (l < r) {
|
||||
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;
|
||||
int xn = ZIP_CFILE_NAMESIZE(zipos->cdir + zipos->index[m]);
|
||||
int xn = ZIP_CFILE_NAMESIZE(zipos->map + zipos->index[m]);
|
||||
int yn = len;
|
||||
int n = MIN(xn, yn);
|
||||
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 cfile = zipos->index[l];
|
||||
const char *zname = ZIP_CFILE_NAME(zipos->cdir + cfile);
|
||||
int zsize = ZIP_CFILE_NAMESIZE(zipos->cdir + cfile);
|
||||
size_t cfile = zipos->index[l];
|
||||
const char *zname = ZIP_CFILE_NAME(zipos->map + cfile);
|
||||
int zsize = ZIP_CFILE_NAMESIZE(zipos->map + 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->cnt; l += dx) {
|
||||
int cf;
|
||||
for (l += dx; 0 <= l && l < zipos->records; l += dx) {
|
||||
ssize_t cf;
|
||||
if ((cf = __zipos_match(zipos, name, len, l)) != -1) {
|
||||
return cf;
|
||||
}
|
||||
cfile = zipos->index[l];
|
||||
zname = ZIP_CFILE_NAME(zipos->cdir + cfile);
|
||||
zsize = ZIP_CFILE_NAMESIZE(zipos->cdir + cfile);
|
||||
zname = ZIP_CFILE_NAME(zipos->map + cfile);
|
||||
zsize = ZIP_CFILE_NAMESIZE(zipos->map + cfile);
|
||||
if (zsize < len || (len && zname[len - 1] != name->path[len - 1])) {
|
||||
break;
|
||||
}
|
||||
|
@ -112,8 +112,8 @@ int __zipos_scan(struct Zipos *zipos, struct ZiposUri *name) {
|
|||
}
|
||||
|
||||
// support code for open(), stat(), and access()
|
||||
int __zipos_find(struct Zipos *zipos, struct ZiposUri *name) {
|
||||
int cf;
|
||||
ssize_t __zipos_find(struct Zipos *zipos, struct ZiposUri *name) {
|
||||
ssize_t cf;
|
||||
if ((cf = __zipos_scan(zipos, name)) == -1) {
|
||||
// test if parent component exists that isn't a directory
|
||||
char *p;
|
||||
|
@ -121,7 +121,7 @@ int __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->cdir + cf))) {
|
||||
!S_ISDIR(GetZipCfileMode(zipos->map + cf))) {
|
||||
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
|
||||
if (cf != ZIPOS_SYNTHETIC_DIRECTORY && //
|
||||
name->len && name->path[name->len - 1] == '/' &&
|
||||
!S_ISDIR(GetZipCfileMode(zipos->cdir + cf))) {
|
||||
!S_ISDIR(GetZipCfileMode(zipos->map + cf))) {
|
||||
return enotdir();
|
||||
}
|
||||
return cf;
|
||||
|
|
|
@ -16,17 +16,25 @@
|
|||
│ 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/limits.h"
|
||||
#include "libc/macros.internal.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"
|
||||
|
||||
|
@ -34,162 +42,118 @@
|
|||
__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_wipe(void) {
|
||||
pthread_mutex_init(&__zipos.lock, 0);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
int n = MIN(xn, yn);
|
||||
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;
|
||||
}
|
||||
return xn - yn; // xn and yn are 16-bit
|
||||
}
|
||||
|
||||
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;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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
|
||||
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);
|
||||
smoothsort_r(zipos->index, zipos->records, sizeof(size_t),
|
||||
__zipos_compare_names, zipos);
|
||||
}
|
||||
|
||||
static void __zipos_init(void) {
|
||||
int status;
|
||||
if (getenv("COSMOPOLITAN_DISABLE_ZIPOS")) return;
|
||||
if (!(__zipos.progpath = getenv("COSMOPOLITAN_INIT_ZIPOS"))) {
|
||||
__zipos.progpath = GetProgramExecutableName();
|
||||
}
|
||||
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);
|
||||
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;
|
||||
}
|
||||
close(fd);
|
||||
} else {
|
||||
status = kZipErrorOpenFailed;
|
||||
progpath = 0;
|
||||
msg = -777;
|
||||
}
|
||||
(void)status;
|
||||
STRACE("__zipos_init(%#s) → %d% m", __zipos.progpath, status);
|
||||
(void)msg;
|
||||
STRACE("__zipos_get(%#s) → %d% m", progpath, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -197,6 +161,6 @@ static void __zipos_init(void) {
|
|||
* @asyncsignalsafe
|
||||
*/
|
||||
struct Zipos *__zipos_get(void) {
|
||||
cosmo_once(&__zipos.once, __zipos_init);
|
||||
return __zipos.cnt ? &__zipos : 0;
|
||||
cosmo_once(&__zipos_once, __zipos_init);
|
||||
return __zipos.cdir ? &__zipos : 0;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ 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);
|
||||
|
|
|
@ -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->cdir + h->cfile))) {
|
||||
S_ISDIR(GetZipCfileMode(h->zipos->map + h->cfile))) {
|
||||
return VIP(eisdir());
|
||||
}
|
||||
|
||||
|
|
|
@ -16,56 +16,68 @@
|
|||
│ 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/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/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/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fd.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/consts/s.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.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) {
|
||||
switch (method) {
|
||||
case kZipCompressionNone:
|
||||
case kZipCompressionDeflate:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
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 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;
|
||||
size_t offset;
|
||||
offset = zipos->maptotal;
|
||||
zipos->maptotal += mapsize;
|
||||
unassert(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);
|
||||
|
@ -93,7 +105,7 @@ StartOver:
|
|||
ph = &h->next;
|
||||
}
|
||||
if (!h) {
|
||||
h = __zipos_mmap_space(zipos, mapsize);
|
||||
h = __zipos_mmap_space(mapsize);
|
||||
}
|
||||
__zipos_unlock();
|
||||
if (IsAsan()) {
|
||||
|
@ -110,85 +122,91 @@ StartOver:
|
|||
return h;
|
||||
}
|
||||
|
||||
static int __zipos_load_fd(struct Zipos *zipos, int cf, int fd,
|
||||
struct ZiposUri *name,
|
||||
struct ZiposHandle **out_handle) {
|
||||
uint64_t size;
|
||||
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;
|
||||
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 {
|
||||
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);
|
||||
lf = GetZipCfileOffset(zipos->map + cf);
|
||||
npassert((ZIP_LFILE_MAGIC(zipos->map + lf) == kZipLfileHdrMagic));
|
||||
size = GetZipLfileUncompressedSize(zipos->map + lf);
|
||||
switch (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
|
||||
case kZipCompressionNone:
|
||||
if (!(h = __zipos_alloc(zipos, 0))) return -1;
|
||||
h->mem = ZIP_LFILE_CONTENT(zipos->map + lf);
|
||||
break;
|
||||
case kZipCompressionDeflate:
|
||||
if (!(h = __zipos_alloc(zipos, size))) return -1;
|
||||
if (!__inflate(h->data, size, ZIP_LFILE_CONTENT(zipos->map + lf),
|
||||
GetZipLfileCompressedSize(zipos->map + lf))) {
|
||||
h->mem = h->data;
|
||||
} else {
|
||||
h->mem = 0;
|
||||
eio();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return eio();
|
||||
}
|
||||
} 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;
|
||||
*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);
|
||||
if (h->mem) {
|
||||
minfd = 3;
|
||||
__fds_lock();
|
||||
TryAgain:
|
||||
if (IsWindows() || IsMetal()) {
|
||||
if ((fd = __reservefd_unlocked(-1)) != -1) {
|
||||
return __zipos_setfd(fd, h, flags);
|
||||
}
|
||||
// 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;
|
||||
} 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);
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
__fds_unlock();
|
||||
}
|
||||
__zipos_free(h);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -213,10 +231,6 @@ 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;
|
||||
|
@ -228,12 +242,15 @@ 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
|
||||
int cf;
|
||||
ssize_t 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->cdir + cf);
|
||||
int mode = GetZipCfileMode(zipos->map + cf);
|
||||
if ((flags & O_DIRECTORY) && !S_ISDIR(mode)) {
|
||||
return enotdir();
|
||||
}
|
||||
|
@ -248,3 +265,8 @@ 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);
|
||||
}
|
||||
|
|
|
@ -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->cdir + h->cfile))) {
|
||||
S_ISDIR(GetZipCfileMode(h->zipos->map + 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->data + y, b);
|
||||
if (b) memcpy(iov[i].iov_base, h->mem + y, b);
|
||||
}
|
||||
if (opt_offset == -1) {
|
||||
h->pos = y;
|
||||
|
|
|
@ -25,7 +25,8 @@
|
|||
#include "libc/sysv/consts/s.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));
|
||||
st->st_nlink = 1;
|
||||
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(
|
||||
&__umask, memory_order_acquire));
|
||||
} else {
|
||||
st->st_mode = GetZipCfileMode(zipos->cdir + cf);
|
||||
st->st_size = GetZipCfileUncompressedSize(zipos->cdir + cf);
|
||||
lf = GetZipCfileOffset(zipos->map + cf);
|
||||
st->st_mode = GetZipCfileMode(zipos->map + cf);
|
||||
st->st_size = GetZipLfileUncompressedSize(zipos->map + lf);
|
||||
st->st_blocks =
|
||||
roundup(GetZipCfileCompressedSize(zipos->cdir + cf), 512) / 512;
|
||||
GetZipCfileTimestamps(zipos->cdir + cf, &st->st_mtim, &st->st_atim,
|
||||
roundup(GetZipLfileCompressedSize(zipos->map + lf), 512) / 512;
|
||||
GetZipCfileTimestamps(zipos->map + cf, &st->st_mtim, &st->st_atim,
|
||||
&st->st_ctim, 0);
|
||||
st->st_birthtim = st->st_ctim;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
* @asyncsignalsafe
|
||||
*/
|
||||
int __zipos_stat(struct ZiposUri *name, struct stat *st) {
|
||||
int cf;
|
||||
ssize_t cf;
|
||||
struct Zipos *zipos;
|
||||
if (!(zipos = __zipos_get())) return enoexec();
|
||||
if ((cf = __zipos_find(zipos, name)) == -1) return -1;
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
#ifndef COSMOPOLITAN_ZIPOS_H_
|
||||
#define COSMOPOLITAN_ZIPOS_H_
|
||||
#include "libc/thread/thread.h"
|
||||
#ifndef COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_
|
||||
#define COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#define ZIPOS_PATH_MAX 1024
|
||||
|
||||
#define ZIPOS_SYNTHETIC_DIRECTORY -2
|
||||
#define ZIPOS_SYNTHETIC_DIRECTORY 0
|
||||
|
||||
struct stat;
|
||||
struct iovec;
|
||||
|
@ -20,43 +19,37 @@ struct ZiposUri {
|
|||
struct ZiposHandle {
|
||||
struct ZiposHandle *next;
|
||||
struct Zipos *zipos;
|
||||
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 */
|
||||
size_t size;
|
||||
size_t mapsize;
|
||||
size_t pos;
|
||||
size_t cfile;
|
||||
uint8_t *mem;
|
||||
uint8_t data[];
|
||||
};
|
||||
|
||||
struct Zipos {
|
||||
_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;
|
||||
long pagesz;
|
||||
uint8_t *map;
|
||||
uint8_t *cdir;
|
||||
uint64_t dev;
|
||||
size_t *index;
|
||||
size_t records;
|
||||
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);
|
||||
int __zipos_find(struct Zipos *, struct ZiposUri *);
|
||||
int __zipos_scan(struct Zipos *, struct ZiposUri *);
|
||||
ssize_t __zipos_find(struct Zipos *, struct ZiposUri *);
|
||||
ssize_t __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 *, 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);
|
||||
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_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_ZIPOS_H_ */
|
||||
#endif /* COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_ */
|
||||
|
|
|
@ -77,8 +77,8 @@ struct dirstream {
|
|||
struct {
|
||||
struct Zipos *zipos;
|
||||
uint64_t inode;
|
||||
int offset;
|
||||
int records;
|
||||
uint64_t offset;
|
||||
uint64_t 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->cdir + h->cfile))) {
|
||||
!S_ISDIR(GetZipCfileMode(h->zipos->map + 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->cdir + h->cfile);
|
||||
name = ZIP_CFILE_NAME(h->zipos->cdir + h->cfile);
|
||||
len = ZIP_CFILE_NAMESIZE(h->zipos->map + h->cfile);
|
||||
name = ZIP_CFILE_NAME(h->zipos->map + 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 = 0;
|
||||
dir->zip.records = h->zipos->cnt;
|
||||
dir->zip.offset = GetZipCdirOffset(h->zipos->cdir);
|
||||
dir->zip.records = GetZipCdirRecords(h->zipos->cdir);
|
||||
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->cdir + dir->zip.offset);
|
||||
size_t n = ZIP_CFILE_NAMESIZE(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->map + 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->cdir +
|
||||
} else if (S_ISDIR(GetZipCfileMode(dir->zip.zipos->map +
|
||||
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->cdir + dir->zip.offset);
|
||||
ZIP_CFILE_HDRSIZE(dir->zip.zipos->map + 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 = 0;
|
||||
dir->zip.offset = GetZipCdirOffset(dir->zip.zipos->cdir);
|
||||
} 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 = 0;
|
||||
dir->zip.offset = GetZipCdirOffset(dir->zip.zipos->cdir);
|
||||
while (dir->tell < tell) {
|
||||
if (!readdir_zipos(dir)) {
|
||||
break;
|
||||
|
|
|
@ -36,8 +36,6 @@
|
|||
#define kZipErrorRaceCondition _ZE(-12)
|
||||
#define kZipErrorMapFailed _ZE(-13)
|
||||
#define kZipErrorOpenFailed _ZE(-14)
|
||||
#define kZipErrorReadFailed _ZE(-15)
|
||||
#define kZipErrorZipCorrupt _ZE(-16)
|
||||
|
||||
#define kZipCosmopolitanVersion kZipEra2001
|
||||
|
||||
|
@ -72,8 +70,6 @@
|
|||
#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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -52,7 +52,7 @@ void *Worker(void *arg) {
|
|||
}
|
||||
|
||||
TEST(zipos, test) {
|
||||
int i, n = 20;
|
||||
int i, n = 16;
|
||||
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,14 +86,6 @@ 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));
|
||||
|
|
Loading…
Reference in a new issue