capture: implement directory structure capture
This commit is contained in:
19
src/bin.h
19
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;
|
||||
};
|
||||
|
||||
175
src/capture.c
175
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 <blue/cmd.h>
|
||||
#include <blue/io/directory.h>
|
||||
#include <blue/object/list.h>
|
||||
#include <blue/term/print.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -13,6 +13,7 @@ enum command_id {
|
||||
CMD_CHECK_SIG,
|
||||
CMD_EXPLORE,
|
||||
CMD_QUERY,
|
||||
CMD_TREE,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
247
src/fs-tree.c
Normal file
247
src/fs-tree.c
Normal 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
59
src/fs-tree.h
Normal 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
|
||||
78
src/image.c
78
src/image.c
@@ -4,11 +4,13 @@
|
||||
#include "pipeline.h"
|
||||
#include "shadow-image.h"
|
||||
#include "status.h"
|
||||
#include "tag.h"
|
||||
|
||||
#include <blue/io/directory.h>
|
||||
#include <blue/io/file.h>
|
||||
#include <blue/io/path.h>
|
||||
#include <blue/object/buffer.h>
|
||||
#include <blue/object/string.h>
|
||||
#include <stdlib.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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
14
src/image.h
14
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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
106
src/query.c
106
src/query.c
@@ -6,16 +6,21 @@
|
||||
|
||||
#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_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)
|
||||
|
||||
83
src/shell.c
83
src/shell.c
@@ -1,6 +1,11 @@
|
||||
#include "commands.h"
|
||||
#include "image.h"
|
||||
#include "tag.h"
|
||||
#include "volume.h"
|
||||
|
||||
#include <blue/cmd.h>
|
||||
#include <blue/term/print.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
356
src/tree.c
Normal file
356
src/tree.c
Normal 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, ¤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);
|
||||
}
|
||||
}
|
||||
20
src/vnode.c
Normal file
20
src/vnode.c
Normal 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
21
src/vnode.h
Normal 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
|
||||
519
src/volume.c
519
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 <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;
|
||||
}
|
||||
|
||||
43
src/volume.h
43
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
|
||||
|
||||
Reference in New Issue
Block a user