diff --git a/src/bin.h b/src/bin.h index e9f02c0..1d29457 100644 --- a/src/bin.h +++ b/src/bin.h @@ -48,6 +48,11 @@ enum ec3_tag_flags { EC3_TAG_ENCRYPTED = 0x00000004u }; +enum ec3_vnode_mode { + EC3_V_REG = 0x0001u, + EC3_V_DIR = 0x0002u, +}; + #define EC3_INVALID_OFFSET 0xFFFFFFFFu /* 32K per cluster group */ @@ -190,9 +195,9 @@ struct ec3_directory_entry { b_i32 d_vnode; }; -struct ec3_vnode { +struct ec3_bin_vnode { b_i32 n_id; - b_i32 n_atime, n_mtime; + b_i32 n_ctime, n_mtime; b_i16 n_mode, n_reserved; b_i16 n_uid, n_gid; /* id of the chunk containing the vnode data */ @@ -206,26 +211,26 @@ struct ec3_vnode_group { union { struct { - struct ec3_vnode g_vnodes[EC3_VNODES_PER_GROUP_4K]; + struct ec3_bin_vnode g_vnodes[EC3_VNODES_PER_GROUP_4K]; b_i32 g_child_offsets[EC3_VNODES_PER_GROUP_4K + 1]; } g_4k; struct { - struct ec3_vnode g_vnodes[EC3_VNODES_PER_GROUP_8K]; + struct ec3_bin_vnode g_vnodes[EC3_VNODES_PER_GROUP_8K]; b_i32 g_child_offsets[EC3_VNODES_PER_GROUP_8K + 1]; } g_8k; struct { - struct ec3_vnode g_vnodes[EC3_VNODES_PER_GROUP_16K]; + struct ec3_bin_vnode g_vnodes[EC3_VNODES_PER_GROUP_16K]; b_i32 g_child_offsets[EC3_VNODES_PER_GROUP_16K + 1]; } g_16k; struct { - struct ec3_vnode g_vnodes[EC3_VNODES_PER_GROUP_32K]; + struct ec3_bin_vnode g_vnodes[EC3_VNODES_PER_GROUP_32K]; b_i32 g_child_offsets[EC3_VNODES_PER_GROUP_32K + 1]; } g_32k; struct { - struct ec3_vnode g_vnodes[EC3_VNODES_PER_GROUP_64K]; + struct ec3_bin_vnode g_vnodes[EC3_VNODES_PER_GROUP_64K]; b_i32 g_child_offsets[EC3_VNODES_PER_GROUP_64K + 1]; } g_64k; }; diff --git a/src/capture.c b/src/capture.c index b6c75f8..d3c5928 100644 --- a/src/capture.c +++ b/src/capture.c @@ -1,13 +1,16 @@ #include "bin.h" #include "chunk-table.h" #include "commands.h" +#include "fs-tree.h" #include "image.h" #include "misc.h" #include "status.h" #include "string-table.h" +#include "volume.h" #include #include +#include #include #include #include @@ -33,17 +36,20 @@ enum { }; struct capture_ctx { + struct fs_tree ctx_tree; struct ec3_image_ioctx *ctx_image; struct chunk_table ctx_chunks; struct string_table ctx_strings; + struct ec3_volume *ctx_volume; }; static enum ec3_status capture_file( struct capture_ctx *ctx, - struct ec3_tag_ioctx *vol, + struct ec3_volume *vol, b_directory *dir, const char *filename, - const b_path *filepath) + const b_path *filepath, + ec3_chunk_id out_chunk) { const struct ec3_image_info *image_info = ec3_image_ioctx_get_info(ctx->ctx_image); @@ -101,22 +107,143 @@ static enum ec3_status capture_file( } } - ec3_chunk_id id = {0}; - s2 = chunk_table_end_chunk(&ctx->ctx_chunks, id); + s2 = chunk_table_end_chunk(&ctx->ctx_chunks, out_chunk); if (s2 != EC3_SUCCESS) { return s2; } char id_str[128]; - ec3_chunk_id_to_string(id, id_str, sizeof id_str); - printf("wrote %zu byte chunk %s\n", chunk_size, id_str); + ec3_chunk_id_to_string(out_chunk, id_str, sizeof id_str); + // printf("wrote %zu byte chunk %s\n", chunk_size, id_str); free(buf); b_file_release(src); return s2; } +static int print_fs_tree_node( + struct fs_tree *tree, + struct fs_tree_node *node, + unsigned int depth, + int direction, + void *arg) +{ + if (direction == ITERATE_POSTORDER) { + depth = 0; + } + + for (unsigned int i = 0; i < depth; i++) { + fputs(" ", stdout); + } + + printf("%s ", node->n_name); + char id_str[128]; + + switch (node->n_type) { + case FS_TREE_FILE: + ec3_chunk_id_to_string(node->n_chunk, id_str, sizeof id_str); + printf("[%s]", id_str); + break; + case FS_TREE_DIR: + printf("[d]"); + break; + default: + break; + } + + printf("\n"); + + return 0; +} + +struct capture_directory_structure_args { + b_list *args_stack; + struct capture_ctx *args_ctx; +}; + +static int capture_directory_structure_callback( + struct fs_tree *tree, + struct fs_tree_node *node, + unsigned int depth, + int direction, + void *arg) +{ + if (direction != ITERATE_POSTORDER) { + return 0; + } + + struct capture_directory_structure_args *args = arg; + b_list *stack = args->args_stack; + struct capture_ctx *ctx = args->args_ctx; + + if (node->n_type == FS_TREE_FILE) { + b_list_push_back(stack, node); + return 0; + } + + size_t nr_children = fs_tree_node_get_nr_children(node); + struct ec3_directory_entry dent; + enum ec3_status status = EC3_SUCCESS; + + chunk_table_begin_chunk(&ctx->ctx_chunks); + for (size_t i = 0; i < nr_children; i++) { + struct fs_tree_node *child = b_list_pop_back(stack); + if (!child) { + return EC3_ERR_INTERNAL_FAILURE; + } + + dent.d_name = b_i32_htob( + string_table_get(&ctx->ctx_strings, child->n_name)); + dent.d_vnode = b_i32_htob(child->n_id); + + status = chunk_table_put(&ctx->ctx_chunks, &dent, sizeof dent); + } + + status = chunk_table_end_chunk(&ctx->ctx_chunks, node->n_chunk); + if (status != EC3_SUCCESS) { + return status; + } + + char id_str[128]; + ec3_chunk_id_to_string(node->n_chunk, id_str, sizeof id_str); + printf("[d %s / %zu] wrote %zu byte chunk %s\n", + node->n_name, + node->n_id, + nr_children * sizeof dent, + id_str); + + struct ec3_vnode vnode = { + .v_id = node->n_id, + .v_mode = EC3_V_DIR, + }; + + memcpy(vnode.v_data, node->n_chunk, sizeof vnode.v_data); + + ec3_volume_put_vnode(ctx->ctx_volume, &vnode); + + b_list_push_back(stack, node); + + return 0; +} + +static enum ec3_status capture_directory_structure(struct capture_ctx *ctx) +{ + b_list *stack = b_list_create(); + + struct capture_directory_structure_args args = { + .args_stack = stack, + .args_ctx = ctx, + }; + + fs_tree_iterate( + &ctx->ctx_tree, + capture_directory_structure_callback, + &args); + + return EC3_SUCCESS; +} + static enum ec3_status capture_directory( struct capture_ctx *ctx, uint64_t id, @@ -143,28 +270,43 @@ static enum ec3_status capture_directory( return s2; } + struct ec3_volume *volume; + s2 = ec3_volume_create(ctx->ctx_image, volu, &volume); + if (s2 != EC3_SUCCESS) { ec3_tag_ioctx_close(volu); b_directory_release(dir); return s2; } + ctx->ctx_volume = volume; + + fs_tree_init(&ctx->ctx_tree); + + struct ec3_vnode vnode = {0}; + vnode.v_id = ctx->ctx_tree.fs_root->n_id; + ec3_volume_put_vnode(volume, &vnode); + b_directory_iterator it; - b_directory_iterator_begin(dir, &it, B_DIRECTORY_ITERATE_PARENT_FIRST); + b_directory_iterator_begin(dir, &it, B_DIRECTORY_ITERATE_PARENT_LAST); while (b_directory_iterator_is_valid(&it)) { if (!b_directory_path_is_file(dir, it.filepath)) { + printf("dir: %s\n", b_path_ptr(it.filepath)); b_directory_iterator_next(&it); continue; } - printf("%s\n", b_path_ptr(it.filepath)); + ec3_vnode_from_file_info(&it.info, &vnode); + + printf("file: %s\n", b_path_ptr(it.filepath)); enum ec3_status status2 = capture_file( ctx, - volu, + volume, dir, it.filename, - it.filepath); + it.filepath, + vnode.v_data); if (status2 != EC3_SUCCESS) { b_err("failed to capture file '%s'", @@ -173,9 +315,22 @@ static enum ec3_status capture_directory( break; } + fs_tree_put( + &ctx->ctx_tree, + b_path_ptr(it.filepath), + &vnode.v_id, + vnode.v_data); + + ec3_volume_put_vnode(volume, &vnode); + b_directory_iterator_next(&it); } + // fs_tree_iterate(&ctx->ctx_tree, print_fs_tree_node, NULL); + + capture_directory_structure(ctx); + fs_tree_fini(&ctx->ctx_tree); + ec3_tag_ioctx_close(volu); b_directory_release(dir); diff --git a/src/chunk-table.c b/src/chunk-table.c index 587a61b..479c33e 100644 --- a/src/chunk-table.c +++ b/src/chunk-table.c @@ -438,17 +438,23 @@ void chunk_table_finish(struct chunk_table *tab) enum ec3_status chunk_table_get( struct chunk_table *tab, ec3_chunk_id id, - void *out_data, - size_t *out_len) + struct ec3_chunk *out) { - return EC3_ERR_NOT_SUPPORTED; + memcpy(out->c_id, id, sizeof out->c_id); + int err = b_tree_get(&tab->tab_chunks, (b_tree_node_entry *)out); + if (err != 0) { + return EC3_ERR_IO_FAILURE; + } + + return EC3_SUCCESS; } enum ec3_status chunk_table_begin_chunk(struct chunk_table *tab) { b_hash_ctx_init(&tab->tab_hash, B_HASH_SHAKE128); size_t nr_written = 0; - tab->tab_first_chunk_cdat_cluster = tab->tab_next_cdat_cluster; + tab->tab_first_cluster = tab->tab_next_cdat_cluster; + tab->tab_nr_clusters = 0; return EC3_SUCCESS; } @@ -469,6 +475,7 @@ static enum ec3_status flush_cluster_buf(struct chunk_table *tab) } tab->tab_cluster_buf_pos = 0; + tab->tab_nr_clusters++; return EC3_SUCCESS; } @@ -550,6 +557,9 @@ enum ec3_status chunk_table_end_chunk( struct ec3_chunk chunk = {0}; memcpy(chunk.c_id, id, sizeof id); + chunk.c_first_cluster = b_i32_htob(tab->tab_first_cluster); + chunk.c_nr_clusters = b_i32_htob(tab->tab_nr_clusters); + int err = b_tree_put( &tab->tab_chunks, (const b_tree_node_entry *)&chunk); diff --git a/src/chunk-table.h b/src/chunk-table.h index 3b29bf4..065928a 100644 --- a/src/chunk-table.h +++ b/src/chunk-table.h @@ -24,7 +24,8 @@ struct chunk_table { struct ec3_tag_ioctx *tab_cdat; b_hash_ctx tab_hash; - size_t tab_first_chunk_cdat_cluster; + size_t tab_first_cluster; + size_t tab_nr_clusters; }; extern enum ec3_status chunk_table_init( @@ -38,8 +39,7 @@ extern void chunk_table_finish(struct chunk_table *tab); extern enum ec3_status chunk_table_get( struct chunk_table *tab, ec3_chunk_id id, - void *out_data, - size_t *out_len); + struct ec3_chunk *out); extern enum ec3_status chunk_table_begin_chunk(struct chunk_table *tab); extern enum ec3_status chunk_table_put( diff --git a/src/commands.h b/src/commands.h index d01bd45..79a4a50 100644 --- a/src/commands.h +++ b/src/commands.h @@ -13,6 +13,7 @@ enum command_id { CMD_CHECK_SIG, CMD_EXPLORE, CMD_QUERY, + CMD_TREE, }; #endif diff --git a/src/fs-tree.c b/src/fs-tree.c new file mode 100644 index 0000000..d016e1b --- /dev/null +++ b/src/fs-tree.c @@ -0,0 +1,247 @@ +#include "fs-tree.h" + +#include +#include +#include + +static struct fs_tree_node *node_create( + const char *name, + enum fs_tree_node_type type) +{ + struct fs_tree_node *out = malloc(sizeof *out); + if (!out) { + return NULL; + } + + memset(out, 0x0, sizeof *out); + + out->n_name = b_strdup(name); + if (!out->n_name) { + free(out); + return NULL; + } + + out->n_type = type; + + if (type != FS_TREE_DIR) { + return out; + } + + out->n_children = b_hashmap_create(NULL, free); + + if (!out->n_children) { + free(out->n_name); + free(out); + return NULL; + } + + return out; +} + +static struct fs_tree_node *node_get_child( + struct fs_tree_node *parent, + const char *name) +{ + if (parent->n_type != FS_TREE_DIR) { + return NULL; + } + + b_hashmap_key key = { + .key_data = name, + .key_size = strlen(name), + }; + + const b_hashmap_value *value = b_hashmap_get(parent->n_children, &key); + if (value) { + return value->value_data; + } + + return NULL; +} + +static b_status node_put_child( + struct fs_tree_node *parent, + struct fs_tree_node *child) +{ + if (parent->n_type != FS_TREE_DIR) { + return B_ERR_NOT_SUPPORTED; + } + + b_hashmap_key key = { + .key_data = child->n_name, + .key_size = strlen(child->n_name), + }; + + b_hashmap_value value = { + .value_data = child, + .value_size = sizeof *child, + }; + + b_status status = b_hashmap_put(parent->n_children, &key, &value); + + if (B_OK(status)) { + parent->n_nr_children++; + } + + return status; +} + +static void node_convert_to_file(struct fs_tree_node *node, ec3_chunk_id chunk) +{ + if (node->n_type == FS_TREE_DIR) { + b_hashmap_release(node->n_children); + node->n_type = FS_TREE_FILE; + } + + memcpy(node->n_chunk, chunk, sizeof node->n_chunk); +} + +static bool node_has_children(const struct fs_tree_node *parent) +{ + if (parent->n_type != FS_TREE_DIR) { + return false; + } + + return !b_hashmap_is_empty(parent->n_children); +} + +b_status fs_tree_init(struct fs_tree *out) +{ + memset(out, 0x0, sizeof *out); + out->fs_root = node_create("/", FS_TREE_DIR); + + if (!out->fs_root) { + return B_ERR_NO_MEMORY; + } + + out->fs_root->n_id = fs_tree_alloc_id(out); + + return B_SUCCESS; +} + +void fs_tree_fini(struct fs_tree *tree) +{ +} + +size_t fs_tree_alloc_id(struct fs_tree *tree) +{ + return tree->fs_next_id++; +} + +b_status fs_tree_put( + struct fs_tree *tree, + const char *path, + size_t *id, + ec3_chunk_id chunk) +{ + char *rpath = b_strdup(path); + if (!rpath) { + return B_ERR_NO_MEMORY; + } + + struct fs_tree_node *root = tree->fs_root; + + char *ep; + char *tok = strtok_r(rpath, "/", &ep); + while (tok) { + struct fs_tree_node *next = node_get_child(root, tok); + + if (!next) { + next = node_create(tok, FS_TREE_DIR); + next->n_id = fs_tree_alloc_id(tree); + node_put_child(root, next); + } + + root = next; + + tok = strtok_r(NULL, "/", &ep); + } + + if (node_has_children(root)) { + return B_ERR_IS_DIRECTORY; + } + + *id = root->n_id; + node_convert_to_file(root, chunk); + + return B_SUCCESS; +} + +static void collect_children(struct fs_tree_node *node, b_queue *out) +{ + if (node->n_type != FS_TREE_DIR) { + return; + } + + b_queue_entry *insert_after = &node->n_entry; + + b_hashmap_iterator it; + b_hashmap_foreach(&it, node->n_children) + { + struct fs_tree_node *child = it.value->value_data; + child->n_tmp = node->n_tmp + 1; + b_queue_insert_after(out, &child->n_entry, insert_after); + insert_after = &child->n_entry; + } +} + +int fs_tree_iterate( + struct fs_tree *tree, + fs_tree_iterate_callback callback, + void *arg) +{ + b_queue q = B_QUEUE_INIT; + tree->fs_root->n_tmp = 0; + b_queue_push_back(&q, &tree->fs_root->n_entry); + + b_queue_entry *entry = b_queue_first(&q); + + while (entry) { + struct fs_tree_node *node + = b_unbox(struct fs_tree_node, entry, n_entry); + + collect_children(node, &q); + + int ret = callback( + tree, + node, + node->n_tmp, + ITERATE_PREORDER, + arg); + + if (ret != 0) { + return ret; + } + + entry = b_queue_next(entry); + } + + while (!b_queue_empty(&q)) { + entry = b_queue_pop_back(&q); + + struct fs_tree_node *node + = b_unbox(struct fs_tree_node, entry, n_entry); + + int ret = callback( + tree, + node, + node->n_tmp, + ITERATE_POSTORDER, + arg); + + if (ret != 0) { + return ret; + } + } + + return 0; +} + +size_t fs_tree_node_get_nr_children(const struct fs_tree_node *node) +{ + if (node->n_type != FS_TREE_DIR) { + return 0; + } + + return node->n_nr_children; +} diff --git a/src/fs-tree.h b/src/fs-tree.h new file mode 100644 index 0000000..4e0a52a --- /dev/null +++ b/src/fs-tree.h @@ -0,0 +1,59 @@ +#ifndef FS_TREE_H_ +#define FS_TREE_H_ + +#include "bin.h" + +#include +#include +#include + +enum fs_tree_node_type { + FS_TREE_NONE = 0, + FS_TREE_DIR, + FS_TREE_FILE, +}; + +struct fs_tree_node { + char *n_name; + enum fs_tree_node_type n_type; + size_t n_id; + + unsigned int n_tmp; + b_queue_entry n_entry; + + ec3_chunk_id n_chunk; + struct b_hashmap *n_children; + size_t n_nr_children; +}; + +struct fs_tree { + struct fs_tree_node *fs_root; + size_t fs_next_id; +}; + +typedef int (*fs_tree_iterate_callback)( + struct fs_tree *, + struct fs_tree_node *, + unsigned int, + int, + void *); + +extern b_status fs_tree_init(struct fs_tree *out); +extern void fs_tree_fini(struct fs_tree *tree); + +extern size_t fs_tree_alloc_id(struct fs_tree *tree); + +extern b_status fs_tree_put( + struct fs_tree *tree, + const char *path, + size_t *id, + ec3_chunk_id chunk); + +extern int fs_tree_iterate( + struct fs_tree *tree, + fs_tree_iterate_callback callback, + void *arg); + +extern size_t fs_tree_node_get_nr_children(const struct fs_tree_node *node); + +#endif diff --git a/src/image.c b/src/image.c index 792f9c1..5e00b89 100644 --- a/src/image.c +++ b/src/image.c @@ -4,11 +4,13 @@ #include "pipeline.h" #include "shadow-image.h" #include "status.h" +#include "tag.h" #include #include #include #include +#include #include #include @@ -600,6 +602,82 @@ const struct ec3_extent_info *ec3_image_ioctx_get_extent_info( return b_buffer_ptr(image->io_extent_table); } +enum ec3_status ec3_image_ioctx_get_string( + struct ec3_image_ioctx *image, + size_t key, + struct b_string *out) +{ + enum ec3_status status = EC3_SUCCESS; + if (!image->io_stab) { + status = ec3_image_ioctx_open_tag_by_type( + image, + EC3_TAG_STAB, + 0, + EC3_TAG_IO_READ, + &image->io_stab); + } + + if (status != EC3_SUCCESS) { + return status; + } + + if (!image->io_stab_buf) { + image->io_stab_buf = malloc(image->io_header.img_cluster_size); + + if (!image->io_stab_buf) { + return EC3_ERR_NO_MEMORY; + } + } + + size_t nr_clusters = 0; + ec3_tag_ioctx_get_nr_clusters(image->io_stab, &nr_clusters); + size_t max_key = nr_clusters * image->io_header.img_cluster_size; + + if (key >= max_key) { + return EC3_ERR_OUT_OF_BOUNDS; + } + + size_t cluster = key / image->io_header.img_cluster_size; + size_t byte = key % image->io_header.img_cluster_size; + size_t nr_read; + char *buf = image->io_stab_buf; + bool done = false; + + while (!done) { + status = ec3_tag_ioctx_read_cluster( + image->io_stab, + cluster, + buf, + &nr_read); + + if (status != EC3_SUCCESS) { + return status; + } + + for (;;) { + char c = buf[byte]; + + if (c == 0) { + done = true; + break; + } + + char s[] = {c, 0}; + b_string_append_cstr(out, s); + + byte++; + + if (byte >= nr_read) { + byte = 0; + cluster++; + break; + } + } + } + + return EC3_SUCCESS; +} + static uint64_t allocate_tag_id(struct ec3_image_ioctx *image) { uint64_t id = 0; diff --git a/src/image.h b/src/image.h index 691d031..0f2607a 100644 --- a/src/image.h +++ b/src/image.h @@ -9,6 +9,7 @@ struct b_file; struct b_buffer; +struct b_string; struct cluster; struct string_table; @@ -73,6 +74,9 @@ struct ec3_image_ioctx { struct cluster_table io_cluster_table; struct b_file *io_main; + + struct ec3_tag_ioctx *io_stab; + void *io_stab_buf; }; extern enum ec3_status ec3_image_ioctx_open( @@ -92,14 +96,10 @@ extern const struct ec3_tag_info *ec3_image_ioctx_get_tag_info_by_id( extern const struct ec3_extent_info *ec3_image_ioctx_get_extent_info( struct ec3_image_ioctx *image); -extern enum ec3_status ec3_image_ioctx_open_string_table( +extern enum ec3_status ec3_image_ioctx_get_string( struct ec3_image_ioctx *image, - enum ec3_image_ioctx_mode mode, - struct string_table **out); -extern enum ec3_status ec3_image_ioctx_open_chunk_table( - struct ec3_image_ioctx *image, - enum ec3_image_ioctx_mode mode, - struct chunk_table **out); + size_t key, + struct b_string *out); extern enum ec3_status ec3_image_ioctx_open_tag_by_type( struct ec3_image_ioctx *image, diff --git a/src/misc.h b/src/misc.h index 14db6f9..0a5dac8 100644 --- a/src/misc.h +++ b/src/misc.h @@ -18,6 +18,9 @@ __pragma(pack(push, 1)) __Declaration__ __pragma(pack(pop)) #endif +#define ITERATE_PREORDER 1 +#define ITERATE_POSTORDER 2 + extern enum ec3_status ec3_identifier_from_string(const char *s, uint64_t *out); extern enum ec3_status ec3_identifier_to_string( uint64_t id, diff --git a/src/query.c b/src/query.c index 95dbc15..690f7cd 100644 --- a/src/query.c +++ b/src/query.c @@ -6,16 +6,21 @@ #include #include +#include #include +#include #include +#include enum { ARG_CONTAINER, OPT_VERBOSE, + OPT_INFO_ALL, OPT_INFO_IMAGE, OPT_INFO_TAGS, OPT_INFO_EXTENTS, + OPT_INFO_STRINGS, OPT_INFO_CHUNKS, }; @@ -25,6 +30,7 @@ enum info_type { INFO_TAGS = 0x02, INFO_EXTENTS = 0x04, INFO_CHUNKS = 0x08, + INFO_STRINGS = 0x10, }; static void tag_type_string(unsigned long in, char out[5]) @@ -150,9 +156,74 @@ static void print_extent_info(struct ec3_image_ioctx *image) } } +static void print_strings_info(struct ec3_image_ioctx *image) +{ + const struct ec3_image_info *img_info = ec3_image_ioctx_get_info(image); + unsigned char *cluster_buf = malloc(img_info->img_cluster_size); + if (!cluster_buf) { + return; + } + + struct ec3_tag_ioctx *stab; + enum ec3_status status = ec3_image_ioctx_open_tag_by_type( + image, + EC3_TAG_STAB, + 0, + EC3_TAG_IO_READ, + &stab); + + if (status != EC3_SUCCESS) { + return; + } + + printf("strings:\n"); + size_t nr_clusters = 0; + ec3_tag_ioctx_get_nr_clusters(stab, &nr_clusters); + b_string *str = b_string_create(); + size_t current_cluster_offset = 0; + size_t key = 0; + + for (size_t i = 0; i < nr_clusters; i++) { + size_t nr_read; + status = ec3_tag_ioctx_read_cluster( + stab, + i, + cluster_buf, + &nr_read); + + if (status != EC3_SUCCESS) { + break; + } + + for (size_t i = 0; i < nr_read; i++) { + char c[] = {cluster_buf[i], 0}; + + if (isgraph(c[0])) { + b_string_append_cstr(str, c); + } else if (c[0] == 0) { + printf(" [%04zx] = %s\n", + key, + b_string_ptr(str)); + b_string_clear(str); + key = current_cluster_offset + i + 1; + } + } + + current_cluster_offset += nr_read; + } + + b_string_release(str); + ec3_tag_ioctx_close(stab); + free(cluster_buf); +} + static enum info_type get_selected_info_types(const struct b_arglist *opt) { enum info_type type = INFO_NONE; + if (b_arglist_get_count(opt, OPT_INFO_ALL, B_COMMAND_INVALID_ID) > 0) { + type |= INFO_IMAGE | INFO_TAGS | INFO_EXTENTS | INFO_CHUNKS + | INFO_STRINGS; + } if (b_arglist_get_count(opt, OPT_INFO_IMAGE, B_COMMAND_INVALID_ID) > 0) { @@ -168,6 +239,16 @@ static enum info_type get_selected_info_types(const struct b_arglist *opt) type |= INFO_EXTENTS; } + if (b_arglist_get_count(opt, OPT_INFO_CHUNKS, B_COMMAND_INVALID_ID) + > 0) { + type |= INFO_CHUNKS; + } + + if (b_arglist_get_count(opt, OPT_INFO_STRINGS, B_COMMAND_INVALID_ID) + > 0) { + type |= INFO_STRINGS; + } + return type; } @@ -213,6 +294,10 @@ static int query( print_extent_info(reader); } + if (selected_info & INFO_STRINGS) { + print_strings_info(reader); + } + ec3_image_ioctx_close(reader); fclose(fp); @@ -236,6 +321,14 @@ B_COMMAND(CMD_QUERY, CMD_ROOT) B_ARG_NR_VALUES(1); } + B_COMMAND_OPTION(OPT_INFO_ALL) + { + B_OPTION_LONG_NAME("all"); + B_OPTION_SHORT_NAME('a'); + B_OPTION_DESC( + "print all available information about the image."); + } + B_COMMAND_OPTION(OPT_INFO_IMAGE) { B_OPTION_LONG_NAME("header"); @@ -254,7 +347,18 @@ B_COMMAND(CMD_QUERY, CMD_ROOT) { B_OPTION_LONG_NAME("extents"); B_OPTION_SHORT_NAME('x'); - B_OPTION_DESC("print the contents of the image extent table."); + B_OPTION_DESC( + "print the contents of the image extent " + "table."); + } + + B_COMMAND_OPTION(OPT_INFO_STRINGS) + { + B_OPTION_LONG_NAME("strings"); + B_OPTION_SHORT_NAME('s'); + B_OPTION_DESC( + "print the contents of the image string " + "table."); } B_COMMAND_OPTION(OPT_INFO_CHUNKS) diff --git a/src/shell.c b/src/shell.c index 2727a04..9705f7e 100644 --- a/src/shell.c +++ b/src/shell.c @@ -1,6 +1,11 @@ #include "commands.h" +#include "image.h" +#include "tag.h" +#include "volume.h" #include +#include +#include enum { ARG_CONTAINER, @@ -12,6 +17,84 @@ static int shell( const b_arglist *opt, const b_array *args) { + const char *in_path = NULL; + b_arglist_get_string( + opt, + B_COMMAND_INVALID_ID, + ARG_CONTAINER, + 0, + &in_path); + + FILE *fp = fopen(in_path, "rb"); + + struct ec3_image_ioctx *image = NULL; + enum ec3_status status = ec3_image_ioctx_open( + in_path, + NULL, + EC3_IMAGE_IO_READ, + &image); + if (status != EC3_SUCCESS) { + fclose(fp); + + b_err("cannot open container '%s'", in_path); + return -1; + } + + struct ec3_tag_ioctx *volu, *ctab, *cdat; + status = ec3_image_ioctx_open_tag_by_type( + image, + EC3_TAG_VOLU, + 0, + EC3_TAG_IO_READ, + &volu); + + if (status != EC3_SUCCESS) { + b_err("cannot open container volume"); + b_i("error code: %s", ec3_status_to_string(status)); + return -1; + } + + status = ec3_image_ioctx_open_tag_by_type( + image, + EC3_TAG_CTAB, + 0, + EC3_TAG_IO_READ, + &ctab); + + if (status != EC3_SUCCESS) { + b_err("cannot open container chunk table"); + b_i("error code: %s", ec3_status_to_string(status)); + return -1; + } + + status = ec3_image_ioctx_open_tag_by_type( + image, + EC3_TAG_CDAT, + 0, + EC3_TAG_IO_READ, + &cdat); + + if (status != EC3_SUCCESS) { + b_err("cannot access container chunk data"); + b_i("error code: %s", ec3_status_to_string(status)); + return -1; + } + + struct ec3_volume *volume; + status = ec3_volume_mount(image, volu, ctab, cdat, &volume); + if (status != EC3_SUCCESS) { + b_err("cannot mount volume"); + b_i("error code: %s", ec3_status_to_string(status)); + return -1; + } + + b_i("mount OK"); + + ec3_tag_ioctx_close(volu); + ec3_tag_ioctx_close(ctab); + ec3_tag_ioctx_close(cdat); + ec3_image_ioctx_close(image); + return 0; } diff --git a/src/status.h b/src/status.h index 0f0a2ac..4c21b05 100644 --- a/src/status.h +++ b/src/status.h @@ -15,6 +15,7 @@ enum ec3_status { EC3_ERR_IO_FAILURE, EC3_ERR_END_OF_FILE, EC3_ERR_OUT_OF_BOUNDS, + EC3_ERR_INTERNAL_FAILURE, }; extern enum ec3_status ec3_status_from_b_status( diff --git a/src/tree.c b/src/tree.c new file mode 100644 index 0000000..fbaa584 --- /dev/null +++ b/src/tree.c @@ -0,0 +1,356 @@ +#include "bin.h" +#include "chunk-table.h" +#include "cluster-table.h" +#include "commands.h" +#include "image.h" +#include "misc.h" +#include "status.h" +#include "string-table.h" +#include "volume.h" + +#include +#include +#include +#include +#include +#include +#include + +enum { + ARG_CONTAINER, + OPT_VERBOSE, + + OPT_VOLUME, + OPT_VOLUME_VAL, +}; + +struct tree_ctx { + FILE *ctx_fp; + struct chunk_table ctx_chunks; + struct ec3_image_ioctx *ctx_image; + struct ec3_tag_ioctx *ctx_ctab, *ctx_cdat, *ctx_volu; + struct ec3_volume *ctx_volume; + b_queue ctx_stack; + void *ctx_buf; + b_string *ctx_str; +}; + +struct tree_item { + char *i_name; + struct ec3_vnode i_vnode; + b_queue_entry i_entry; + unsigned int i_depth; +}; + +static struct tree_item *tree_item_create(void) +{ + struct tree_item *out = malloc(sizeof *out); + if (!out) { + return NULL; + } + + memset(out, 0x0, sizeof *out); + + return out; +} + +static enum ec3_status collect_children( + struct tree_ctx *ctx, + struct tree_item *parent) +{ + struct ec3_chunk chunk; + enum ec3_status status = chunk_table_get( + &ctx->ctx_chunks, + parent->i_vnode.v_data, + &chunk); + + if (status != EC3_SUCCESS) { + b_err("cannot read chunk."); + return status; + } + + size_t first_cluster = b_i32_btoh(chunk.c_first_cluster); + size_t nr_clusters = b_i32_btoh(chunk.c_nr_clusters); + size_t nr_read = 0; + b_queue_entry *insert_after = &parent->i_entry; + + for (size_t i = 0; i < nr_clusters; i++) { + ec3_tag_ioctx_read_cluster( + ctx->ctx_cdat, + first_cluster + i, + ctx->ctx_buf, + &nr_read); + + struct ec3_directory_entry *dents = ctx->ctx_buf; + size_t nr_dents = nr_read / sizeof *dents; + + for (size_t ii = 0; ii < nr_dents; ii++) { + size_t name_key = b_i32_btoh(dents[ii].d_name); + size_t vnode_id = b_i32_btoh(dents[ii].d_vnode); + b_string_clear(ctx->ctx_str); + ec3_image_ioctx_get_string( + ctx->ctx_image, + name_key, + ctx->ctx_str); + // printf(" %s\n", b_string_ptr(ctx->ctx_str)); + + struct tree_item *child = tree_item_create(); + + child->i_name = b_string_steal(ctx->ctx_str); + child->i_depth = parent->i_depth + 1; + ec3_volume_get_vnode( + ctx->ctx_volume, + vnode_id, + &child->i_vnode); + + b_queue_insert_after( + &ctx->ctx_stack, + &child->i_entry, + insert_after); + insert_after = &child->i_entry; + } + } + + return EC3_SUCCESS; +} + +static enum ec3_status tree_ctx_init( + struct tree_ctx *ctx, + const char *image_path) +{ + FILE *fp = fopen(image_path, "rb"); + enum ec3_status status = ec3_image_ioctx_open( + image_path, + NULL, + EC3_IMAGE_IO_READ, + &ctx->ctx_image); + + if (status != EC3_SUCCESS) { + b_err("cannot open container '%s'", image_path); + return status; + } + + status = ec3_image_ioctx_open_tag_by_type( + ctx->ctx_image, + EC3_TAG_CDAT, + 0, + EC3_TAG_IO_READ, + &ctx->ctx_cdat); + if (status != EC3_SUCCESS) { + b_err("cannot open chunk data tag."); + return status; + } + + status = ec3_image_ioctx_open_tag_by_type( + ctx->ctx_image, + EC3_TAG_CTAB, + 0, + EC3_TAG_IO_READ, + &ctx->ctx_ctab); + if (status != EC3_SUCCESS) { + b_err("cannot open chunk table tag."); + return status; + } + + const struct ec3_image_info *info + = ec3_image_ioctx_get_info(ctx->ctx_image); + + ctx->ctx_buf = malloc(info->img_cluster_size); + ctx->ctx_str = b_string_create(); + + enum ec3_cluster_size cluster_size + = ec3_cluster_size_bytes_to_id(info->img_cluster_size); + + return chunk_table_init( + ctx->ctx_ctab, + ctx->ctx_cdat, + cluster_size, + &ctx->ctx_chunks); +} + +static void tree_ctx_fini(struct tree_ctx *ctx) +{ + if (ctx->ctx_buf) { + free(ctx->ctx_buf); + } + + if (ctx->ctx_str) { + b_string_release(ctx->ctx_str); + } + + if (ctx->ctx_volume) { + ec3_volume_close(ctx->ctx_volume); + } + + if (ctx->ctx_volu) { + ec3_tag_ioctx_close(ctx->ctx_volu); + } + + if (ctx->ctx_cdat) { + ec3_tag_ioctx_close(ctx->ctx_cdat); + } + + if (ctx->ctx_ctab) { + ec3_tag_ioctx_close(ctx->ctx_ctab); + } + + if (ctx->ctx_image) { + ec3_image_ioctx_close(ctx->ctx_image); + } + + if (ctx->ctx_fp) { + fclose(ctx->ctx_fp); + } +} + +static enum ec3_status tree_ctx_mount_volume( + struct tree_ctx *ctx, + const char *volume_ident) +{ + enum ec3_status status = EC3_SUCCESS; + uint64_t ident = 0; + + if (volume_ident) { + status = ec3_identifier_from_string(volume_ident, &ident); + + if (status != EC3_SUCCESS) { + return status; + } + + status = ec3_image_ioctx_open_tag_by_id( + ctx->ctx_image, + ident, + EC3_TAG_IO_READ, + &ctx->ctx_volu); + } else { + status = ec3_image_ioctx_open_tag_by_type( + ctx->ctx_image, + EC3_TAG_VOLU, + 0, + EC3_TAG_IO_READ, + &ctx->ctx_volu); + } + + if (status != EC3_SUCCESS) { + return status; + } + + return ec3_volume_mount( + ctx->ctx_image, + ctx->ctx_volu, + ctx->ctx_ctab, + ctx->ctx_cdat, + &ctx->ctx_volume); +} + +static int tree( + const b_command *self, + const b_arglist *opt, + const b_array *args) +{ + const char *in_path = NULL; + b_arglist_get_string( + opt, + B_COMMAND_INVALID_ID, + ARG_CONTAINER, + 0, + &in_path); + + struct tree_ctx ctx = {}; + if (tree_ctx_init(&ctx, in_path) != EC3_SUCCESS) { + tree_ctx_fini(&ctx); + return -1; + } + + int result = -1; + const char *ident_str; + b_arglist_get_string(opt, OPT_VOLUME, OPT_VOLUME_VAL, 0, &ident_str); + + if (tree_ctx_mount_volume(&ctx, ident_str) != EC3_SUCCESS) { + b_err("cannot mount volume."); + tree_ctx_fini(&ctx); + return -1; + } + + struct tree_item *current = tree_item_create(); + current->i_name = b_strdup("/"); + enum ec3_status status + = ec3_volume_get_vnode(ctx.ctx_volume, 0, ¤t->i_vnode); + if (status != EC3_SUCCESS) { + b_err("cannot read root volume vnode."); + goto cleanup; + } + + b_queue stack = B_QUEUE_INIT; + b_queue_push_back(&stack, ¤t->i_entry); + + while (!b_queue_empty(&stack)) { + b_queue_entry *entry = b_queue_first(&stack); + current = b_unbox(struct tree_item, entry, i_entry); + + for (unsigned int i = 0; i < current->i_depth; i++) { + fputs(" ", stdout); + } + + printf("%s\n", current->i_name); + + if (current->i_vnode.v_mode == EC3_V_REG) { + b_queue_delete(&stack, entry); + free(current->i_name); + free(current); + continue; + } + + collect_children(&ctx, current); + + b_queue_delete(&stack, entry); + free(current->i_name); + free(current); + } + +cleanup: + + tree_ctx_fini(&ctx); + + return 0; +} + +B_COMMAND(CMD_TREE, CMD_ROOT) +{ + B_COMMAND_NAME("tree"); + B_COMMAND_SHORT_NAME('T'); + B_COMMAND_DESC("print the filesystem tree of a container volume."); + B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT); + B_COMMAND_FUNCTION(tree); + + B_COMMAND_HELP_OPTION(); + + B_COMMAND_ARG(ARG_CONTAINER) + { + B_ARG_NAME("container"); + B_ARG_DESC("the container to query."); + B_ARG_NR_VALUES(1); + } + + B_COMMAND_OPTION(OPT_VOLUME) + { + B_OPTION_LONG_NAME("volume"); + B_OPTION_SHORT_NAME('V'); + B_OPTION_DESC( + "the identifier of the volume to print. if no " + "volume is specified, the first volume in the " + "container is used."); + + B_OPTION_ARG(OPT_VOLUME_VAL) + { + B_ARG_NAME("identifier"); + B_ARG_NR_VALUES(1); + } + } + + B_COMMAND_USAGE() + { + B_COMMAND_USAGE_OPT(OPT_VOLUME); + B_COMMAND_USAGE_ARG(ARG_CONTAINER); + } +} diff --git a/src/vnode.c b/src/vnode.c new file mode 100644 index 0000000..2cc22e2 --- /dev/null +++ b/src/vnode.c @@ -0,0 +1,20 @@ +#include "vnode.h" + +#include + +enum ec3_status ec3_vnode_from_file_info( + const struct b_file_info *file_info, + struct ec3_vnode *out) +{ + out->v_mode = 0; + + if (file_info->attrib & B_FILE_ATTRIB_NORMAL) { + out->v_mode |= EC3_V_REG; + } + + if (file_info->attrib & B_FILE_ATTRIB_DIRECTORY) { + out->v_mode |= EC3_V_DIR; + } + + return EC3_SUCCESS; +} diff --git a/src/vnode.h b/src/vnode.h new file mode 100644 index 0000000..6c9a9ec --- /dev/null +++ b/src/vnode.h @@ -0,0 +1,21 @@ +#ifndef VNODE_H_ +#define VNODE_H_ + +#include "bin.h" + +struct b_file_info; + +struct ec3_vnode { + unsigned long v_id; + unsigned long v_ctime, v_mtime; + unsigned short v_mode; + unsigned short v_uid, v_gid; + + ec3_chunk_id v_data; +}; + +extern enum ec3_status ec3_vnode_from_file_info( + const struct b_file_info *file_info, + struct ec3_vnode *out); + +#endif diff --git a/src/volume.c b/src/volume.c index e69de29..e271b10 100644 --- a/src/volume.c +++ b/src/volume.c @@ -0,0 +1,519 @@ +#include "volume.h" + +#include "b-tree.h" +#include "cluster-table.h" +#include "image.h" +#include "tag.h" + +#include +#include + +static unsigned int vnode_table_orders[] = { + [EC3_CLUSTER_4K] = EC3_VNODES_PER_GROUP_4K + 1, + [EC3_CLUSTER_8K] = EC3_VNODES_PER_GROUP_8K + 1, + [EC3_CLUSTER_16K] = EC3_VNODES_PER_GROUP_16K + 1, + [EC3_CLUSTER_32K] = EC3_VNODES_PER_GROUP_32K + 1, + [EC3_CLUSTER_64K] = EC3_VNODES_PER_GROUP_64K + 1, +}; +static size_t nr_vnode_table_orders + = sizeof vnode_table_orders / sizeof vnode_table_orders[0]; + +static int node_init(struct ec3_volume *volume, struct ec3_vnode_group *n) +{ + memset(n, 0x0, sizeof *n); + + b_i32 *children; + unsigned int nr_children; + + switch (volume->v_cluster_size) { + case EC3_CLUSTER_4K: + children = n->g_4k.g_child_offsets; + nr_children = EC3_VNODES_PER_GROUP_4K; + break; + case EC3_CLUSTER_8K: + children = n->g_8k.g_child_offsets; + nr_children = EC3_VNODES_PER_GROUP_8K; + break; + case EC3_CLUSTER_16K: + children = n->g_16k.g_child_offsets; + nr_children = EC3_VNODES_PER_GROUP_16K; + break; + case EC3_CLUSTER_32K: + children = n->g_32k.g_child_offsets; + nr_children = EC3_VNODES_PER_GROUP_32K; + break; + case EC3_CLUSTER_64K: + children = n->g_64k.g_child_offsets; + nr_children = EC3_VNODES_PER_GROUP_64K; + break; + default: + return -1; + } + + for (unsigned int i = 0; i < nr_children; i++) { + children[i] = b_i32_htob(EC3_INVALID_OFFSET); + } + + return 0; +} + +static int tree_get_node(struct b_tree *p, unsigned long id, b_tree_node *n) +{ + struct ec3_volume *volume = (struct ec3_volume *)p; + size_t cluster_size + = ec3_cluster_size_id_to_bytes(volume->v_cluster_size); + + size_t nr_read; + enum ec3_status status + = ec3_tag_ioctx_read_cluster(volume->v_volu, id, n, &nr_read); + + if (status != EC3_SUCCESS) { + return -1; + } + + return nr_read == cluster_size ? 0 : -1; +} + +static int tree_put_node( + struct b_tree *p, + unsigned long id, + const b_tree_node *n) +{ + struct ec3_volume *volume = (struct ec3_volume *)p; + size_t cluster_size + = ec3_cluster_size_id_to_bytes(volume->v_cluster_size); + + size_t nr_written = 0; + enum ec3_status status = ec3_tag_ioctx_write_cluster( + volume->v_volu, + id, + n, + cluster_size, + &nr_written); + + return status == EC3_SUCCESS ? 0 : -1; +} + +static long tree_alloc_node(struct b_tree *p) +{ + struct ec3_volume *volume = (struct ec3_volume *)p; + size_t cluster_size + = ec3_cluster_size_id_to_bytes(volume->v_cluster_size); + + size_t nr_clusters; + enum ec3_status status + = ec3_tag_ioctx_get_nr_clusters(volume->v_volu, &nr_clusters); + + if (status != EC3_SUCCESS) { + return -1; + } + + struct ec3_vnode_group *n + = (struct ec3_vnode_group *)b_tree_cache_alloc_node(p); + node_init(volume, n); + + size_t nr_written = 0; + status = ec3_tag_ioctx_write_cluster( + volume->v_volu, + nr_clusters, + n, + cluster_size, + &nr_written); + + if (status != EC3_SUCCESS) { + return -1; + } + + return (long)nr_clusters; +} + +static unsigned long node_get_nr_entries(struct b_tree *tree, b_tree_node *n) +{ + struct ec3_vnode_group *node = (struct ec3_vnode_group *)n; + return b_i16_btoh(node->g_nr_vnodes); +} + +static void node_set_nr_entries( + struct b_tree *tree, + b_tree_node *n, + unsigned long val) +{ + struct ec3_vnode_group *node = (struct ec3_vnode_group *)n; + node->g_nr_vnodes = b_i16_htob(val); +} + +static b_tree_node_entry *node_get_entry( + struct b_tree *tree, + b_tree_node *n, + unsigned long index) +{ + struct ec3_volume *volume = (struct ec3_volume *)tree; + struct ec3_vnode_group *node = (struct ec3_vnode_group *)n; + + switch (volume->v_cluster_size) { + case EC3_CLUSTER_4K: + return (b_tree_node_entry *)&node->g_4k.g_vnodes[index]; + case EC3_CLUSTER_8K: + return (b_tree_node_entry *)&node->g_8k.g_vnodes[index]; + case EC3_CLUSTER_16K: + return (b_tree_node_entry *)&node->g_16k.g_vnodes[index]; + case EC3_CLUSTER_32K: + return (b_tree_node_entry *)&node->g_32k.g_vnodes[index]; + case EC3_CLUSTER_64K: + return (b_tree_node_entry *)&node->g_64k.g_vnodes[index]; + default: + return NULL; + } +} + +static void node_set_entry( + struct b_tree *tree, + b_tree_node *n, + unsigned long index, + const b_tree_node_entry *entry) +{ + struct ec3_volume *volume = (struct ec3_volume *)tree; + struct ec3_vnode_group *node = (struct ec3_vnode_group *)n; + + switch (volume->v_cluster_size) { + case EC3_CLUSTER_4K: + memmove(&node->g_4k.g_vnodes[index], + entry, + sizeof(struct ec3_bin_vnode)); + break; + case EC3_CLUSTER_8K: + memmove(&node->g_8k.g_vnodes[index], + entry, + sizeof(struct ec3_bin_vnode)); + break; + case EC3_CLUSTER_16K: + memmove(&node->g_16k.g_vnodes[index], + entry, + sizeof(struct ec3_bin_vnode)); + break; + case EC3_CLUSTER_32K: + memmove(&node->g_32k.g_vnodes[index], + entry, + sizeof(struct ec3_bin_vnode)); + break; + case EC3_CLUSTER_64K: + memmove(&node->g_64k.g_vnodes[index], + entry, + sizeof(struct ec3_bin_vnode)); + break; + default: + break; + } +} + +static void node_kill_entry( + struct b_tree *tree, + b_tree_node *n, + unsigned long index) +{ + struct ec3_volume *volume = (struct ec3_volume *)tree; + struct ec3_vnode_group *node = (struct ec3_vnode_group *)n; + + switch (volume->v_cluster_size) { + case EC3_CLUSTER_4K: + memset(&node->g_4k.g_vnodes[index], + 0x0, + sizeof(struct ec3_bin_vnode)); + break; + case EC3_CLUSTER_8K: + memset(&node->g_8k.g_vnodes[index], + 0x0, + sizeof(struct ec3_bin_vnode)); + break; + case EC3_CLUSTER_16K: + memset(&node->g_16k.g_vnodes[index], + 0x0, + sizeof(struct ec3_bin_vnode)); + break; + case EC3_CLUSTER_32K: + memset(&node->g_32k.g_vnodes[index], + 0x0, + sizeof(struct ec3_bin_vnode)); + break; + case EC3_CLUSTER_64K: + memset(&node->g_64k.g_vnodes[index], + 0x0, + sizeof(struct ec3_bin_vnode)); + break; + default: + break; + } +} + +static unsigned long node_get_child( + struct b_tree *tree, + b_tree_node *n, + unsigned long index) +{ + struct ec3_volume *volume = (struct ec3_volume *)tree; + struct ec3_vnode_group *node = (struct ec3_vnode_group *)n; + unsigned long child = EC3_INVALID_OFFSET; + + switch (volume->v_cluster_size) { + case EC3_CLUSTER_4K: + child = b_i32_btoh(node->g_4k.g_child_offsets[index]); + break; + case EC3_CLUSTER_8K: + child = b_i32_btoh(node->g_8k.g_child_offsets[index]); + break; + case EC3_CLUSTER_16K: + child = b_i32_btoh(node->g_16k.g_child_offsets[index]); + break; + case EC3_CLUSTER_32K: + child = b_i32_btoh(node->g_32k.g_child_offsets[index]); + break; + case EC3_CLUSTER_64K: + child = b_i32_btoh(node->g_64k.g_child_offsets[index]); + break; + default: + return B_TREE_INVALID_PTR; + } + + return child == EC3_INVALID_OFFSET ? B_TREE_INVALID_PTR : child; +} + +static void node_set_child( + struct b_tree *tree, + b_tree_node *n, + unsigned long index, + unsigned long ptr) +{ + struct ec3_volume *volume = (struct ec3_volume *)tree; + struct ec3_vnode_group *node = (struct ec3_vnode_group *)n; + unsigned long child + = ptr == B_TREE_INVALID_PTR ? EC3_INVALID_OFFSET : ptr; + + switch (volume->v_cluster_size) { + case EC3_CLUSTER_4K: + node->g_4k.g_child_offsets[index] = b_i32_htob(child); + break; + case EC3_CLUSTER_8K: + node->g_8k.g_child_offsets[index] = b_i32_htob(child); + break; + case EC3_CLUSTER_16K: + node->g_16k.g_child_offsets[index] = b_i32_htob(child); + break; + case EC3_CLUSTER_32K: + node->g_32k.g_child_offsets[index] = b_i32_htob(child); + break; + case EC3_CLUSTER_64K: + node->g_64k.g_child_offsets[index] = b_i32_htob(child); + break; + default: + break; + } +} + +static int entry_compare( + const struct b_tree *tree, + const b_tree_node_entry *e0, + const b_tree_node_entry *e1) +{ + struct ec3_bin_vnode *a = (struct ec3_bin_vnode *)e0, + *b = (struct ec3_bin_vnode *)e1; + + unsigned long a_id = b_i32_btoh(a->n_id); + unsigned long b_id = b_i32_btoh(b->n_id); + + if (a_id < b_id) { + return -1; + } + + if (a_id > b_id) { + return 1; + } + + return 0; +} + +static const struct b_tree_ops vnode_table_ops = { + .tree_get_node = tree_get_node, + .tree_put_node = tree_put_node, + .tree_alloc_node = tree_alloc_node, + + .node_get_nr_entries = node_get_nr_entries, + .node_set_nr_entries = node_set_nr_entries, + .node_get_entry = node_get_entry, + .node_set_entry = node_set_entry, + .node_kill_entry = node_kill_entry, + .node_get_child = node_get_child, + .node_set_child = node_set_child, + + .entry_compare = entry_compare, +}; + +static void encode_vnode(const struct ec3_vnode *in, struct ec3_bin_vnode *out) +{ + out->n_id = b_i32_htob(in->v_id); + out->n_ctime = b_i32_htob(in->v_ctime); + out->n_mtime = b_i32_htob(in->v_mtime); + out->n_mode = b_i16_htob(in->v_mode); + out->n_uid = b_i16_htob(in->v_uid); + out->n_gid = b_i16_htob(in->v_gid); + memcpy(out->n_data, in->v_data, sizeof in->v_data); +} + +static void decode_vnode(const struct ec3_bin_vnode *in, struct ec3_vnode *out) +{ + out->v_id = b_i32_btoh(in->n_id); + out->v_ctime = b_i32_btoh(in->n_ctime); + out->v_mtime = b_i32_btoh(in->n_mtime); + out->v_mode = b_i16_btoh(in->n_mode); + out->v_uid = b_i16_btoh(in->n_uid); + out->v_gid = b_i16_btoh(in->n_gid); + memcpy(out->v_data, in->n_data, sizeof in->n_data); +} + +static enum ec3_status load_vnode( + struct ec3_volume *volume, + size_t id, + struct ec3_vnode *out) +{ + struct ec3_bin_vnode vnode = {0}; + vnode.n_id = b_i32_htob(id); + + int err = b_tree_get( + &volume->v_vnode_table, + (b_tree_node_entry *)&vnode); + + if (err != 0) { + return EC3_ERR_NO_ENTRY; + } + + return EC3_SUCCESS; +} + +enum ec3_status ec3_volume_mount( + struct ec3_image_ioctx *container, + struct ec3_tag_ioctx *volu, + struct ec3_tag_ioctx *ctab, + struct ec3_tag_ioctx *cdat, + struct ec3_volume **out_volume) +{ + struct ec3_volume *volume = malloc(sizeof *volume); + if (!volume) { + return EC3_ERR_NO_MEMORY; + } + + memset(volume, 0x0, sizeof *volume); + + const struct ec3_image_info *img_info + = ec3_image_ioctx_get_info(container); + + volume->v_container = container; + volume->v_volu = volu; + volume->v_ctab = ctab; + volume->v_cdat = cdat; + volume->v_cluster_size + = ec3_cluster_size_bytes_to_id(img_info->img_cluster_size); + + size_t cluster_size_bytes = img_info->img_cluster_size; + unsigned int order = vnode_table_orders[volume->v_cluster_size]; + + b_tree_init( + &volume->v_vnode_table, + &vnode_table_ops, + cluster_size_bytes, + sizeof(struct ec3_bin_vnode), + order); + + enum ec3_status status = load_vnode(volume, 0, &volume->v_root); + if (status != EC3_SUCCESS) { + ec3_volume_close(volume); + return status; + } + + *out_volume = volume; + return EC3_SUCCESS; +} + +enum ec3_status ec3_volume_create( + struct ec3_image_ioctx *container, + struct ec3_tag_ioctx *volu, + struct ec3_volume **out_volume) +{ + struct ec3_volume *volume = malloc(sizeof *volume); + if (!volume) { + return EC3_ERR_NO_MEMORY; + } + + memset(volume, 0x0, sizeof *volume); + + const struct ec3_image_info *img_info + = ec3_image_ioctx_get_info(container); + + volume->v_container = container; + volume->v_volu = volu; + volume->v_cluster_size + = ec3_cluster_size_bytes_to_id(img_info->img_cluster_size); + + size_t cluster_size_bytes = img_info->img_cluster_size; + unsigned int order = vnode_table_orders[volume->v_cluster_size]; + + b_tree_init( + &volume->v_vnode_table, + &vnode_table_ops, + cluster_size_bytes, + sizeof(struct ec3_bin_vnode), + order); + + /* allocate root node */ + struct ec3_vnode_group *root + = (struct ec3_vnode_group *)b_tree_cache_alloc_node( + &volume->v_vnode_table); + node_init(volume, root); + tree_put_node(&volume->v_vnode_table, 0, (b_tree_node *)root); + + b_tree_cache_release_node(&volume->v_vnode_table, (b_tree_node *)root); + + *out_volume = volume; + return EC3_SUCCESS; +} + +void ec3_volume_close(struct ec3_volume *volume) +{ + b_tree_finish(&volume->v_vnode_table); + free(volume); +} + +enum ec3_status ec3_volume_get_vnode( + struct ec3_volume *volume, + size_t id, + struct ec3_vnode *out) +{ + struct ec3_bin_vnode vnode = {}; + vnode.n_id = b_i32_htob((uint32_t)id); + int err = b_tree_get( + &volume->v_vnode_table, + (b_tree_node_entry *)&vnode); + if (err != 0) { + return EC3_ERR_IO_FAILURE; + } + + decode_vnode(&vnode, out); + return EC3_SUCCESS; +} + +#include +enum ec3_status ec3_volume_put_vnode( + struct ec3_volume *volume, + const struct ec3_vnode *vnode) +{ + printf("put(%zu, %x%x)\n", + vnode->v_id, + vnode->v_data[0], + vnode->v_data[1]); + + struct ec3_bin_vnode bin_vnode; + encode_vnode(vnode, &bin_vnode); + int err = b_tree_put( + &volume->v_vnode_table, + (b_tree_node_entry *)&bin_vnode); + + return err == 0 ? EC3_SUCCESS : EC3_ERR_IO_FAILURE; +} diff --git a/src/volume.h b/src/volume.h index e69de29..5a287f7 100644 --- a/src/volume.h +++ b/src/volume.h @@ -0,0 +1,43 @@ +#ifndef VOLUME_H_ +#define VOLUME_H_ + +#include "b-tree.h" +#include "bin.h" +#include "status.h" +#include "vnode.h" + +struct ec3_vnode; +struct ec3_tag_ioctx; + +struct ec3_volume { + struct b_tree v_vnode_table; + struct ec3_vnode v_root; + /* these pointers are not owned by ec3_volume, and won't be released + * when the volume is closed */ + struct ec3_image_ioctx *v_container; + struct ec3_tag_ioctx *v_volu, *v_cdat, *v_ctab; + enum ec3_cluster_size v_cluster_size; +}; + +extern enum ec3_status ec3_volume_mount( + struct ec3_image_ioctx *container, + struct ec3_tag_ioctx *volu, + struct ec3_tag_ioctx *ctab, + struct ec3_tag_ioctx *cdat, + struct ec3_volume **out_volume); +extern enum ec3_status ec3_volume_create( + struct ec3_image_ioctx *container, + struct ec3_tag_ioctx *volu, + struct ec3_volume **out_volume); + +extern void ec3_volume_close(struct ec3_volume *volume); + +extern enum ec3_status ec3_volume_get_vnode( + struct ec3_volume *volume, + size_t id, + struct ec3_vnode *out); +extern enum ec3_status ec3_volume_put_vnode( + struct ec3_volume *volume, + const struct ec3_vnode *vnode); + +#endif