diff options
author | Lizzy Fleckenstein <lizzy@vlhl.dev> | 2023-12-19 19:31:03 +0100 |
---|---|---|
committer | Lizzy Fleckenstein <lizzy@vlhl.dev> | 2023-12-19 19:41:57 +0100 |
commit | 42c69b59e1fdcf70219bc04a3124d2f35d9463ac (patch) | |
tree | 554838f5c4a759262563e3fb505d0a7097acf8ea /stage3/fs.c | |
parent | 3102878c86c810c0bf877d72aceefeb28a44271d (diff) | |
download | cuddles-42c69b59e1fdcf70219bc04a3124d2f35d9463ac.tar.xz |
ls command
Diffstat (limited to 'stage3/fs.c')
-rw-r--r-- | stage3/fs.c | 153 |
1 files changed, 143 insertions, 10 deletions
diff --git a/stage3/fs.c b/stage3/fs.c index d4a325f..5653e4e 100644 --- a/stage3/fs.c +++ b/stage3/fs.c @@ -4,29 +4,162 @@ #include "memory.h" #include "heap.h" -str fs_read(str filename) +#define FS_WALKER(X) bool (X)(str filename, u64 lba, usize fsize, usize fsect, void *varg) + +void fs_walk(FS_WALKER(*fun), void *arg) { - u64 start = (*(u32 *) (0x1000-10-8))/512; + u64 lba = (*(u32 *) (0x1000-10-8))/512; for (;;) { - u8 *info = ata_read_full(start, 1); + u8 *info = ata_read_full(lba, 1); if (memcmp(info+257, "ustar", 5) != 0) { free(info); - return NILS; + break; } - usize fsize; + u64 fsize; str_parse_num((str) { 11, (char *) info+124 }, 8, &fsize); usize fsect = (fsize+511)/512; + str filename = str_intro((char *) info); - if (memcmp(info, filename.data, filename.len) == 0 && info[filename.len] == '\0') { - free(info); - return (str) { .len = fsize, .data = ata_read_full(start+1, fsect) }; + bool done = fun(filename, lba, fsize, fsect, arg); + + free(info); + if (done) + break; + + lba += 1 + fsect; + } +} + +typedef struct { + str path; + str result; +} fs_read_arg; + +static FS_WALKER(fs_read_walker) +{ + fs_read_arg *arg = varg; + + if (str_cmp(filename, arg->path) != 0) + return false; + + arg->result = (str) { .len = fsize, .data = ata_read_full(lba+1, fsect) }; + return true; +} + +str fs_read(str path) +{ + fs_read_arg arg = { + .path = path, + .result = NILS, + }; + + fs_walk(&fs_read_walker, &arg); + return arg.result; +} + +typedef struct { + str path; + usize cap; + dir result; +} fs_readdir_arg; + +static str strip_trail(str x) +{ + while (x.len > 0 && x.data[x.len-1] == '/') + x.len--; + return x; +} + +static FS_WALKER(fs_readdir_walker) +{ + (void) lba, (void) fsect; + fs_readdir_arg *arg = varg; + + enum { + HER_SELF, + HER_CHILD, + HER_GRANDCHILD, + } heritage; + + str entn; + str iter = filename; + + bool match = str_start(iter, arg->path); + if (!match) + return false; + iter = str_advance(iter, arg->path.len); + + if (iter.len == 0) { + entn = filename; + heritage = HER_SELF; + } else { + if (arg->path.len != 0) { + if (iter.data[0] != '/') + return false; + iter = str_advance(iter, 1); + } + + if (iter.len == 0) { + entn = strip_trail(filename); + heritage = HER_SELF; } else { - free(info); - start += 1 + fsect; + entn = str_walk(&iter, S("/")); + if (iter.len == 0) + heritage = HER_CHILD; + else + heritage = HER_GRANDCHILD; } } + + bool is_dir = filename.len == 0 || filename.data[filename.len-1] == '/'; + + switch (heritage) { + case HER_SELF: + if (is_dir) + return false; + + __attribute__((fallthrough)); + + case HER_CHILD: + if (arg->result.len == arg->cap) + arg->result.data = realloc(arg->result.data, + sizeof(dirent) * (arg->cap = arg->cap ? arg->cap*2 : 1)); + arg->result.data[arg->result.len++] = (dirent) { + .name = str_clone(entn), + .size = fsize, + .is_dir = is_dir, + .children = 0, + }; + + return heritage == HER_SELF; + + case HER_GRANDCHILD: + for (usize i = 0; i < arg->result.len; i++) { + dirent *d = &arg->result.data[i]; + if (str_cmp(d->name, entn) == 0) { + d->size += fsize; + d->children++; + return false; + } + } + + // orphan. this shouldn't happen + return false; + }; +} + +dir fs_readdir(str path) +{ + fs_readdir_arg arg = { + .path = strip_trail(path), + .cap = 0, + .result = { 0, nil }, + }; + + fs_walk(&fs_readdir_walker, &arg); + return arg.result; } |