Skip to content

Commit 1ef955c

Browse files
authored
Improve zipos path handling (#579)
This change adds opendir, readdir, and stat support for the /zip/ root, as well as directories not explicitly encoded in the zip file.
1 parent 494d742 commit 1ef955c

File tree

5 files changed

+73
-10
lines changed

5 files changed

+73
-10
lines changed

libc/stdio/dirstream.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ static struct dirent *readdir_impl(DIR *dir) {
326326
uint8_t *s, *p;
327327
struct Zipos *zip;
328328
struct dirent *ent;
329+
struct dirent *lastent;
329330
struct dirent_bsd *bsd;
330331
struct dirent_netbsd *nbsd;
331332
struct dirent_openbsd *obsd;
@@ -351,6 +352,20 @@ static struct dirent *readdir_impl(DIR *dir) {
351352
ent->d_type = S_ISDIR(mode) ? DT_DIR : DT_REG;
352353
memcpy(ent->d_name, s, ent->d_reclen);
353354
ent->d_name[ent->d_reclen] = 0;
355+
} else {
356+
lastent = (struct dirent *)dir->buf;
357+
n = p - s;
358+
n = MIN(n, 255);
359+
if (!lastent->d_ino || (n != lastent->d_reclen) ||
360+
memcmp(lastent->d_name, s, n)) {
361+
ent = lastent;
362+
ent->d_ino++;
363+
ent->d_off = -1;
364+
ent->d_reclen = n;
365+
ent->d_type = DT_DIR;
366+
memcpy(ent->d_name, s, ent->d_reclen);
367+
ent->d_name[ent->d_reclen] = 0;
368+
}
354369
}
355370
}
356371
dir->zip.offset += ZIP_CFILE_HDRSIZE(zip->map + dir->zip.offset);

libc/zipos/find.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
ssize_t __zipos_find(struct Zipos *zipos, const struct ZiposUri *name) {
2929
const char *zname;
3030
size_t i, n, c, znamesize;
31+
if (!name->len) {
32+
return 0;
33+
}
3134
c = GetZipCdirOffset(zipos->cdir);
3235
n = GetZipCdirRecords(zipos->cdir);
3336
for (i = 0; i < n; ++i, c += ZIP_CFILE_HDRSIZE(zipos->map + c)) {
@@ -38,6 +41,13 @@ ssize_t __zipos_find(struct Zipos *zipos, const struct ZiposUri *name) {
3841
(name->len + 1 == znamesize && !memcmp(name->path, zname, name->len) &&
3942
zname[name->len] == '/')) {
4043
return c;
44+
} else if ((name->len < znamesize &&
45+
!memcmp(name->path, zname, name->len) &&
46+
zname[name->len - 1] == '/') ||
47+
(name->len + 1 < znamesize &&
48+
!memcmp(name->path, zname, name->len) &&
49+
zname[name->len] == '/')) {
50+
return 0;
4151
}
4252
}
4353
return -1;

libc/zipos/stat-impl.c

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
19-
#include "libc/intrin/safemacros.internal.h"
2019
#include "libc/calls/struct/stat.h"
20+
#include "libc/intrin/safemacros.internal.h"
2121
#include "libc/str/str.h"
22+
#include "libc/sysv/consts/s.h"
2223
#include "libc/sysv/errfuns.h"
2324
#include "libc/zip.h"
2425
#include "libc/zipos/zipos.internal.h"
@@ -27,14 +28,18 @@ int __zipos_stat_impl(struct Zipos *zipos, size_t cf, struct stat *st) {
2728
size_t lf;
2829
if (zipos && st) {
2930
bzero(st, sizeof(*st));
30-
lf = GetZipCfileOffset(zipos->map + cf);
31-
st->st_mode = GetZipCfileMode(zipos->map + cf);
32-
st->st_size = GetZipLfileUncompressedSize(zipos->map + lf);
33-
st->st_blocks =
34-
roundup(GetZipLfileCompressedSize(zipos->map + lf), 512) / 512;
35-
GetZipCfileTimestamps(zipos->map + cf, &st->st_mtim, &st->st_atim,
36-
&st->st_ctim, 0);
37-
st->st_birthtim = st->st_ctim;
31+
if (cf) {
32+
lf = GetZipCfileOffset(zipos->map + cf);
33+
st->st_mode = GetZipCfileMode(zipos->map + cf);
34+
st->st_size = GetZipLfileUncompressedSize(zipos->map + lf);
35+
st->st_blocks =
36+
roundup(GetZipLfileCompressedSize(zipos->map + lf), 512) / 512;
37+
GetZipCfileTimestamps(zipos->map + cf, &st->st_mtim, &st->st_atim,
38+
&st->st_ctim, 0);
39+
st->st_birthtim = st->st_ctim;
40+
} else {
41+
st->st_mode = 0444 | S_IFDIR | 0111;
42+
}
3843
return 0;
3944
} else {
4045
return einval();

test/libc/calls/stat_test.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ TEST(stat, zipos) {
6464
stat("/zip/.python/test/"
6565
"tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt",
6666
&st));
67+
EXPECT_SYS(0, 0, stat("/zip", &st));
68+
EXPECT_SYS(0, 0, stat("/zip/", &st));
69+
EXPECT_SYS(0, 0, stat("/zip/.python", &st));
70+
EXPECT_SYS(0, 0, stat("/zip/.python/", &st));
6771
}
6872

6973
static long Stat(const char *path, struct stat *st) {

test/libc/stdio/dirstream_test.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
#include "libc/calls/struct/dirent.h"
2121
#include "libc/dce.h"
2222
#include "libc/errno.h"
23-
#include "libc/stdio/rand.h"
2423
#include "libc/runtime/gc.internal.h"
2524
#include "libc/runtime/runtime.h"
25+
#include "libc/stdio/rand.h"
2626
#include "libc/str/str.h"
2727
#include "libc/sysv/consts/dt.h"
2828
#include "libc/testlib/testlib.h"
@@ -57,6 +57,35 @@ TEST(opendir, enotdir) {
5757
ASSERT_SYS(ENOTDIR, NULL, opendir("yo/there"));
5858
}
5959

60+
TEST(opendir, zipTest_fake) {
61+
ASSERT_NE(NULL, (dir = opendir("/zip")));
62+
EXPECT_NE(NULL, (ent = readdir(dir)));
63+
EXPECT_STREQ("echo", ent->d_name);
64+
EXPECT_NE(NULL, (ent = readdir(dir)));
65+
EXPECT_STREQ("usr", ent->d_name);
66+
EXPECT_EQ(NULL, (ent = readdir(dir)));
67+
EXPECT_EQ(0, closedir(dir));
68+
ASSERT_NE(NULL, (dir = opendir("/zip/")));
69+
EXPECT_NE(NULL, (ent = readdir(dir)));
70+
EXPECT_STREQ("echo", ent->d_name);
71+
EXPECT_NE(NULL, (ent = readdir(dir)));
72+
EXPECT_STREQ("usr", ent->d_name);
73+
EXPECT_EQ(NULL, (ent = readdir(dir)));
74+
EXPECT_EQ(0, closedir(dir));
75+
ASSERT_NE(NULL, (dir = opendir("/zip/usr")));
76+
EXPECT_NE(NULL, (ent = readdir(dir)));
77+
EXPECT_STREQ("share", ent->d_name);
78+
EXPECT_EQ(NULL, (ent = readdir(dir)));
79+
EXPECT_EQ(0, closedir(dir));
80+
ASSERT_NE(NULL, (dir = opendir("/zip/usr/")));
81+
EXPECT_NE(NULL, (ent = readdir(dir)));
82+
EXPECT_STREQ("share", ent->d_name);
83+
EXPECT_EQ(NULL, (ent = readdir(dir)));
84+
EXPECT_EQ(0, closedir(dir));
85+
EXPECT_EQ(NULL, (dir = opendir("/zip/us")));
86+
EXPECT_EQ(NULL, (dir = opendir("/zip/us/")));
87+
}
88+
6089
TEST(dirstream, testDots) {
6190
int hasdot = 0;
6291
int hasdotdot = 0;

0 commit comments

Comments
 (0)