From b7452a449b9225186164e81b11a15b3dcd406ce6 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Tue, 10 Mar 2026 19:16:22 +0000 Subject: [PATCH] bootstrap: update tarfs driver with libfs support --- sys/bootstrap/CMakeLists.txt | 2 +- sys/bootstrap/main.c | 54 +++--- sys/bootstrap/queue.c | 138 +++++++++++++++ sys/bootstrap/queue.h | 100 +++++++++++ sys/bootstrap/tar.c | 319 +++++++++++++++++++++++++++++++++++ sys/bootstrap/tar.h | 8 + 6 files changed, 591 insertions(+), 30 deletions(-) create mode 100644 sys/bootstrap/queue.c create mode 100644 sys/bootstrap/queue.h diff --git a/sys/bootstrap/CMakeLists.txt b/sys/bootstrap/CMakeLists.txt index 33690a0..cd52200 100644 --- a/sys/bootstrap/CMakeLists.txt +++ b/sys/bootstrap/CMakeLists.txt @@ -6,7 +6,7 @@ set_property(SOURCE ${arch_sources} PROPERTY LANGUAGE C) add_executable(bootstrap ${c_sources} ${arch_sources}) target_link_libraries(bootstrap - libmango libc-core libc-malloc libfs-static liblaunch + libmango libc-core libc-malloc libfs-static liblaunch libxpc-static interface::fs) target_compile_options(bootstrap PRIVATE diff --git a/sys/bootstrap/main.c b/sys/bootstrap/main.c index 2de92a1..70536a9 100644 --- a/sys/bootstrap/main.c +++ b/sys/bootstrap/main.c @@ -53,7 +53,8 @@ static enum launch_status resolve_dependency( } static kern_status_t open( - const struct msg_endpoint *sender, + xpc_context_t *ctx, + const xpc_endpoint_t *sender, const char *path, int flags, int *out_err, @@ -178,51 +179,46 @@ int main( .fs_arg = &heap, }; - struct fs_context *fs = fs_context_create(&fs_allocator, NULL); + struct fs_context *fs = fs_context_create(&fs_allocator); if (!fs) { kern_logf("cannot initialise fs"); return -1; } + enum fs_status fs_status = fs_context_mount_filesystem( + fs, + tar_mount, + (void *)bsp_base, + 0); + if (fs_status != FS_SUCCESS) { + kern_logf("cannot mount filesustem (%d)", fs_status); + return -1; + } + while (1) { - struct msg_endpoint sender; - struct msg_header hdr; - kern_msg_handle_t handles[KERN_MSG_MAX_HANDLES] = {0}; - kern_status_t status = msg_recv_generic( - channel, - &sender, - &hdr, - handles, - KERN_MSG_MAX_HANDLES); + xpc_msg_t msg; + kern_status_t status = xpc_msg_recv(channel, &msg); if (status != KERN_OK) { kern_logf("message recv error %d", status); - msg_reply_generic( - channel, - &sender, - &hdr, - KERN_UNSUPPORTED); continue; } - switch (hdr.hdr_protocol) { - case PROTOCOL_FS: - status = fs_context_dispatch_msg( - fs, - channel, - &sender, - &hdr); + switch (msg.msg_header.hdr_interface) { + case INTERFACE_FS: + status = fs_context_dispatch_msg(fs, &msg); break; default: kern_logf( "unknown message protocol %u", - hdr.hdr_protocol); - msg_reply_generic( - channel, - &sender, - &hdr, - KERN_UNSUPPORTED); + msg.msg_header.hdr_interface); + xpc_msg_reply_error(&msg, KERN_UNSUPPORTED); break; } + + if (status != KERN_OK) { + kern_logf("message reply error %d", status); + continue; + } } return 0; diff --git a/sys/bootstrap/queue.c b/sys/bootstrap/queue.c new file mode 100644 index 0000000..5c6060e --- /dev/null +++ b/sys/bootstrap/queue.c @@ -0,0 +1,138 @@ +#include "queue.h" + +size_t queue_length(struct queue *q) +{ + size_t i = 0; + struct queue_entry *x = q->q_first; + while (x) { + i++; + x = x->qe_next; + } + + return i; +} + +void queue_insert_before( + struct queue *q, + struct queue_entry *entry, + struct queue_entry *before) +{ + struct queue_entry *x = before->qe_prev; + if (x) { + x->qe_next = entry; + } else { + q->q_first = entry; + } + + entry->qe_prev = x; + + before->qe_prev = entry; + entry->qe_next = before; +} + +void queue_insert_after( + struct queue *q, + struct queue_entry *entry, + struct queue_entry *after) +{ + struct queue_entry *x = after->qe_next; + if (x) { + x->qe_prev = entry; + } else { + q->q_last = entry; + } + + entry->qe_prev = x; + + after->qe_next = entry; + entry->qe_prev = after; +} + +void queue_push_front(struct queue *q, struct queue_entry *entry) +{ + if (q->q_first) { + q->q_first->qe_prev = entry; + } + + entry->qe_next = q->q_first; + entry->qe_prev = NULL; + + q->q_first = entry; + + if (!q->q_last) { + q->q_last = entry; + } +} + +void queue_push_back(struct queue *q, struct queue_entry *entry) +{ + if (q->q_last) { + q->q_last->qe_next = entry; + } + + entry->qe_prev = q->q_last; + entry->qe_next = NULL; + + q->q_last = entry; + + if (!q->q_first) { + q->q_first = entry; + } +} + +struct queue_entry *queue_pop_front(struct queue *q) +{ + struct queue_entry *x = q->q_first; + if (x) { + queue_delete(q, x); + } + + return x; +} + +struct queue_entry *queue_pop_back(struct queue *q) +{ + struct queue_entry *x = q->q_last; + if (x) { + queue_delete(q, x); + } + + return x; +} + +void queue_delete(struct queue *q, struct queue_entry *entry) +{ + if (!entry) { + return; + } + + if (entry == q->q_first) { + q->q_first = q->q_first->qe_next; + } + + if (entry == q->q_last) { + q->q_last = q->q_last->qe_prev; + } + + if (entry->qe_next) { + entry->qe_next->qe_prev = entry->qe_prev; + } + + if (entry->qe_prev) { + entry->qe_prev->qe_next = entry->qe_next; + } + + entry->qe_next = entry->qe_prev = NULL; +} + +void queue_delete_all(struct queue *q) +{ + struct queue_entry *x = q->q_first; + while (x) { + struct queue_entry *next = x->qe_next; + x->qe_next = x->qe_prev = NULL; + x = next; + } + + q->q_first = q->q_last = NULL; +} diff --git a/sys/bootstrap/queue.h b/sys/bootstrap/queue.h new file mode 100644 index 0000000..787fb99 --- /dev/null +++ b/sys/bootstrap/queue.h @@ -0,0 +1,100 @@ +#ifndef QUEUE_H_ +#define QUEUE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define QUEUE_CONTAINER(t, m, v) \ + ((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0)) + +#define QUEUE_INIT ((struct queue) {.q_first = NULL, .q_last = NULL}) +#define QUEUE_ENTRY_INIT \ + ((struct queue_entry) {.qe_next = NULL, .qe_prev = NULL}) + +#define queue_foreach(iter_type, iter_name, queue_name, node_member) \ + for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \ + iter_type, \ + node_member, \ + queue_first(queue_name)); \ + iter_name; \ + iter_name = (iter_type *)QUEUE_CONTAINER( \ + iter_type, \ + node_member, \ + queue_next(&((iter_name)->node_member)))) + +#define queue_foreach_r(iter_type, iter_name, queue_name, node_member) \ + for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \ + iter_type, \ + node_member, \ + queue_last(queue_name)); \ + iter_name; \ + iter_name = (iter_type *)QUEUE_CONTAINER( \ + iter_type, \ + node_member, \ + queue_prev(&((iter_name)->node_member)))) + +struct queue_entry { + struct queue_entry *qe_next; + struct queue_entry *qe_prev; +}; + +struct queue { + struct queue_entry *q_first; + struct queue_entry *q_last; +}; + +static inline void queue_init(struct queue *q) +{ + memset(q, 0x00, sizeof *q); +} +static inline bool queue_empty(struct queue *q) +{ + return q->q_first == NULL; +} + +static inline struct queue_entry *queue_first(struct queue *q) +{ + return q->q_first; +} +static inline struct queue_entry *queue_last(struct queue *q) +{ + return q->q_last; +} +static inline struct queue_entry *queue_next(struct queue_entry *entry) +{ + return entry->qe_next; +} +static inline struct queue_entry *queue_prev(struct queue_entry *entry) +{ + return entry->qe_prev; +} + +extern size_t queue_length(struct queue *q); + +extern void queue_insert_before( + struct queue *q, + struct queue_entry *entry, + struct queue_entry *before); +extern void queue_insert_after( + struct queue *q, + struct queue_entry *entry, + struct queue_entry *after); + +extern void queue_push_front(struct queue *q, struct queue_entry *entry); +extern void queue_push_back(struct queue *q, struct queue_entry *entry); + +extern struct queue_entry *queue_pop_front(struct queue *q); +extern struct queue_entry *queue_pop_back(struct queue *q); + +extern void queue_delete(struct queue *q, struct queue_entry *entry); +extern void queue_delete_all(struct queue *q); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sys/bootstrap/tar.c b/sys/bootstrap/tar.c index 74af056..966b03a 100644 --- a/sys/bootstrap/tar.c +++ b/sys/bootstrap/tar.c @@ -1,8 +1,30 @@ #include "tar.h" +#include "queue.h" + +#include +#include +#include +#include #include +#include #include +#include #include +#include + +struct tar_superblock { + struct fs_superblock sb_base; + struct tar_bin_header *sb_data; +}; + +struct tar_entry { + struct fs_dentry e_dentry; + struct fs_inode e_inode; + void *e_data; + struct queue_entry e_entry; + struct queue e_children; +}; size_t getsize(const char *in) { @@ -94,3 +116,300 @@ int tar_header_decode(const struct tar_bin_header *in, struct tar_header *out) out->size = getsize(in->size); return 0; } + +static struct tar_entry *entry_from_dentry(struct fs_dentry *dent) +{ + return QUEUE_CONTAINER(struct tar_entry, e_dentry, dent); +} + +static struct tar_entry *entry_from_inode(struct fs_inode *inode) +{ + return QUEUE_CONTAINER(struct tar_entry, e_inode, inode); +} + +static struct tar_entry *entry_get_child( + struct tar_entry *entry, + const char *name) +{ + struct queue_entry *cur = queue_first(&entry->e_children); + while (cur) { + struct tar_entry *child + = QUEUE_CONTAINER(struct tar_entry, e_entry, cur); + + if (!strcmp(child->e_dentry.d_name, name)) { + return child; + } + + cur = queue_next(cur); + } + + return NULL; +} + +static const struct fs_dentry_ops dentry_ops = {}; + +static enum fs_status dir_lookup( + struct fs_inode *inode, + const char *name, + struct fs_dentry **out) +{ + struct tar_entry *entry = entry_from_inode(inode); + struct tar_entry *child = entry_get_child(entry, name); + if (!child) { + return FS_ERR_NO_ENTRY; + } + + *out = &child->e_dentry; + return FS_SUCCESS; +} + +static enum fs_status file_read( + struct fs_file *f, + xpc_buffer_t *buf, + size_t count, + off_t *seek) +{ + off_t offset = *seek; + struct tar_entry *entry = entry_from_inode(fs_file_get_inode(f)); + if (!entry) { + return FS_ERR_BAD_STATE; + } + + if (offset >= entry->e_inode.i_size) { + count = 0; + } else if (offset + count > entry->e_inode.i_size) { + count = entry->e_inode.i_size - offset; + } + + if (count == 0) { + return FS_SUCCESS; + } + + char *src = (char *)entry->e_data + offset; + size_t w; + xpc_buffer_write(buf, src, count, &w); + + offset += count; + *seek = offset; + + return FS_SUCCESS; +} + +static const struct fs_file_ops file_ops = { + .f_read = file_read, +}; + +static const struct fs_inode_ops file_inode_ops = { + .i_lookup = NULL, +}; + +static const struct fs_inode_ops dir_inode_ops = { + .i_lookup = dir_lookup, +}; + +static struct tar_entry *create_dir_entry( + struct fs_context *ctx, + struct tar_superblock *sb, + const char *name) +{ + struct tar_entry *entry = fs_context_alloc(ctx, sizeof *entry); + if (!entry) { + return NULL; + } + + memset(entry, 0x0, sizeof *entry); + + entry->e_inode.i_sb = &sb->sb_base; + entry->e_inode.i_mode = FS_INODE_DIR; + entry->e_inode.i_ops = &dir_inode_ops; + + entry->e_dentry.d_sb = &sb->sb_base; + entry->e_dentry.d_ops = &dentry_ops; + entry->e_dentry.d_inode = &entry->e_inode; + + size_t name_len = strlen(name); + entry->e_dentry.d_name = fs_context_alloc(ctx, name_len + 1); + if (!entry->e_dentry.d_name) { + fs_context_free(ctx, entry); + return NULL; + } + + memcpy(entry->e_dentry.d_name, name, name_len + 1); + + return entry; +} + +static struct tar_entry *create_file_entry( + struct fs_context *ctx, + struct tar_superblock *sb, + struct tar_header *header, + void *data, + const char *name) +{ + struct tar_entry *entry = fs_context_alloc(ctx, sizeof *entry); + if (!entry) { + return NULL; + } + + memset(entry, 0x0, sizeof *entry); + + entry->e_inode.i_sb = &sb->sb_base; + entry->e_inode.i_mode = FS_INODE_REG; + entry->e_inode.i_ops = &file_inode_ops; + entry->e_inode.i_fops = &file_ops; + entry->e_inode.i_size = header->size; + + entry->e_dentry.d_sb = &sb->sb_base; + entry->e_dentry.d_ops = &dentry_ops; + entry->e_dentry.d_inode = &entry->e_inode; + + entry->e_data = data; + + size_t name_len = strlen(name); + entry->e_dentry.d_name = fs_context_alloc(ctx, name_len + 1); + if (!entry->e_dentry.d_name) { + fs_context_free(ctx, entry); + return NULL; + } + + memcpy(entry->e_dentry.d_name, name, name_len + 1); + + return entry; +} + +static size_t get_first_path_component(const char *in, char *out, size_t max) +{ + size_t i = 0; + while (i < max - 1) { + if (in[i] == '\0' || in[i] == '/') { + break; + } + + out[i] = in[i]; + i++; + } + + out[i] = '\0'; + return i; +} + +static enum fs_status add_entry_to_tree( + struct fs_context *ctx, + struct tar_superblock *sb, + struct tar_header *entry, + void *data) +{ + struct tar_entry *cur = entry_from_dentry(sb->sb_base.s_root); + const char *path = entry->filename; + + char tok[256]; + + while (*path != '\0') { + while (*path == '/') { + path++; + } + + size_t tok_len + = get_first_path_component(path, tok, sizeof tok); + + if (!tok_len) { + break; + } + + struct tar_entry *next = entry_get_child(cur, tok); + bool is_file = *(path + tok_len) == '\0'; + if (next) { + goto next; + } + + if (is_file) { + next = create_file_entry(ctx, sb, entry, data, tok); + } else { + next = create_dir_entry(ctx, sb, tok); + } + + if (!next) { + return FS_ERR_NO_MEMORY; + } + + queue_push_back(&cur->e_children, &next->e_entry); + next->e_dentry.d_parent = &cur->e_dentry; + + next: + cur = next; + path += tok_len; + } + + return FS_SUCCESS; +} + +static enum fs_status build_dentry_tree( + struct fs_context *ctx, + struct tar_superblock *sb) +{ + + struct tar_entry *root = create_dir_entry(ctx, sb, "/"); + if (!root) { + return FS_ERR_NO_MEMORY; + } + + sb->sb_base.s_root = &root->e_dentry; + struct tar_bin_header *bin_header = sb->sb_data; + struct tar_header header; + enum fs_status status = FS_SUCCESS; + + for (size_t i = 0;; i++) { + tar_header_decode(bin_header, &header); + if (bin_header->filename[0] == 0) { + break; + } + + char *s = (char *)bin_header; + s += sizeof *bin_header; + + bool add = true; + + if (!strcmp(header.filename, "././@PaxHeader")) { + add = false; + } + + if (add) { + status = add_entry_to_tree(ctx, sb, &header, s); + } + + if (status != FS_SUCCESS) { + break; + } + + s += header.size; + s += ((sizeof *bin_header) + - ((uintptr_t)s % (sizeof *bin_header))); + + bin_header = (struct tar_bin_header *)s; + } + + return FS_SUCCESS; +} + +enum fs_status tar_mount( + struct fs_context *ctx, + void *arg, + enum fs_mount_flags flags, + struct fs_superblock **out) +{ + struct tar_superblock *sb = fs_context_alloc(ctx, sizeof *sb); + if (!sb) { + return FS_ERR_NO_MEMORY; + } + + sb->sb_data = arg; + + enum fs_status status = build_dentry_tree(ctx, sb); + if (status != FS_SUCCESS) { + fs_context_free(ctx, sb); + return status; + } + + *out = (struct fs_superblock *)sb; + return FS_SUCCESS; +} diff --git a/sys/bootstrap/tar.h b/sys/bootstrap/tar.h index a7f7b71..ebf5c65 100644 --- a/sys/bootstrap/tar.h +++ b/sys/bootstrap/tar.h @@ -1,6 +1,8 @@ #ifndef TAR_H_ #define TAR_H_ +#include +#include #include #include #include @@ -42,4 +44,10 @@ extern int tar_header_decode( const struct tar_bin_header *in, struct tar_header *out); +extern enum fs_status tar_mount( + struct fs_context *ctx, + void *arg, + enum fs_mount_flags flags, + struct fs_superblock **out); + #endif