bootstrap: update tarfs driver with libfs support

This commit is contained in:
2026-03-10 19:16:22 +00:00
parent ea6ec785a9
commit b7452a449b
6 changed files with 591 additions and 30 deletions

View File

@@ -1,8 +1,30 @@
#include "tar.h"
#include "queue.h"
#include <fs/dentry.h>
#include <fs/file.h>
#include <fs/inode.h>
#include <fs/superblock.h>
#include <mango/handle.h>
#include <mango/log.h>
#include <mango/vm.h>
#include <stdio.h>
#include <string.h>
#include <xpc/buffer.h>
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;
}