From d57f87dc4060e77ee12deaa450560a9baba1b66e Mon Sep 17 00:00:00 2001
From: Justine Tunney <jtunney@gmail.com>
Date: Mon, 21 Mar 2022 07:34:19 -0700
Subject: [PATCH] Fix some issues with zipos and redbean

- redbean.com -D /zip/dir/ now works, for pure fun
- possibly fixed bug with redbean serving empty files
- zipos stat() mode now indicates directories on windows

See #372
---
 libc/runtime/mmap.c    |  4 ++++
 libc/zipos/stat-impl.c |  6 +-----
 tool/net/net.mk        |  5 +++++
 tool/net/redbean.c     | 43 ++++++++++++++++++++++++++++++++++++++----
 4 files changed, 49 insertions(+), 9 deletions(-)

diff --git a/libc/runtime/mmap.c b/libc/runtime/mmap.c
index 7778519e4..01683b76b 100644
--- a/libc/runtime/mmap.c
+++ b/libc/runtime/mmap.c
@@ -273,6 +273,10 @@ noasan void *mmap(void *addr, size_t size, int prot, int flags, int fd,
     }
     return VIP(efault());
   }
+  if (__isfdkind(fd, kFdZip)) {
+    STRACE("mmap(%.12p, %'zu) EINVAL (fd is zipos handle)", p, size);
+    return VIP(einval());
+  }
   STRACE("mmap(%.12p, %'zu, %s, %d, %'ld)% m", p, size,
          DescribeMapping(prot, flags, mode), fd, off);
   if (fd == -1) {
diff --git a/libc/zipos/stat-impl.c b/libc/zipos/stat-impl.c
index 16b040828..a9d9558a3 100644
--- a/libc/zipos/stat-impl.c
+++ b/libc/zipos/stat-impl.c
@@ -28,12 +28,8 @@ int __zipos_stat_impl(struct Zipos *zipos, size_t cf, struct stat *st) {
   size_t lf;
   if (zipos && st) {
     bzero(st, sizeof(*st));
-    if (ZIP_CFILE_FILEATTRCOMPAT(zipos->map + cf) == kZipOsUnix) {
-      st->st_mode = ZIP_CFILE_EXTERNALATTRIBUTES(zipos->map + cf) >> 16;
-    } else {
-      st->st_mode = 0100644;
-    }
     lf = GetZipCfileOffset(zipos->map + cf);
+    st->st_mode = GetZipCfileMode(zipos->map + cf);
     st->st_size = GetZipLfileUncompressedSize(zipos->map + lf);
     st->st_blocks =
         roundup(GetZipLfileCompressedSize(zipos->map + lf), 512) / 512;
diff --git a/tool/net/net.mk b/tool/net/net.mk
index 582d6ff7d..ca52719d2 100644
--- a/tool/net/net.mk
+++ b/tool/net/net.mk
@@ -130,6 +130,7 @@ o/$(MODE)/tool/net/demo/404.html.zip.o:						\
 		ZIPOBJ_FLAGS +=							\
 			-B
 
+o/$(MODE)/tool/net/demo/.lua/.zip.o						\
 o/$(MODE)/tool/net/demo/.lua/mymodule.lua.zip.o:				\
 		ZIPOBJ_FLAGS +=							\
 			-C3
@@ -161,6 +162,9 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg:					\
 		o/$(MODE)/tool/net/demo/printpayload.lua.zip.o			\
 		o/$(MODE)/tool/net/demo/redbean-form.lua.zip.o			\
 		o/$(MODE)/tool/net/demo/redbean-xhr.lua.zip.o			\
+		o/$(MODE)/tool/.zip.o						\
+		o/$(MODE)/tool/net/.zip.o					\
+		o/$(MODE)/tool/net/demo/.zip.o					\
 		o/$(MODE)/tool/net/demo/index.html.zip.o			\
 		o/$(MODE)/tool/net/demo/redbean.css.zip.o			\
 		o/$(MODE)/tool/net/redbean.png.zip.o				\
@@ -168,6 +172,7 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg:					\
 		o/$(MODE)/tool/net/demo/404.html.zip.o				\
 		o/$(MODE)/tool/net/demo/seekable.txt.zip.o			\
 		o/$(MODE)/tool/net/demo/virtualbean.html.zip.o			\
+		o/$(MODE)/tool/net/demo/.lua/.zip.o				\
 		o/$(MODE)/tool/net/demo/.lua/mymodule.lua.zip.o			\
 		o/$(MODE)/tool/net/demo/.reload.lua.zip.o			\
 		o/$(MODE)/tool/net/demo/.init.lua.zip.o				\
diff --git a/tool/net/redbean.c b/tool/net/redbean.c
index 2cf6b473a..f45fe5053 100644
--- a/tool/net/redbean.c
+++ b/tool/net/redbean.c
@@ -50,6 +50,7 @@
 #include "libc/rand/rand.h"
 #include "libc/runtime/clktck.h"
 #include "libc/runtime/directmap.internal.h"
+#include "libc/runtime/gc.h"
 #include "libc/runtime/gc.internal.h"
 #include "libc/runtime/runtime.h"
 #include "libc/runtime/stack.h"
@@ -129,6 +130,7 @@
 #include "tool/build/lib/psk.h"
 
 STATIC_STACK_SIZE(0x40000);
+STATIC_YOINK("zip_uri_support");
 
 /**
  * @fileoverview redbean - single-file distributable web server
@@ -155,6 +157,7 @@ STATIC_STACK_SIZE(0x40000);
 #endif
 
 #define VERSION          0x010500
+#define HEARTBEAT        5000 /*ms*/
 #define HASH_LOAD_FACTOR /* 1. / */ 4
 #define read(F, P, N)    readv(F, &(struct iovec){P, N}, 1)
 #define write(F, P, N)   writev(F, &(struct iovec){P, N}, 1)
@@ -940,7 +943,7 @@ static void ProgramDirectory(const char *path) {
   size_t n;
   struct stat st;
 
-  if(stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) {
+  if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) {
     DIEF("(cfg) error: not a directory: %`'s", path);
   }
 
@@ -1287,6 +1290,22 @@ static void ReapZombies(void) {
   } while (!terminated);
 }
 
+static ssize_t ReadAll(int fd, const char *p, size_t n) {
+  ssize_t rc;
+  size_t i, got;
+  for (i = 0; i < n;) {
+    rc = read(fd, p + i, n - i);
+    if (rc != -1) {
+      got = rc;
+      i += got;
+    } else if (errno != EINTR) {
+      WARNF("(file) read error: %m");
+      return -1;
+    }
+  }
+  return i;
+}
+
 static ssize_t WritevAll(int fd, struct iovec *iov, int iovlen) {
   int i;
   ssize_t rc;
@@ -3525,9 +3544,11 @@ static void StoreFile(char *path) {
   if (startswith(target, "./")) target += 2;
   tlen = strlen(target);
   if (!IsReasonablePath(target, tlen))
-    DIEF("(cfg) error: can't store %`'s: contains '.' or '..' segments", target);
+    DIEF("(cfg) error: can't store %`'s: contains '.' or '..' segments",
+         target);
   if (lstat(path, &st) == -1) DIEF("(cfg) error: can't stat %`'s: %m", path);
-  if (!(p = xslurp(path, &plen))) DIEF("(cfg) error: can't read %`'s: %m", path);
+  if (!(p = xslurp(path, &plen)))
+    DIEF("(cfg) error: can't read %`'s: %m", path);
   StoreAsset(target, tlen, p, plen, st.st_mode & 0777);
   free(p);
 }
@@ -6144,6 +6165,7 @@ static char *OpenAsset(struct Asset *a) {
   int fd;
   void *data;
   size_t size;
+  struct stat *st;
   if (a->file->st.st_size) {
     size = a->file->st.st_size;
     if (msg.method == kHttpHead) {
@@ -6158,6 +6180,18 @@ static char *OpenAsset(struct Asset *a) {
           UnmapLater(fd, data, size);
           content = data;
           contentlength = size;
+        } else if ((st = _gc(malloc(sizeof(struct stat)))) &&
+                   fstat(fd, st) != -1 && (data = malloc(st->st_size))) {
+          /* probably empty file or zipos handle */
+          LockInc(&shared->c.slurps);
+          FreeLater(data);
+          if (ReadAll(fd, data, st->st_size) != -1) {
+            content = data;
+            contentlength = st->st_size;
+            close(fd);
+          } else {
+            return HandleMapFailed(a, fd);
+          }
         } else {
           return HandleMapFailed(a, fd);
         }
@@ -6979,7 +7013,7 @@ static void HandleConnection(size_t i) {
 
 static void HandlePoll(void) {
   size_t i;
-  if (poll(polls, servers.n, 500) != -1) {
+  if (poll(polls, servers.n, HEARTBEAT) != -1) {
     for (i = 0; i < servers.n; ++i) {
       if (polls[i].revents) {
         serveraddr = &servers.p[i].addr;
@@ -7343,6 +7377,7 @@ void RedBean(int argc, char *argv[]) {
 }
 
 int main(int argc, char *argv[]) {
+  int fd;
   if (!IsTiny()) {
     setenv("GDB", "", true);
     ShowCrashReports();