diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/kernel/arch/amd64/driver/fsroot.c | 1 | ||||
-rw-r--r-- | src/user/app/tmpfs/tmpfs.c | 11 | ||||
-rw-r--r-- | src/user/bootstrap/tar.c | 76 | ||||
-rw-r--r-- | src/user/lib/fs/dir.c | 43 | ||||
-rw-r--r-- | src/user/lib/fs/misc.c | 23 |
5 files changed, 94 insertions, 60 deletions
diff --git a/src/kernel/arch/amd64/driver/fsroot.c b/src/kernel/arch/amd64/driver/fsroot.c index ea9fa7d..882fe91 100644 --- a/src/kernel/arch/amd64/driver/fsroot.c +++ b/src/kernel/arch/amd64/driver/fsroot.c @@ -77,6 +77,7 @@ static int handle(struct vfs_request *req) { } case VFSOP_CLOSE: return 0; + // TODO getsize for kernel provided directories case VFSOP_GETSIZE: return -ENOSYS; default: panic_invalid_state(); diff --git a/src/user/app/tmpfs/tmpfs.c b/src/user/app/tmpfs/tmpfs.c index 88a9fac..ed3c2e3 100644 --- a/src/user/app/tmpfs/tmpfs.c +++ b/src/user/app/tmpfs/tmpfs.c @@ -77,7 +77,6 @@ int main(void) { } else { fs_normslice(&res.offset, &res.len, ptr->size, false); _syscall_fs_respond(ptr->buf + res.offset, res.len, 0); - break; } break; @@ -119,7 +118,15 @@ int main(void) { case VFSOP_GETSIZE: ptr = (void*)res.id; - _syscall_fs_respond(NULL, ptr->size, 0); + if (ptr == &special_root) { + struct dirbuild db; + dir_start(&db, res.offset, NULL, buflen); + for (struct node *iter = root; iter; iter = iter->next) + dir_append(&db, iter->name); + _syscall_fs_respond(NULL, dir_finish(&db), 0); + } else { + _syscall_fs_respond(NULL, ptr->size, 0); + } break; default: diff --git a/src/user/bootstrap/tar.c b/src/user/bootstrap/tar.c index 6bd4179..10c3701 100644 --- a/src/user/bootstrap/tar.c +++ b/src/user/bootstrap/tar.c @@ -11,6 +11,8 @@ #define BUF_SIZE 64 static void *tar_open(const char *path, int len, void *base, size_t base_len); +static char tar_type(void *meta); +static void tar_dirbuild(struct dirbuild *db, const char *meta, void *base, size_t base_len); static void tar_read(struct fs_wait_response *res, void *base, size_t base_len); static int tar_size(void *sector); static int oct_parse(char *str, size_t len); @@ -35,8 +37,14 @@ void tar_driver(void *base) { break; case VFSOP_GETSIZE: - // TODO works weirdly on directories / the root dir - _syscall_fs_respond(NULL, tar_size(res.id), 0); + if (tar_type(res.id) != '5') { + _syscall_fs_respond(NULL, tar_size(res.id), 0); + } else { + struct dirbuild db; + dir_start(&db, res.offset, NULL, 0); + tar_dirbuild(&db, res.id, base, ~0); + _syscall_fs_respond(NULL, dir_finish(&db), 0); + } break; default: @@ -47,6 +55,11 @@ void tar_driver(void *base) { exit(0); } +static char tar_type(void *meta) { + if (meta == root_fakemeta) return '5'; + return *(char*)(meta + 156); +} + static void *tar_open(const char *path, int len, void *base, size_t base_len) { if (len <= 0) return NULL; path += 1; // skip the leading slash @@ -61,51 +74,46 @@ static void *tar_open(const char *path, int len, void *base, size_t base_len) { return tar_find(path, len, base, base_len); } +static void tar_dirbuild(struct dirbuild *db, const char *meta, void *base, size_t base_len) { + size_t meta_len = strlen(meta); + for (size_t off = 0; off < base_len;) { + if (0 != memcmp(base + off + 257, "ustar", 5)) + break; // not a metadata sector + + /* check if prefix matches */ + if (0 == memcmp(base + off, meta, meta_len) + && *(char*)(base + off + meta_len) != '\0') { + char *suffix = base + off + meta_len; + + /* check if the path contains any non-trailing slashes */ + char *slash = strchr(suffix, '/'); + if (!slash || slash[1] == '\0') { + if (dir_append(db, suffix)) break; + } + } + + int size = tar_size(base + off); + off += 512; // skip this metadata sector + off += (size + 511) & ~511; // skip the data sectors + } +} + static void tar_read(struct fs_wait_response *res, void *base, size_t base_len) { void *meta = (void*)res->id; - char type = *(char*)(meta + 156); - size_t meta_len; - int size; - static char buf[BUF_SIZE]; // TODO reuse a single buffer for both tar_driver and tar_read - if (meta == root_fakemeta) type = '5'; /* see comment in tar_open() */ - - switch (type) { + switch (tar_type(meta)) { case '\0': case '0': /* normal files */ - size = tar_size(meta); - fs_normslice(&res->offset, &res->len, size, false); + fs_normslice(&res->offset, &res->len, tar_size(meta), false); _syscall_fs_respond(meta + 512 + res->offset, res->len, 0); break; case '5': /* directory */ struct dirbuild db; dir_start(&db, res->offset, buf, sizeof buf); - - meta_len = strlen(meta); - for (size_t off = 0; off < base_len;) { - if (0 != memcmp(base + off + 257, "ustar", 5)) - break; // not a metadata sector - - /* check if prefix matches */ - if (0 == memcmp(base + off, meta, meta_len) - && *(char*)(base + off + meta_len) != '\0') { - char *suffix = base + off + meta_len; - - /* check if the path contains any non-trailing slashes */ - char *slash = strchr(suffix, '/'); - if (!slash || slash[1] == '\0') { - if (dir_append(&db, suffix)) break; - } - } - - size = tar_size(base + off); - off += 512; // skip this metadata sector - off += (size + 511) & ~511; // skip the data sectors - } - + tar_dirbuild(&db, meta, base, base_len); _syscall_fs_respond(buf, dir_finish(&db), 0); break; diff --git a/src/user/lib/fs/dir.c b/src/user/lib/fs/dir.c index 802dd61..4235ea6 100644 --- a/src/user/lib/fs/dir.c +++ b/src/user/lib/fs/dir.c @@ -33,9 +33,11 @@ bool dir_appendl(struct dirbuild *db, const char *name, size_t len) { len -= db->offset; db->offset = 0; - // TODO no buffer overrun check - memcpy(db->buf + db->bpos, name, len - 1); - db->buf[db->bpos + len - 1] = '\0'; + if (db->buf) { + // TODO no buffer overrun check + memcpy(db->buf + db->bpos, name, len - 1); + db->buf[db->bpos + len - 1] = '\0'; + } db->bpos += len; } else { db->offset -= len; @@ -45,26 +47,35 @@ bool dir_appendl(struct dirbuild *db, const char *name, size_t len) { bool dir_append_from(struct dirbuild *db, handle_t h) { if (db->error) return true; + if (db->buf && db->bpos == db->blen) return false; - if (db->bpos == db->blen) - return false; + int ret; + if (db->buf) { + ret = _syscall_read(h, db->buf + db->bpos, db->blen - db->bpos, db->offset); + if (ret < 0) { + db->error = ret; + return true; + } else if (ret > 0) { + /* not eof */ + db->offset = 0; + db->bpos += ret; + return false; + } /* else ret == 0, EOF, need getsize */ + } - int ret = _syscall_read(h, db->buf + db->bpos, db->blen - db->bpos, db->offset); + ret = _syscall_getsize(h); if (ret < 0) { db->error = ret; return true; } - if (ret == 0) { - // TODO no idea how much we've overread - // this messes up reading bind mounts of multiple directories - db->error = -ENOSYS; - return true; + if (db->offset < ret) { + /* should only occur when !buf, otherwise leaks previous data from buf. + * TODO consider impact */ + db->bpos += ret - db->offset; + db->offset = 0; + } else { + db->offset -= ret; } - - // TODO deduplicate entries - - db->offset = 0; - db->bpos += ret; return false; } diff --git a/src/user/lib/fs/misc.c b/src/user/lib/fs/misc.c index 54f54fd..16e95e7 100644 --- a/src/user/lib/fs/misc.c +++ b/src/user/lib/fs/misc.c @@ -74,9 +74,11 @@ void fs_whitelist(const char **list) { char *buf = malloc(buf_len); char *ipath; bool passthru, inject; + struct dirbuild db; if (!buf) exit(1); while (!_syscall_fs_wait(buf, buf_len, &res)) { + size_t blen; ipath = res.id; switch (res.op) { case VFSOP_OPEN: @@ -110,16 +112,17 @@ void fs_whitelist(const char **list) { break; case VFSOP_READ: - struct dirbuild db; - dir_start(&db, res.offset, buf, buf_len); - size_t blen = strlen(ipath); + case VFSOP_GETSIZE: + blen = strlen(ipath); + char *target = res.op == VFSOP_READ ? buf : NULL; + dir_start(&db, res.offset, target, buf_len); for (const char **iter = list; *iter; iter++) { // TODO could be precomputed too size_t len = strlen(*iter); // inefficient, whatever if (blen < len && !memcmp(ipath, *iter, blen)) dir_appendl(&db, *iter + blen, dir_seglen(*iter + blen)); } - _syscall_fs_respond(buf, dir_finish(&db), 0); + _syscall_fs_respond(target, dir_finish(&db), 0); break; case VFSOP_CLOSE: @@ -175,10 +178,12 @@ void fs_union(const char **list) { break; case VFSOP_READ: + case VFSOP_GETSIZE: if (res.capacity > buflen) res.capacity = buflen; bool end = false; - dir_start(&db, res.offset, pre, res.capacity); + char *target = res.op == VFSOP_READ ? pre : NULL; + dir_start(&db, res.offset, target, res.capacity); for (size_t i = 0; !end && list[i]; i++) { const char *prefix = list[i]; size_t prefixlen = strlen(prefix); @@ -189,7 +194,7 @@ void fs_union(const char **list) { end = end || dir_append_from(&db, h); _syscall_close(h); } - _syscall_fs_respond(pre, dir_finish(&db), 0); + _syscall_fs_respond(target, dir_finish(&db), 0); break; default: @@ -242,13 +247,15 @@ void fs_dir_inject(const char *path) { break; case VFSOP_READ: + case VFSOP_GETSIZE: if (res.capacity > buf_len) res.capacity = buf_len; - dir_start(&db, res.offset, buf, res.capacity); + char *target = res.op == VFSOP_READ ? buf : NULL; + dir_start(&db, res.offset, target, res.capacity); dir_appendl(&db, data->inject, data->inject_len); if (data->delegate >= 0) dir_append_from(&db, data->delegate); - _syscall_fs_respond(buf, dir_finish(&db), 0); + _syscall_fs_respond(target, dir_finish(&db), 0); break; default: |