proof-of-concept file reading from tar-formatted disk

This commit is contained in:
uosfz 2026-05-30 16:03:43 +02:00
parent b0d57fcb54
commit 01d3acbac9
Signed by: uosfz
SSH key fingerprint: SHA256:FlktuluyhTQg3jHZNLKwxOOC5hbfrUXM0tz3IA3lGJo
7 changed files with 186 additions and 45 deletions

View file

@ -82,7 +82,12 @@ drive.img:
truncate -s 16M $@
drive-virtio.img:
echo "Hello World!" > a.txt
echo "Goodbye World!" > b.txt
tar --format=v7 -cvf $@ a.txt b.txt
truncate -s 16M $@
rm a.txt
rm b.txt
clean:
rm -rf build/*

View file

@ -3,23 +3,19 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
struct tar_header {
char *name;
uint8_t UNUSED_mode_owner;
uint8_t UNUSED_mode_group;
uint16_t UNUSED_owner_id;
uint16_t UNUSED_group_id;
uint64_t size;
uint64_t UNUSED_mtime;
uint64_t UNUSED_checksum;
#define TAR_LINK_HARD 1
#define TAR_LINK_SYM 2
uint8_t link_indicator;
char *link_name;
void *data;
};
bool tar_get_file(const char *name, struct tar_header *hd_out);
bool tar_get_file_info_in_mem(
const char *name,
void *tar_start,
void *tar_end,
uint64_t *size_out, void **data_out);
bool tar_get_file_info_custom(
const char *name,
size_t tar_num_blocks,
uint8_t *(*get_block)(uint64_t,void*),
void (*put_block)(uint64_t,void*),
void *cbdata,
uint64_t *size_out, uint64_t *bno_out);
#endif

View file

@ -116,10 +116,17 @@ struct blk_req {
uint64_t num_sectors;
struct pa data;
};
#define BLK_READ(s, d, n) ((struct blk_req){ .type = BLK_REQ_READ, .sector = s, .data = d, .num_sectors = n })
#define BLK_WRITE(s, d, n) ((struct blk_req){ .type = BLK_REQ_WRITE, .sector = s, .data = d, .num_sectors = n }) // make copy because we free mem
#define BLK_READ(s, n, d) ((struct blk_req){ .type = BLK_REQ_READ, .sector = s, .num_sectors = n, .data = d })
#define BLK_WRITE(s, n, d) ((struct blk_req){ .type = BLK_REQ_WRITE, .sector = s, .num_sectors = n, .data = d })
#define BLK_FLUSH() ((struct blk_req){ .type = BLK_REQ_FLUSH })
// Queues an operation on the device.
// See macros above to specify an operation
// Returns as soon as request is submitted, does not wait until operation is complete.
// Once complete, `cb` is called.
// `req.data` must be physically contiguous and at least of size `512 * req.num_sectors`.
// It must not be freed before the callback is called. It may be freed inside the callback.
// Requests may be completed out of order, so `cb` may also be called out of order.
enum blk_queue_result
virtio_blk_queue_req(
struct virtio_blk_context *ctx,

View file

@ -1,20 +1,27 @@
#include "font.h"
#include "bootboot.h"
#include "sff.h"
#include "std.h"
#include "tar.h"
#include "texture.h"
#include "address.h"
extern BOOTBOOT bootboot;
bool font_load(const char *name, font_handle *out) {
if (name == NULL || out == NULL) {
return false;
}
struct tar_header hd;
bool success = tar_get_file(name, &hd);
uint64_t size;
void *data;
char *initrd_ptr = pa_to_pointer(pa_from_value(bootboot.initrd_ptr));
char *initrd_end = initrd_ptr + bootboot.initrd_size;
bool success = tar_get_file_info_in_mem(name, initrd_ptr, initrd_end, &size, &data);
if (!success) {
return false;
}
success = sff_decode(hd.data, hd.size, out);
success = sff_decode(data, size, out);
return success;
}

View file

@ -12,6 +12,7 @@
#include "cpu.h"
#include "texture.h"
#include "time.h"
#include "tar.h"
#include "x86_64/asm.h"
#include "x86_64/ps2_driver.h"
@ -85,6 +86,51 @@ void mycb(struct blk_req req, void* userdata) {
slab_cache_free(pa_to_pointer(req.data));
}
struct slab_cache sector_cache;
struct cache_entry {
bool used;
bool done;
uint64_t bno;
struct pa data;
} re1, re2;
void get_block_done(struct blk_req req, void* userdata) {
struct cache_entry *entry = userdata;
entry->done = true;
}
uint8_t *get_block(uint64_t bno,void *cbdata) {
struct virtio_blk_context *ctx = cbdata;
struct pa data = pa_from_pointer(slab_cache_alloc(&sector_cache));
volatile struct cache_entry *entry;
if (!re1.used) {
entry = &re1;
} else {
ASSERT(!re2.used);
entry = &re2;
}
entry->used = true;
entry->done = false;
entry->bno = bno;
entry->data = data;
virtio_blk_queue_req(ctx, BLK_READ(bno, 1, data), get_block_done, entry);
while (!entry->done) {
virtio_blk_on_used_notif(ctx);
}
return pa_to_pointer(entry->data);
}
void put_block(uint64_t bno, void *cbdata) {
struct cache_entry *entry;
if (re1.used && re1.bno == bno) {
entry = &re1;
} else {
ASSERT(re2.used && re2.bno == bno);
entry = &re2;
}
slab_cache_free(pa_to_pointer(entry->data));
entry->used = false;
}
void main(void) {
__asm__("sti");
@ -114,21 +160,14 @@ void main(void) {
struct virtio_blk_context ctx;
virtio_blk_device_init(bdf, &ctx);
struct slab_cache sector_cache;
slab_cache_init_with_align(&sector_cache, 512, 512, NULL, NULL);
#define NR 50
for (int i = 0; i < NR; i++) {
struct pa addr = pa_from_pointer(slab_cache_alloc(&sector_cache));
ASSERT(addr.value != 0);
enum blk_queue_result res = virtio_blk_queue_req(&ctx, BLK_READ(i, addr, 1), mycb, NULL);
ASSERT(res == BLK_QUEUE_RESULT_OK);
}
struct virtq_used *used = ctx.used;
while (LE16_TO_CPU(used->idx) < NR) ; // wait. TODO interrupts
virtio_blk_on_used_notif(&ctx);
uint64_t size;
uint64_t bno;
ASSERT(tar_get_file_info_custom("b.txt", ctx.num_sectors, get_block, put_block, &ctx,
&size, &bno));
ASSERT(!re1.used && !re2.used);
printlinef("found file: size %lu bno %lu", size, bno);
slab_cache_destroy(&sector_cache);

View file

@ -104,6 +104,7 @@ bool slab_cache_new_slab(struct slab_cache *cache) {
// never returns NULL.
static void *slab_obj_alloc(struct slab *slab) {
ASSERT(slab != NULL);
ASSERT(slab->magic == SLAB_MAGIC);
ASSERT(slab->free_stack != NULL && slab->free_count > 0);
slab->free_count--;
struct free_stack *free = slab->free_stack;
@ -149,6 +150,7 @@ void *slab_cache_alloc(struct slab_cache *cache) {
struct slab *slab = cache->empty_slabs;
ASSERT(slab != NULL);
ASSERT(slab->magic == SLAB_MAGIC);
// move previously empty slab to partial or full list
cache->empty_slabs = slab->next;
@ -172,9 +174,11 @@ static void slab_cache_remove_slab_from_list(
{
ASSERT(cache != NULL);
ASSERT(slab != NULL);
ASSERT(slab->magic == SLAB_MAGIC);
ASSERT(list_ptr != NULL);
struct slab *prev = NULL;
for (struct slab *curr = *list_ptr; curr != NULL; curr = curr->next) {
ASSERT(curr->magic == SLAB_MAGIC);
if (curr == slab) {
if (prev != NULL) {
prev->next = slab->next;
@ -192,6 +196,7 @@ static void slab_cache_remove_slab_from_list(
static void slab_obj_free(struct slab *slab, void *obj) {
ASSERT(slab != NULL);
ASSERT(obj != NULL);
ASSERT(slab->magic == SLAB_MAGIC);
ASSERT(slab->free_count < slab->cache->num_slots_per_slab);
ASSERT(PAGE_BASE(obj) == (void*)slab);
@ -214,6 +219,7 @@ static void slab_cache_return_slab_to_kernel(
{
ASSERT(cache != NULL);
ASSERT(slab != NULL);
ASSERT(slab->magic == SLAB_MAGIC);
ASSERT(slab->free_count == cache->num_slots_per_slab);
uint8_t *raw = (uint8_t*)slab;
uint8_t * const page_start = raw;
@ -279,6 +285,7 @@ void slab_cache_return_unused_mem(struct slab_cache *cache) {
ASSERT(cache != NULL);
struct slab *slab = cache->empty_slabs;
while (slab != NULL) {
ASSERT(slab->magic == SLAB_MAGIC);
struct slab *next_slab = slab->next; // read next pointer before freeing mem
slab_cache_return_slab_to_kernel(cache, slab);
slab = next_slab;

100
src/tar.c
View file

@ -1,9 +1,22 @@
#include "address.h"
#include "std.h"
#include "tar.h"
#include "bootboot.h"
extern BOOTBOOT bootboot; // see bootboot.h
struct tar_header {
char *name;
uint8_t UNUSED_mode_owner;
uint8_t UNUSED_mode_group;
uint16_t UNUSED_owner_id;
uint16_t UNUSED_group_id;
uint64_t size;
uint64_t UNUSED_mtime;
uint64_t UNUSED_checksum;
#define TAR_LINK_HARD 1
#define TAR_LINK_SYM 2
uint8_t link_indicator;
char *link_name;
};
#define IS_OCTAL(c) ((c) >= '0' && (c) <= '7')
#define IS_DEC(c) ((c) >= '0' && (c) <= '9')
@ -24,14 +37,18 @@ static void tar_header_parse(void *addr, struct tar_header *hd_out) {
char lin = *((char *)addr + 156);
hd_out->link_indicator = IS_DEC(lin) ? lin - '0' : lin;
hd_out->link_name = (char *)addr + 157;
hd_out->data = (char *)addr + 512;
}
#define SYM_FOLLOW_CAP 100
bool tar_get_file(const char *name, struct tar_header *hd_out) {
char *initrd_ptr = pa_to_pointer(pa_from_value(bootboot.initrd_ptr));
char *initrd_end = initrd_ptr + bootboot.initrd_size;
bool tar_get_file_info_in_mem(
const char *name,
void *tar_start,
void *tar_end,
uint64_t *size_out, void **data_out)
{
uint8_t *ptr = tar_start;
uint8_t *end = tar_end;
unsigned int hard_follow_count = 0;
unsigned int sym_follow_count = 0;
struct tar_header hd;
@ -40,8 +57,8 @@ find_begin:
ASSERT(hard_follow_count <= 1);
ASSERT(sym_follow_count < SYM_FOLLOW_CAP);
while (initrd_ptr < initrd_end) {
tar_header_parse(initrd_ptr, &hd);
while (ptr < end) {
tar_header_parse(ptr, &hd);
if (streq(hd.name, name)) {
if (hd.link_indicator == TAR_LINK_HARD) {
name = hd.link_name;
@ -52,12 +69,75 @@ find_begin:
sym_follow_count += 1;
goto find_begin;
} else {
memcpy(hd_out, &hd, sizeof(hd));
*size_out = hd.size;
*data_out = ptr + 512;
return true;
}
} else {
initrd_ptr += 512 + ((hd.size + 511) & ~511);
ptr += 512 + ((hd.size + 511) & ~511);
}
}
return false;
}
bool tar_get_file_info_custom(
const char *name,
size_t tar_num_blocks,
uint8_t *(*get_block)(uint64_t,void*),
void (*put_block)(uint64_t,void*),
void *cbdata,
uint64_t *size_out, uint64_t *bno_out)
{
size_t bno = 0;
unsigned int hard_follow_count = 0;
unsigned int sym_follow_count = 0;
struct tar_header hd;
bool use_other_block = false;
size_t bno_other;
find_begin:
ASSERT(hard_follow_count <= 1);
ASSERT(sym_follow_count < SYM_FOLLOW_CAP);
while (bno < tar_num_blocks) {
uint8_t *ptr = get_block(bno, cbdata);
tar_header_parse(ptr, &hd);
if (streq(hd.name, name)) {
if (hd.link_indicator == TAR_LINK_HARD) {
name = hd.link_name;
hard_follow_count += 1;
if (use_other_block) {
put_block(bno_other, cbdata);
}
use_other_block = true;
bno_other = bno;
goto find_begin;
} else if (hd.link_indicator == TAR_LINK_SYM) {
name = hd.link_name;
sym_follow_count += 1;
if (use_other_block) {
put_block(bno_other, cbdata);
}
use_other_block = true;
bno_other = bno;
goto find_begin;
} else {
*size_out = hd.size;
*bno_out = bno + 1;
if (use_other_block) {
put_block(bno_other, cbdata);
}
put_block(bno, cbdata);
return true;
}
} else {
put_block(bno, cbdata);
bno += 1 + ((hd.size + 511) >> 12);
}
}
if (use_other_block) {
put_block(bno_other, cbdata);
}
put_block(bno, cbdata);
return false;
}