capture: implement directory structure capture

This commit is contained in:
2025-04-26 21:28:43 +01:00
parent d5df4d593f
commit 150b092d1b
18 changed files with 1737 additions and 32 deletions

View File

@@ -48,6 +48,11 @@ enum ec3_tag_flags {
EC3_TAG_ENCRYPTED = 0x00000004u EC3_TAG_ENCRYPTED = 0x00000004u
}; };
enum ec3_vnode_mode {
EC3_V_REG = 0x0001u,
EC3_V_DIR = 0x0002u,
};
#define EC3_INVALID_OFFSET 0xFFFFFFFFu #define EC3_INVALID_OFFSET 0xFFFFFFFFu
/* 32K per cluster group */ /* 32K per cluster group */
@@ -190,9 +195,9 @@ struct ec3_directory_entry {
b_i32 d_vnode; b_i32 d_vnode;
}; };
struct ec3_vnode { struct ec3_bin_vnode {
b_i32 n_id; 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_mode, n_reserved;
b_i16 n_uid, n_gid; b_i16 n_uid, n_gid;
/* id of the chunk containing the vnode data */ /* id of the chunk containing the vnode data */
@@ -206,26 +211,26 @@ struct ec3_vnode_group {
union { union {
struct { 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]; b_i32 g_child_offsets[EC3_VNODES_PER_GROUP_4K + 1];
} g_4k; } g_4k;
struct { 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]; b_i32 g_child_offsets[EC3_VNODES_PER_GROUP_8K + 1];
} g_8k; } g_8k;
struct { 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]; b_i32 g_child_offsets[EC3_VNODES_PER_GROUP_16K + 1];
} g_16k; } g_16k;
struct { 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]; b_i32 g_child_offsets[EC3_VNODES_PER_GROUP_32K + 1];
} g_32k; } g_32k;
struct { 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]; b_i32 g_child_offsets[EC3_VNODES_PER_GROUP_64K + 1];
} g_64k; } g_64k;
}; };

View File

@@ -1,13 +1,16 @@
#include "bin.h" #include "bin.h"
#include "chunk-table.h" #include "chunk-table.h"
#include "commands.h" #include "commands.h"
#include "fs-tree.h"
#include "image.h" #include "image.h"
#include "misc.h" #include "misc.h"
#include "status.h" #include "status.h"
#include "string-table.h" #include "string-table.h"
#include "volume.h"
#include <blue/cmd.h> #include <blue/cmd.h>
#include <blue/io/directory.h> #include <blue/io/directory.h>
#include <blue/object/list.h>
#include <blue/term/print.h> #include <blue/term/print.h>
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
@@ -33,17 +36,20 @@ enum {
}; };
struct capture_ctx { struct capture_ctx {
struct fs_tree ctx_tree;
struct ec3_image_ioctx *ctx_image; struct ec3_image_ioctx *ctx_image;
struct chunk_table ctx_chunks; struct chunk_table ctx_chunks;
struct string_table ctx_strings; struct string_table ctx_strings;
struct ec3_volume *ctx_volume;
}; };
static enum ec3_status capture_file( static enum ec3_status capture_file(
struct capture_ctx *ctx, struct capture_ctx *ctx,
struct ec3_tag_ioctx *vol, struct ec3_volume *vol,
b_directory *dir, b_directory *dir,
const char *filename, const char *filename,
const b_path *filepath) const b_path *filepath,
ec3_chunk_id out_chunk)
{ {
const struct ec3_image_info *image_info const struct ec3_image_info *image_info
= ec3_image_ioctx_get_info(ctx->ctx_image); = 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, out_chunk);
s2 = chunk_table_end_chunk(&ctx->ctx_chunks, id);
if (s2 != EC3_SUCCESS) { if (s2 != EC3_SUCCESS) {
return s2; return s2;
} }
char id_str[128]; char id_str[128];
ec3_chunk_id_to_string(id, id_str, sizeof 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); // printf("wrote %zu byte chunk %s\n", chunk_size, id_str);
free(buf); free(buf);
b_file_release(src); b_file_release(src);
return s2; 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( static enum ec3_status capture_directory(
struct capture_ctx *ctx, struct capture_ctx *ctx,
uint64_t id, uint64_t id,
@@ -143,28 +270,43 @@ static enum ec3_status capture_directory(
return s2; return s2;
} }
struct ec3_volume *volume;
s2 = ec3_volume_create(ctx->ctx_image, volu, &volume);
if (s2 != EC3_SUCCESS) { if (s2 != EC3_SUCCESS) {
ec3_tag_ioctx_close(volu); ec3_tag_ioctx_close(volu);
b_directory_release(dir); b_directory_release(dir);
return s2; 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 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)) { while (b_directory_iterator_is_valid(&it)) {
if (!b_directory_path_is_file(dir, it.filepath)) { if (!b_directory_path_is_file(dir, it.filepath)) {
printf("dir: %s\n", b_path_ptr(it.filepath));
b_directory_iterator_next(&it); b_directory_iterator_next(&it);
continue; 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( enum ec3_status status2 = capture_file(
ctx, ctx,
volu, volume,
dir, dir,
it.filename, it.filename,
it.filepath); it.filepath,
vnode.v_data);
if (status2 != EC3_SUCCESS) { if (status2 != EC3_SUCCESS) {
b_err("failed to capture file '%s'", b_err("failed to capture file '%s'",
@@ -173,9 +315,22 @@ static enum ec3_status capture_directory(
break; 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); 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); ec3_tag_ioctx_close(volu);
b_directory_release(dir); b_directory_release(dir);

View File

@@ -438,17 +438,23 @@ void chunk_table_finish(struct chunk_table *tab)
enum ec3_status chunk_table_get( enum ec3_status chunk_table_get(
struct chunk_table *tab, struct chunk_table *tab,
ec3_chunk_id id, ec3_chunk_id id,
void *out_data, struct ec3_chunk *out)
size_t *out_len)
{ {
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) enum ec3_status chunk_table_begin_chunk(struct chunk_table *tab)
{ {
b_hash_ctx_init(&tab->tab_hash, B_HASH_SHAKE128); b_hash_ctx_init(&tab->tab_hash, B_HASH_SHAKE128);
size_t nr_written = 0; 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; 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_cluster_buf_pos = 0;
tab->tab_nr_clusters++;
return EC3_SUCCESS; return EC3_SUCCESS;
} }
@@ -550,6 +557,9 @@ enum ec3_status chunk_table_end_chunk(
struct ec3_chunk chunk = {0}; struct ec3_chunk chunk = {0};
memcpy(chunk.c_id, id, sizeof id); 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( int err = b_tree_put(
&tab->tab_chunks, &tab->tab_chunks,
(const b_tree_node_entry *)&chunk); (const b_tree_node_entry *)&chunk);

View File

@@ -24,7 +24,8 @@ struct chunk_table {
struct ec3_tag_ioctx *tab_cdat; struct ec3_tag_ioctx *tab_cdat;
b_hash_ctx tab_hash; 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( 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( extern enum ec3_status chunk_table_get(
struct chunk_table *tab, struct chunk_table *tab,
ec3_chunk_id id, ec3_chunk_id id,
void *out_data, struct ec3_chunk *out);
size_t *out_len);
extern enum ec3_status chunk_table_begin_chunk(struct chunk_table *tab); extern enum ec3_status chunk_table_begin_chunk(struct chunk_table *tab);
extern enum ec3_status chunk_table_put( extern enum ec3_status chunk_table_put(

View File

@@ -13,6 +13,7 @@ enum command_id {
CMD_CHECK_SIG, CMD_CHECK_SIG,
CMD_EXPLORE, CMD_EXPLORE,
CMD_QUERY, CMD_QUERY,
CMD_TREE,
}; };
#endif #endif

247
src/fs-tree.c Normal file
View File

@@ -0,0 +1,247 @@
#include "fs-tree.h"
#include <blue/object/string.h>
#include <stdlib.h>
#include <string.h>
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;
}

59
src/fs-tree.h Normal file
View File

@@ -0,0 +1,59 @@
#ifndef FS_TREE_H_
#define FS_TREE_H_
#include "bin.h"
#include <blue/core/queue.h>
#include <blue/core/status.h>
#include <blue/object/hashmap.h>
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

View File

@@ -4,11 +4,13 @@
#include "pipeline.h" #include "pipeline.h"
#include "shadow-image.h" #include "shadow-image.h"
#include "status.h" #include "status.h"
#include "tag.h"
#include <blue/io/directory.h> #include <blue/io/directory.h>
#include <blue/io/file.h> #include <blue/io/file.h>
#include <blue/io/path.h> #include <blue/io/path.h>
#include <blue/object/buffer.h> #include <blue/object/buffer.h>
#include <blue/object/string.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -600,6 +602,82 @@ const struct ec3_extent_info *ec3_image_ioctx_get_extent_info(
return b_buffer_ptr(image->io_extent_table); 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) static uint64_t allocate_tag_id(struct ec3_image_ioctx *image)
{ {
uint64_t id = 0; uint64_t id = 0;

View File

@@ -9,6 +9,7 @@
struct b_file; struct b_file;
struct b_buffer; struct b_buffer;
struct b_string;
struct cluster; struct cluster;
struct string_table; struct string_table;
@@ -73,6 +74,9 @@ struct ec3_image_ioctx {
struct cluster_table io_cluster_table; struct cluster_table io_cluster_table;
struct b_file *io_main; struct b_file *io_main;
struct ec3_tag_ioctx *io_stab;
void *io_stab_buf;
}; };
extern enum ec3_status ec3_image_ioctx_open( 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( extern const struct ec3_extent_info *ec3_image_ioctx_get_extent_info(
struct ec3_image_ioctx *image); 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, struct ec3_image_ioctx *image,
enum ec3_image_ioctx_mode mode, size_t key,
struct string_table **out); struct b_string *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);
extern enum ec3_status ec3_image_ioctx_open_tag_by_type( extern enum ec3_status ec3_image_ioctx_open_tag_by_type(
struct ec3_image_ioctx *image, struct ec3_image_ioctx *image,

View File

@@ -18,6 +18,9 @@
__pragma(pack(push, 1)) __Declaration__ __pragma(pack(pop)) __pragma(pack(push, 1)) __Declaration__ __pragma(pack(pop))
#endif #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_from_string(const char *s, uint64_t *out);
extern enum ec3_status ec3_identifier_to_string( extern enum ec3_status ec3_identifier_to_string(
uint64_t id, uint64_t id,

View File

@@ -6,16 +6,21 @@
#include <blue/cmd.h> #include <blue/cmd.h>
#include <blue/core/endian.h> #include <blue/core/endian.h>
#include <blue/object/string.h>
#include <blue/term.h> #include <blue/term.h>
#include <ctype.h>
#include <errno.h> #include <errno.h>
#include <stdlib.h>
enum { enum {
ARG_CONTAINER, ARG_CONTAINER,
OPT_VERBOSE, OPT_VERBOSE,
OPT_INFO_ALL,
OPT_INFO_IMAGE, OPT_INFO_IMAGE,
OPT_INFO_TAGS, OPT_INFO_TAGS,
OPT_INFO_EXTENTS, OPT_INFO_EXTENTS,
OPT_INFO_STRINGS,
OPT_INFO_CHUNKS, OPT_INFO_CHUNKS,
}; };
@@ -25,6 +30,7 @@ enum info_type {
INFO_TAGS = 0x02, INFO_TAGS = 0x02,
INFO_EXTENTS = 0x04, INFO_EXTENTS = 0x04,
INFO_CHUNKS = 0x08, INFO_CHUNKS = 0x08,
INFO_STRINGS = 0x10,
}; };
static void tag_type_string(unsigned long in, char out[5]) 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) static enum info_type get_selected_info_types(const struct b_arglist *opt)
{ {
enum info_type type = INFO_NONE; 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) if (b_arglist_get_count(opt, OPT_INFO_IMAGE, B_COMMAND_INVALID_ID)
> 0) { > 0) {
@@ -168,6 +239,16 @@ static enum info_type get_selected_info_types(const struct b_arglist *opt)
type |= INFO_EXTENTS; 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; return type;
} }
@@ -213,6 +294,10 @@ static int query(
print_extent_info(reader); print_extent_info(reader);
} }
if (selected_info & INFO_STRINGS) {
print_strings_info(reader);
}
ec3_image_ioctx_close(reader); ec3_image_ioctx_close(reader);
fclose(fp); fclose(fp);
@@ -236,6 +321,14 @@ B_COMMAND(CMD_QUERY, CMD_ROOT)
B_ARG_NR_VALUES(1); 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_COMMAND_OPTION(OPT_INFO_IMAGE)
{ {
B_OPTION_LONG_NAME("header"); B_OPTION_LONG_NAME("header");
@@ -254,7 +347,18 @@ B_COMMAND(CMD_QUERY, CMD_ROOT)
{ {
B_OPTION_LONG_NAME("extents"); B_OPTION_LONG_NAME("extents");
B_OPTION_SHORT_NAME('x'); 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) B_COMMAND_OPTION(OPT_INFO_CHUNKS)

View File

@@ -1,6 +1,11 @@
#include "commands.h" #include "commands.h"
#include "image.h"
#include "tag.h"
#include "volume.h"
#include <blue/cmd.h> #include <blue/cmd.h>
#include <blue/term/print.h>
#include <stdio.h>
enum { enum {
ARG_CONTAINER, ARG_CONTAINER,
@@ -12,6 +17,84 @@ static int shell(
const b_arglist *opt, const b_arglist *opt,
const b_array *args) 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; return 0;
} }

View File

@@ -15,6 +15,7 @@ enum ec3_status {
EC3_ERR_IO_FAILURE, EC3_ERR_IO_FAILURE,
EC3_ERR_END_OF_FILE, EC3_ERR_END_OF_FILE,
EC3_ERR_OUT_OF_BOUNDS, EC3_ERR_OUT_OF_BOUNDS,
EC3_ERR_INTERNAL_FAILURE,
}; };
extern enum ec3_status ec3_status_from_b_status( extern enum ec3_status ec3_status_from_b_status(

356
src/tree.c Normal file
View File

@@ -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 <blue/cmd.h>
#include <blue/core/endian.h>
#include <blue/object/string.h>
#include <blue/term.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
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, &current->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, &current->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);
}
}

20
src/vnode.c Normal file
View File

@@ -0,0 +1,20 @@
#include "vnode.h"
#include <blue/io/file.h>
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;
}

21
src/vnode.h Normal file
View File

@@ -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

View File

@@ -0,0 +1,519 @@
#include "volume.h"
#include "b-tree.h"
#include "cluster-table.h"
#include "image.h"
#include "tag.h"
#include <stdlib.h>
#include <string.h>
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 <stdio.h>
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;
}

View File

@@ -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