summaryrefslogtreecommitdiff
path: root/stage3/fs.c
diff options
context:
space:
mode:
authorLizzy Fleckenstein <lizzy@vlhl.dev>2023-12-19 19:31:03 +0100
committerLizzy Fleckenstein <lizzy@vlhl.dev>2023-12-19 19:41:57 +0100
commit42c69b59e1fdcf70219bc04a3124d2f35d9463ac (patch)
tree554838f5c4a759262563e3fb505d0a7097acf8ea /stage3/fs.c
parent3102878c86c810c0bf877d72aceefeb28a44271d (diff)
downloadcuddles-42c69b59e1fdcf70219bc04a3124d2f35d9463ac.tar.xz
ls command
Diffstat (limited to 'stage3/fs.c')
-rw-r--r--stage3/fs.c153
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;
}