mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
164 lines
6.1 KiB
C
164 lines
6.1 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
│ │
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/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/dce.h"
|
|
#include "libc/fmt/conv.h"
|
|
#include "libc/intrin/cmpxchg.h"
|
|
#include "libc/intrin/promises.h"
|
|
#include "libc/intrin/strace.h"
|
|
#include "libc/macros.h"
|
|
#include "libc/mem/alg.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/runtime/zipos.internal.h"
|
|
#include "libc/str/str.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.h"
|
|
|
|
#ifdef __x86_64__
|
|
__static_yoink(APE_COM_NAME);
|
|
#endif
|
|
|
|
static struct Zipos __zipos;
|
|
static atomic_uint __zipos_once;
|
|
|
|
static void __zipos_dismiss(uint8_t *map, const uint8_t *cdir, long pg) {
|
|
uint64_t i, n, c, ef, lf, mo, lo, hi;
|
|
|
|
// determine the byte range of zip file content (excluding central dir)
|
|
c = GetZipCdirOffset(cdir);
|
|
n = GetZipCdirRecords(cdir);
|
|
for (lo = c, hi = i = 0; i < n; ++i, c += ZIP_CFILE_HDRSIZE(map + c)) {
|
|
lf = GetZipCfileOffset(map + c);
|
|
if (lf < lo)
|
|
lo = lf;
|
|
ef = lf + ZIP_LFILE_HDRSIZE(map + lf) + GetZipLfileCompressedSize(map + lf);
|
|
if (ef > hi)
|
|
hi = ef;
|
|
}
|
|
|
|
// unmap the executable portion beneath the local files
|
|
if (!IsWindows()) {
|
|
mo = ROUNDDOWN(lo, __gransize);
|
|
if (mo)
|
|
munmap(map, mo);
|
|
}
|
|
}
|
|
|
|
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(z->map + *x), ZIP_CFILE_NAME(z->map + *y), n);
|
|
if (res)
|
|
return res;
|
|
}
|
|
return xn - yn; // xn and yn are 16-bit
|
|
}
|
|
|
|
// creates binary searchable array of file offsets to cdir records
|
|
static void __zipos_generate_index(struct Zipos *zipos) {
|
|
size_t c, i;
|
|
zipos->records = GetZipCdirRecords(zipos->cdir);
|
|
zipos->index = _mapanon(zipos->records * sizeof(size_t));
|
|
for (i = 0, c = GetZipCdirOffset(zipos->cdir); i < zipos->records;
|
|
++i, c += ZIP_CFILE_HDRSIZE(zipos->map + c))
|
|
zipos->index[i] = c;
|
|
// smoothsort() isn't the fastest algorithm, but it guarantees
|
|
// o(nlogn) won't smash the stack and doesn't depend on malloc
|
|
smoothsort_r(zipos->index, zipos->records, sizeof(size_t),
|
|
__zipos_compare_names, zipos);
|
|
}
|
|
|
|
static void __zipos_init(void) {
|
|
char *endptr;
|
|
const char *s;
|
|
struct stat st;
|
|
int x, fd, err, msg;
|
|
uint8_t *map, *cdir;
|
|
const char *progpath;
|
|
if (!(s = getenv("COSMOPOLITAN_DISABLE_ZIPOS"))) {
|
|
// this environment variable may be a filename or file descriptor
|
|
if ((progpath = secure_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 = __pagesize;
|
|
__zipos_dismiss(map, cdir, pagesz);
|
|
__zipos.map = map;
|
|
__zipos.cdir = cdir;
|
|
__zipos.dev = st.st_ino;
|
|
__zipos.pagesz = pagesz;
|
|
__zipos_generate_index(&__zipos);
|
|
msg = kZipOk;
|
|
} else {
|
|
munmap(map, st.st_size);
|
|
msg = !cdir ? err : kZipErrorRaceCondition;
|
|
}
|
|
} else {
|
|
msg = kZipErrorMapFailed;
|
|
}
|
|
close(fd);
|
|
} else {
|
|
msg = kZipErrorOpenFailed;
|
|
}
|
|
} else {
|
|
msg = -666;
|
|
}
|
|
} else {
|
|
progpath = 0;
|
|
msg = -777;
|
|
}
|
|
STRACE("__zipos_get(%#s) → %d% m", progpath, msg);
|
|
}
|
|
|
|
/**
|
|
* Returns pointer to zip central directory of current executable.
|
|
* @asyncsignalsafe
|
|
*/
|
|
struct Zipos *__zipos_get(void) {
|
|
cosmo_once(&__zipos_once, __zipos_init);
|
|
return __zipos.cdir ? &__zipos : 0;
|
|
}
|