From ececec4c943bc644186091d156d43e1475589bc5 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 18 Sep 2023 22:17:56 -0700 Subject: [PATCH] Fix some zipos directory related bugs --- libc/runtime/zipos-find.c | 46 ++++++++++++++++++++---- libc/runtime/zipos-get.c | 3 +- libc/sock/epoll.c | 2 +- libc/stdio/dirstream.c | 16 ++++++--- libc/testlib-test.txt | 1 + libc/testlib/testlib.mk | 3 +- test/libc/stdio/zipdir_test.c | 64 ++++++++++++++++++++++++++++++---- third_party/mbedtls/test/lib.c | 9 +---- 8 files changed, 114 insertions(+), 30 deletions(-) create mode 100644 libc/testlib-test.txt diff --git a/libc/runtime/zipos-find.c b/libc/runtime/zipos-find.c index 8cd028c31..bdfb7038b 100644 --- a/libc/runtime/zipos-find.c +++ b/libc/runtime/zipos-find.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/kprintf.h" #include "libc/macros.internal.h" #include "libc/runtime/zipos.internal.h" #include "libc/str/str.h" @@ -23,6 +24,22 @@ #include "libc/sysv/errfuns.h" #include "libc/zip.internal.h" +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; + } else if (len + 1 < zsize && zname[len] == '/' && + !memcmp(name->path, zname, len)) { + return ZIPOS_SYNTHETIC_DIRECTORY; + } else { + return -1; + } +} + ssize_t __zipos_scan(struct Zipos *zipos, struct ZiposUri *name) { // strip trailing slash from search name @@ -61,17 +78,32 @@ ssize_t __zipos_scan(struct Zipos *zipos, struct ZiposUri *name) { } } - // return pointer to leftmost record if it matches if (l < zipos->records) { + int dx; size_t cfile = zipos->index[l]; const char *zname = ZIP_CFILE_NAME(zipos->map + cfile); int zsize = ZIP_CFILE_NAMESIZE(zipos->map + cfile); - if ((len == zsize || (len + 1 == zsize && zname[len] == '/')) && - !memcmp(name->path, zname, len)) { - return cfile; - } else if (len + 1 < zsize && zname[len] == '/' && - !memcmp(name->path, zname, len)) { - return ZIPOS_SYNTHETIC_DIRECTORY; + 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->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->map + cfile); + zsize = ZIP_CFILE_NAMESIZE(zipos->map + cfile); + if (zsize < len || (len && zname[len - 1] != name->path[len - 1])) { + break; + } + } + } else { + // otherwise just return pointer to leftmost record if it matches + return __zipos_match(zipos, name, len, l); } } diff --git a/libc/runtime/zipos-get.c b/libc/runtime/zipos-get.c index 31a3ab5dd..0565232e5 100644 --- a/libc/runtime/zipos-get.c +++ b/libc/runtime/zipos-get.c @@ -62,8 +62,7 @@ static void __zipos_dismiss(uint8_t *map, const uint8_t *cdir, long pg) { mo = ROUNDDOWN(lo, FRAMESIZE); if (mo) munmap(map, mo); - // this is supposed to reduce our rss usage but does it - pg = getauxval(AT_PAGESZ); + // 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) { diff --git a/libc/sock/epoll.c b/libc/sock/epoll.c index d90db45ba..e01d9458e 100644 --- a/libc/sock/epoll.c +++ b/libc/sock/epoll.c @@ -948,7 +948,7 @@ static textwindows int sock_update(struct PortState *port_state, updated event mask. */ } else if (sock_state->poll_status == kPollPending) { /* A poll operation is already pending, but it's not monitoring for - all the *events that the user is interested in .Therefore, cancel + all the *events that the user is interested in. Therefore, cancel the pending *poll operation; when we receive it's completion package, a new poll *operation will be submitted with the correct event mask. */ diff --git a/libc/stdio/dirstream.c b/libc/stdio/dirstream.c index 3fba4f06d..c696481c3 100644 --- a/libc/stdio/dirstream.c +++ b/libc/stdio/dirstream.c @@ -404,16 +404,22 @@ static struct dirent *readdir_zipos(DIR *dir) { s += dir->zip.prefix.len; n -= dir->zip.prefix.len; const char *p = memchr(s, '/', n); - if (p) n = p - s; + int d_type; + if (p) { + n = p - s; + d_type = DT_DIR; + } else if (S_ISDIR(GetZipCfileMode(dir->zip.zipos->map + + dir->zip.offset))) { + d_type = DT_DIR; + } else { + d_type = DT_REG; + } if ((n = MIN(n, sizeof(ent->d_name) - 1)) && critbit0_emplace(&dir->zip.found, s, n) == 1) { ent = &dir->ent; ent->d_ino = dir->zip.offset; ent->d_off = dir->tell; - ent->d_type = - S_ISDIR(GetZipCfileMode(dir->zip.zipos->map + dir->zip.offset)) - ? DT_DIR - : DT_REG; + ent->d_type = d_type; memcpy(ent->d_name, s, n); ent->d_name[n] = 0; } diff --git a/libc/testlib-test.txt b/libc/testlib-test.txt new file mode 100644 index 000000000..375fb8ee0 --- /dev/null +++ b/libc/testlib-test.txt @@ -0,0 +1 @@ +ignore this file diff --git a/libc/testlib/testlib.mk b/libc/testlib/testlib.mk index ac55a121b..118e355d5 100644 --- a/libc/testlib/testlib.mk +++ b/libc/testlib/testlib.mk @@ -87,7 +87,8 @@ LIBC_TESTLIB_A_SRCS = \ LIBC_TESTLIB_A_OBJS = \ $(LIBC_TESTLIB_A_SRCS_C:%.c=o/$(MODE)/%.o) \ $(LIBC_TESTLIB_A_SRCS_S:%.S=o/$(MODE)/%.o) \ - $(LIBC_TESTLIB_A_ASSETS:%=o/$(MODE)/%.zip.o) + $(LIBC_TESTLIB_A_ASSETS:%=o/$(MODE)/%.zip.o) \ + o/$(MODE)/libc/testlib-test.txt.zip.o LIBC_TESTLIB_A_DIRECTDEPS = \ LIBC_CALLS \ diff --git a/test/libc/stdio/zipdir_test.c b/test/libc/stdio/zipdir_test.c index cd661cf22..1e49da23d 100644 --- a/test/libc/stdio/zipdir_test.c +++ b/test/libc/stdio/zipdir_test.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" #include "libc/calls/struct/dirent.h" #include "libc/calls/struct/stat.h" #include "libc/errno.h" @@ -28,48 +29,99 @@ #include "libc/str/str.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/consts/dt.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/s.h" #include "libc/testlib/testlib.h" __static_yoink("zipos"); __static_yoink("libc/testlib/hyperion.txt"); __static_yoink("libc/testlib/moby.txt"); +__static_yoink("libc/testlib-test.txt"); +__static_yoink("usr/share/zoneinfo/"); __static_yoink("usr/share/zoneinfo/New_York"); DIR *dir; struct dirent *ent; -TEST(zipdir, test) { +TEST(zipdir, testDirectoryThatHasRealZipEntry) { const char *path = "/zip/libc/testlib///"; ASSERT_NE(NULL, (dir = opendir(path))); ASSERT_EQ(0, telldir(dir)); ASSERT_NE(NULL, (ent = readdir(dir))); - ASSERT_EQ(0, strcmp(ent->d_name, ".")); + ASSERT_STREQ(".", ent->d_name); ASSERT_EQ(DT_DIR, ent->d_type); ASSERT_NE(NULL, (ent = readdir(dir))); - ASSERT_EQ(0, strcmp(ent->d_name, "..")); + ASSERT_STREQ("..", ent->d_name); ASSERT_EQ(DT_DIR, ent->d_type); ASSERT_NE(NULL, (ent = readdir(dir))); - ASSERT_EQ(0, strcmp(ent->d_name, "hyperion.txt")); + ASSERT_STREQ("hyperion.txt", ent->d_name); ASSERT_EQ(DT_REG, ent->d_type); long pos = telldir(dir); ASSERT_NE(NULL, (ent = readdir(dir))); - ASSERT_EQ(0, strcmp(ent->d_name, "moby.txt")); + ASSERT_STREQ("moby.txt", ent->d_name); ASSERT_EQ(DT_REG, ent->d_type); ASSERT_EQ(NULL, (ent = readdir(dir))); seekdir(dir, pos); ASSERT_NE(NULL, (ent = readdir(dir))); - ASSERT_EQ(0, strcmp(ent->d_name, "moby.txt")); + ASSERT_STREQ("moby.txt", ent->d_name); ASSERT_EQ(DT_REG, ent->d_type); ASSERT_EQ(NULL, (ent = readdir(dir))); ASSERT_EQ(NULL, (ent = readdir(dir))); ASSERT_SYS(0, 0, closedir(dir)); } +TEST(zipdir, testDirectoryWithSyntheticOrMissingEntry) { + const char *path = "/zip/libc/"; + ASSERT_NE(NULL, (dir = opendir(path))); + ASSERT_EQ(0, telldir(dir)); + ASSERT_NE(NULL, (ent = readdir(dir))); + ASSERT_STREQ(".", ent->d_name); + ASSERT_EQ(DT_DIR, ent->d_type); + ASSERT_NE(NULL, (ent = readdir(dir))); + ASSERT_STREQ("..", ent->d_name); + ASSERT_EQ(DT_DIR, ent->d_type); + ASSERT_NE(NULL, (ent = readdir(dir))); + ASSERT_STREQ("testlib", ent->d_name); + ASSERT_EQ(DT_DIR, ent->d_type); + ASSERT_NE(NULL, (ent = readdir(dir))); + ASSERT_STREQ("testlib-test.txt", ent->d_name); + ASSERT_EQ(DT_REG, ent->d_type); + ASSERT_EQ(NULL, (ent = readdir(dir))); + ASSERT_SYS(0, 0, closedir(dir)); +} + +TEST(zipdir, testListZip) { + const char *path = "/zip"; + ASSERT_NE(NULL, (dir = opendir(path))); + ASSERT_EQ(0, telldir(dir)); + ASSERT_NE(NULL, (ent = readdir(dir))); + ASSERT_STREQ(".", ent->d_name); + ASSERT_EQ(DT_DIR, ent->d_type); + ASSERT_NE(NULL, (ent = readdir(dir))); + ASSERT_STREQ("..", ent->d_name); + ASSERT_EQ(DT_DIR, ent->d_type); + ASSERT_NE(NULL, (ent = readdir(dir))); + ASSERT_STREQ("echo.com", ent->d_name); + ASSERT_EQ(DT_REG, ent->d_type); + ASSERT_NE(NULL, (ent = readdir(dir))); + ASSERT_STREQ("libc", ent->d_name); + ASSERT_EQ(DT_DIR, ent->d_type); + ASSERT_NE(NULL, (ent = readdir(dir))); + ASSERT_STREQ("usr", ent->d_name); + ASSERT_EQ(DT_DIR, ent->d_type); + ASSERT_NE(NULL, (ent = readdir(dir))); + ASSERT_STREQ(".cosmo", ent->d_name); + ASSERT_EQ(DT_REG, ent->d_type); + ASSERT_EQ(NULL, (ent = readdir(dir))); + ASSERT_SYS(0, 0, closedir(dir)); +} + TEST(dirstream, hasDirectoryEntry) { struct stat st; bool gotsome = false; const char *path = "/zip/usr/share/zoneinfo"; ASSERT_SYS(0, 0, fstatat(AT_FDCWD, path, &st, 0)); + ASSERT_TRUE(S_ISDIR(st.st_mode)); ASSERT_NE(NULL, (dir = opendir(path))); while ((ent = readdir(dir))) { gotsome = true; diff --git a/third_party/mbedtls/test/lib.c b/third_party/mbedtls/test/lib.c index af4153e27..f18afdc31 100644 --- a/third_party/mbedtls/test/lib.c +++ b/third_party/mbedtls/test/lib.c @@ -172,15 +172,8 @@ int mbedtls_test_write(const char *fmt, ...) { n = vfprintf(stderr, fmt, va); } else { char buf[512]; - const char *s; vsnprintf(buf, 512, fmt, va); - if ((s = strchr(buf, '\n')) && // - s == buf + strlen(buf) - 1 && // - strstr(buf, "PASS")) { - n = 0; // ignore pointless success lines - } else { - n = appends(&output, buf); - } + n = appends(&output, buf); } va_end(va); return n;