mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-22 21:32:31 +00:00
Rewrite ZipOS
This reduces the virtual memory usage of Emacs for me by 30%. We now have a simpler implementation that uses read(), rather mmap()ing the whole executable.
This commit is contained in:
parent
ff77f2a6af
commit
b01282e23e
21 changed files with 408 additions and 421 deletions
|
@ -16,68 +16,56 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/blocksigs.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/asancodes.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/intrin/directmap.internal.h"
|
||||
#include "libc/intrin/extend.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fd.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/consts/s.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/zip.internal.h"
|
||||
#include "third_party/zlib/zconf.h"
|
||||
#include "third_party/zlib/zlib.h"
|
||||
|
||||
static char *__zipos_mapend;
|
||||
static size_t __zipos_maptotal;
|
||||
static pthread_mutex_t __zipos_lock_obj;
|
||||
|
||||
static void __zipos_wipe(void) {
|
||||
pthread_mutex_init(&__zipos_lock_obj, 0);
|
||||
static bool __zipos_method_is_supported(int method) {
|
||||
switch (method) {
|
||||
case kZipCompressionNone:
|
||||
case kZipCompressionDeflate:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void __zipos_lock(void) {
|
||||
pthread_mutex_lock(&__zipos_lock_obj);
|
||||
}
|
||||
|
||||
static void __zipos_unlock(void) {
|
||||
pthread_mutex_unlock(&__zipos_lock_obj);
|
||||
}
|
||||
|
||||
static void *__zipos_mmap_space(size_t mapsize) {
|
||||
static void *__zipos_mmap_space(struct Zipos *zipos, size_t mapsize) {
|
||||
char *start;
|
||||
size_t offset;
|
||||
unassert(mapsize);
|
||||
offset = __zipos_maptotal;
|
||||
__zipos_maptotal += mapsize;
|
||||
offset = zipos->maptotal;
|
||||
zipos->maptotal += mapsize;
|
||||
start = (char *)kMemtrackZiposStart;
|
||||
if (!__zipos_mapend) __zipos_mapend = start;
|
||||
__zipos_mapend = _extend(start, __zipos_maptotal, __zipos_mapend, MAP_PRIVATE,
|
||||
kMemtrackZiposStart + kMemtrackZiposSize);
|
||||
if (!zipos->mapend) zipos->mapend = start;
|
||||
zipos->mapend = _extend(start, zipos->maptotal, zipos->mapend, MAP_PRIVATE,
|
||||
kMemtrackZiposStart + kMemtrackZiposSize);
|
||||
return start + offset;
|
||||
}
|
||||
|
||||
void __zipos_free(struct ZiposHandle *h) {
|
||||
if (!h) return;
|
||||
if (IsAsan()) {
|
||||
__asan_poison((char *)h + sizeof(struct ZiposHandle),
|
||||
h->mapsize - sizeof(struct ZiposHandle), kAsanHeapFree);
|
||||
|
@ -105,7 +93,7 @@ StartOver:
|
|||
ph = &h->next;
|
||||
}
|
||||
if (!h) {
|
||||
h = __zipos_mmap_space(mapsize);
|
||||
h = __zipos_mmap_space(zipos, mapsize);
|
||||
}
|
||||
__zipos_unlock();
|
||||
if (IsAsan()) {
|
||||
|
@ -122,91 +110,85 @@ StartOver:
|
|||
return h;
|
||||
}
|
||||
|
||||
static int __zipos_mkfd(int minfd) {
|
||||
int fd, e = errno;
|
||||
if ((fd = __sys_fcntl(2, F_DUPFD_CLOEXEC, minfd)) != -1) {
|
||||
return fd;
|
||||
} else if (errno == EINVAL) {
|
||||
errno = e;
|
||||
return __fixupnewfd(__sys_fcntl(2, F_DUPFD, minfd), O_CLOEXEC);
|
||||
} else {
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
|
||||
static int __zipos_setfd(int fd, struct ZiposHandle *h, unsigned flags) {
|
||||
int want = fd;
|
||||
atomic_compare_exchange_strong_explicit(
|
||||
&g_fds.f, &want, fd + 1, memory_order_release, memory_order_relaxed);
|
||||
g_fds.p[fd].kind = kFdZip;
|
||||
g_fds.p[fd].handle = (intptr_t)h;
|
||||
g_fds.p[fd].flags = flags | O_CLOEXEC;
|
||||
g_fds.p[fd].extra = 0;
|
||||
__fds_unlock();
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int __zipos_load(struct Zipos *zipos, size_t cf, int flags,
|
||||
struct ZiposUri *name) {
|
||||
size_t lf;
|
||||
size_t size;
|
||||
int fd, minfd;
|
||||
static int __zipos_load_fd(struct Zipos *zipos, int cf, int fd,
|
||||
struct ZiposUri *name,
|
||||
struct ZiposHandle **out_handle) {
|
||||
uint64_t size;
|
||||
struct ZiposHandle *h;
|
||||
if (cf == ZIPOS_SYNTHETIC_DIRECTORY) {
|
||||
size = name->len;
|
||||
if (!(h = __zipos_alloc(zipos, size + 1))) return -1;
|
||||
if (size) memcpy(h->data, name->path, size);
|
||||
h->data[size] = 0;
|
||||
h->mem = h->data;
|
||||
} else {
|
||||
lf = GetZipCfileOffset(zipos->map + cf);
|
||||
npassert((ZIP_LFILE_MAGIC(zipos->map + lf) == kZipLfileHdrMagic));
|
||||
size = GetZipLfileUncompressedSize(zipos->map + lf);
|
||||
switch (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
|
||||
case kZipCompressionNone:
|
||||
if (!(h = __zipos_alloc(zipos, 0))) return -1;
|
||||
h->mem = ZIP_LFILE_CONTENT(zipos->map + lf);
|
||||
break;
|
||||
case kZipCompressionDeflate:
|
||||
if (!(h = __zipos_alloc(zipos, size))) return -1;
|
||||
if (!__inflate(h->data, size, ZIP_LFILE_CONTENT(zipos->map + lf),
|
||||
GetZipLfileCompressedSize(zipos->map + lf))) {
|
||||
h->mem = h->data;
|
||||
} else {
|
||||
h->mem = 0;
|
||||
eio();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
uint8_t lfile[kZipLfileHdrMinSize];
|
||||
uint64_t lf = GetZipCfileOffset(zipos->cdir + cf);
|
||||
int compressed = ZIP_CFILE_COMPRESSIONMETHOD(zipos->cdir + cf);
|
||||
if (!__zipos_method_is_supported(compressed) ||
|
||||
pread(fd, lfile, kZipLfileHdrMinSize, lf) != kZipLfileHdrMinSize ||
|
||||
ZIP_LFILE_MAGIC(lfile) != kZipLfileHdrMagic) {
|
||||
return eio(); // this corruption
|
||||
}
|
||||
size = GetZipCfileUncompressedSize(zipos->cdir + cf);
|
||||
if (!(h = __zipos_alloc(zipos, size))) return -1;
|
||||
uint64_t off = lf + ZIP_LFILE_HDRSIZE(lfile);
|
||||
if (!compressed) {
|
||||
if (pread(fd, h->data, size, off) != size) {
|
||||
__zipos_free(h);
|
||||
return eio();
|
||||
}
|
||||
} else {
|
||||
struct ZiposHandle *h2;
|
||||
uint64_t compsize = GetZipCfileCompressedSize(zipos->cdir + cf);
|
||||
if (!(h2 = __zipos_alloc(zipos, compsize))) {
|
||||
__zipos_free(h);
|
||||
return -1;
|
||||
}
|
||||
if (pread(fd, h2->data, compsize, off) != compsize ||
|
||||
__inflate(h->data, size, h2->data, compsize)) {
|
||||
__zipos_free(h2);
|
||||
__zipos_free(h);
|
||||
return eio();
|
||||
}
|
||||
__zipos_free(h2);
|
||||
}
|
||||
}
|
||||
h->pos = 0;
|
||||
h->cfile = cf;
|
||||
h->size = size;
|
||||
if (h->mem) {
|
||||
minfd = 3;
|
||||
__fds_lock();
|
||||
TryAgain:
|
||||
if (IsWindows() || IsMetal()) {
|
||||
if ((fd = __reservefd_unlocked(-1)) != -1) {
|
||||
return __zipos_setfd(fd, h, flags);
|
||||
*out_handle = h;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __zipos_load(struct Zipos *zipos, int cf, int flags,
|
||||
struct ZiposUri *name) {
|
||||
int fd;
|
||||
if ((fd = openat(AT_FDCWD, zipos->progpath,
|
||||
O_RDONLY | O_CLOEXEC |
|
||||
(flags & (O_NONBLOCK | O_RANDOM | O_SEQUENTIAL)),
|
||||
0)) != -1) {
|
||||
struct ZiposHandle *h = 0;
|
||||
if (__zipos_load_fd(zipos, cf, fd, name, &h) != -1) {
|
||||
if (!IsWindows() && !IsMetal()) {
|
||||
// unix doesn't use the g_fds table by default, so we need to
|
||||
// explicitly ensure an entry exists for our new open()d file
|
||||
__ensurefds(fd);
|
||||
}
|
||||
} else if ((fd = __zipos_mkfd(minfd)) != -1) {
|
||||
if (__ensurefds_unlocked(fd) != -1) {
|
||||
if (g_fds.p[fd].kind) {
|
||||
sys_close(fd);
|
||||
minfd = fd + 1;
|
||||
goto TryAgain;
|
||||
}
|
||||
return __zipos_setfd(fd, h, flags);
|
||||
}
|
||||
sys_close(fd);
|
||||
// turn it from a file fd to a zip fd
|
||||
// layer the handle on windows so it can be restored on close
|
||||
h->handle = g_fds.p[fd].handle;
|
||||
g_fds.p[fd].kind = kFdZip;
|
||||
g_fds.p[fd].handle = (intptr_t)h;
|
||||
g_fds.p[fd].flags &= ~O_CLOEXEC;
|
||||
g_fds.p[fd].flags |= flags & O_CLOEXEC;
|
||||
return fd;
|
||||
} else {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
__fds_unlock();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
__zipos_free(h);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -231,6 +213,10 @@ int __zipos_open(struct ZiposUri *name, int flags) {
|
|||
(flags & O_ACCMODE) != O_RDONLY) {
|
||||
return erofs();
|
||||
}
|
||||
if ((flags & ~(O_CLOEXEC | O_NONBLOCK | O_NOFOLLOW | O_NOCTTY | O_DIRECTORY |
|
||||
O_NOATIME | O_RANDOM | O_SEQUENTIAL))) {
|
||||
return einval();
|
||||
}
|
||||
|
||||
// get the zipos global singleton
|
||||
struct Zipos *zipos;
|
||||
|
@ -242,15 +228,12 @@ int __zipos_open(struct ZiposUri *name, int flags) {
|
|||
// majority of these calls will return ENOENT or ENOTDIR. we need to
|
||||
// perform two extremely costly sigprocmask() calls below. thanks to
|
||||
// zipos being a read-only filesystem, we can avoid it in many cases
|
||||
ssize_t cf;
|
||||
int cf;
|
||||
if ((cf = __zipos_find(zipos, name)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (flags & O_EXCL) {
|
||||
return eexist();
|
||||
}
|
||||
if (cf != ZIPOS_SYNTHETIC_DIRECTORY) {
|
||||
int mode = GetZipCfileMode(zipos->map + cf);
|
||||
int mode = GetZipCfileMode(zipos->cdir + cf);
|
||||
if ((flags & O_DIRECTORY) && !S_ISDIR(mode)) {
|
||||
return enotdir();
|
||||
}
|
||||
|
@ -265,8 +248,3 @@ int __zipos_open(struct ZiposUri *name, int flags) {
|
|||
ALLOW_SIGNALS;
|
||||
return rc;
|
||||
}
|
||||
|
||||
__attribute__((__constructor__)) static void __zipos_ctor(void) {
|
||||
__zipos_wipe();
|
||||
pthread_atfork(__zipos_lock, __zipos_unlock, __zipos_wipe);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue