diff --git a/src/query.c b/src/query.c index 690f7cd..c38cf14 100644 --- a/src/query.c +++ b/src/query.c @@ -1,8 +1,10 @@ #include "bin.h" +#include "chunk-table.h" #include "commands.h" #include "image.h" #include "misc.h" #include "status.h" +#include "vnode.h" #include #include @@ -22,6 +24,9 @@ enum { OPT_INFO_EXTENTS, OPT_INFO_STRINGS, OPT_INFO_CHUNKS, + OPT_INFO_VNODES, + + OPT_IN_ORDER, }; enum info_type { @@ -31,6 +36,7 @@ enum info_type { INFO_EXTENTS = 0x04, INFO_CHUNKS = 0x08, INFO_STRINGS = 0x10, + INFO_VNODES = 0x20, }; static void tag_type_string(unsigned long in, char out[5]) @@ -217,6 +223,240 @@ static void print_strings_info(struct ec3_image_ioctx *image) free(cluster_buf); } +static enum ec3_status print_chunk_table( + struct ec3_image_ioctx *img, + struct ec3_tag_ioctx *ctab, + size_t index, + size_t depth) +{ + if (depth > 1) { + abort(); + } + + const struct ec3_image_info *img_info = ec3_image_ioctx_get_info(img); + unsigned char *cluster_buf = malloc(img_info->img_cluster_size); + if (!cluster_buf) { + return EC3_ERR_NO_MEMORY; + } + + enum ec3_cluster_size cluster_size + = ec3_cluster_size_bytes_to_id(img_info->img_cluster_size); + + size_t nr_read; + char chunk_id[100]; + enum ec3_status status = ec3_tag_ioctx_read_cluster( + ctab, + index, + cluster_buf, + &nr_read); + + if (status != EC3_SUCCESS) { + free(cluster_buf); + return status; + } + + struct ec3_chunk_group *group = (struct ec3_chunk_group *)cluster_buf; + struct ec3_chunk *chunks = NULL; + b_i32 *children = NULL; + size_t nr_chunks = b_i16_btoh(group->g_nr_chunks); + + switch (cluster_size) { + case EC3_CLUSTER_4K: + chunks = group->g_4k.g_chunks; + children = group->g_4k.g_child_offsets; + break; + case EC3_CLUSTER_8K: + chunks = group->g_8k.g_chunks; + children = group->g_8k.g_child_offsets; + break; + case EC3_CLUSTER_16K: + chunks = group->g_16k.g_chunks; + children = group->g_16k.g_child_offsets; + break; + case EC3_CLUSTER_32K: + chunks = group->g_32k.g_chunks; + children = group->g_32k.g_child_offsets; + break; + case EC3_CLUSTER_64K: + chunks = group->g_64k.g_chunks; + children = group->g_64k.g_child_offsets; + break; + default: + abort(); + } + + size_t child = b_i32_btoh(children[0]); + if (child == 0) { + printf("child 0 of node %zu is 0\n", index); + abort(); + } + + if (child != EC3_INVALID_OFFSET) { + if (depth == 1) { + printf("break\n"); + } + + status = print_chunk_table(img, ctab, child, depth + 1); + } + + if (status != EC3_SUCCESS) { + free(cluster_buf); + return status; + } + + for (size_t i = 0; i < nr_chunks; i++) { + ec3_chunk_id_to_string( + chunks[i].c_id, + chunk_id, + sizeof chunk_id); + +#if 1 + for (size_t d = 0; d < depth; d++) { + printf(" "); + } +#endif + + printf("[%zu:%s] start: %zu, count: %zu\n", + depth, + chunk_id, + (size_t)b_i32_btoh(chunks[i].c_first_cluster), + (size_t)b_i32_btoh(chunks[i].c_nr_clusters)); + + if (i > 0) { + int cmp = memcmp( + chunks[i].c_id, + chunks[i - 1].c_id, + sizeof(ec3_chunk_id)); + + if (cmp < 0) { + fprintf(stderr, "nodes are out of order!\n"); + abort(); + } + } + + child = b_i32_btoh(children[i + 1]); + if (child == 0) { + printf("child %zu of node %zu is 0\n", i + 1, index); + abort(); + } + + if (child != EC3_INVALID_OFFSET) { + if (depth == 1) { + printf("break\n"); + } + + status = print_chunk_table(img, ctab, child, depth + 1); + } + + if (status != EC3_SUCCESS) { + break; + } + } + + free(cluster_buf); + return EC3_SUCCESS; +} + +static void print_chunk_info_inorder(struct ec3_image_ioctx *image) +{ + struct ec3_tag_ioctx *ctab; + enum ec3_status status = ec3_image_ioctx_open_tag_by_type( + image, + EC3_TAG_CTAB, + 0, + EC3_TAG_IO_READ, + &ctab); + + if (status != EC3_SUCCESS) { + return; + } + + printf("chunks:\n"); + print_chunk_table(image, ctab, 0, 0); + + ec3_tag_ioctx_close(ctab); +} + +static enum ec3_status print_chunk_info(struct ec3_image_ioctx *img) +{ + struct ec3_tag_ioctx *ctab; + enum ec3_status status = ec3_image_ioctx_open_tag_by_type( + img, + EC3_TAG_CTAB, + 0, + EC3_TAG_IO_READ, + &ctab); + + if (status != EC3_SUCCESS) { + return status; + } + + const struct ec3_image_info *img_info = ec3_image_ioctx_get_info(img); + unsigned char *cluster_buf = malloc(img_info->img_cluster_size); + if (!cluster_buf) { + return EC3_ERR_NO_MEMORY; + } + + size_t nr_clusters = 0; + ec3_tag_ioctx_get_nr_clusters(ctab, &nr_clusters); + enum ec3_cluster_size cluster_size + = ec3_cluster_size_bytes_to_id(img_info->img_cluster_size); + + for (size_t cluster = 0; cluster < nr_clusters; cluster++) { + size_t nr_read; + char chunk_id[100]; + status = ec3_tag_ioctx_read_cluster( + ctab, + cluster, + cluster_buf, + &nr_read); + + if (status != EC3_SUCCESS) { + free(cluster_buf); + return status; + } + + struct ec3_chunk_group *group + = (struct ec3_chunk_group *)cluster_buf; + struct ec3_chunk *chunks = NULL; + size_t nr_chunks = b_i16_btoh(group->g_nr_chunks); + + switch (cluster_size) { + case EC3_CLUSTER_4K: + chunks = group->g_4k.g_chunks; + break; + case EC3_CLUSTER_8K: + chunks = group->g_8k.g_chunks; + break; + case EC3_CLUSTER_16K: + chunks = group->g_16k.g_chunks; + break; + case EC3_CLUSTER_32K: + chunks = group->g_32k.g_chunks; + break; + case EC3_CLUSTER_64K: + chunks = group->g_64k.g_chunks; + break; + default: + abort(); + } + + for (size_t i = 0; i < nr_chunks; i++) { + ec3_chunk_id_to_string( + chunks[i].c_id, + chunk_id, + sizeof chunk_id); + printf(" [%s] start: %zu, count: %zu\n", + chunk_id, + (size_t)b_i32_btoh(chunks[i].c_first_cluster), + (size_t)b_i32_btoh(chunks[i].c_nr_clusters)); + } + } + + free(cluster_buf); + return EC3_SUCCESS; +} + static enum info_type get_selected_info_types(const struct b_arglist *opt) { enum info_type type = INFO_NONE; @@ -249,9 +489,103 @@ static enum info_type get_selected_info_types(const struct b_arglist *opt) type |= INFO_STRINGS; } + if (b_arglist_get_count(opt, OPT_INFO_VNODES, B_COMMAND_INVALID_ID) + > 0) { + type |= INFO_VNODES; + } + return type; } +static enum ec3_status print_vnode_info(struct ec3_image_ioctx *img) +{ + struct ec3_tag_ioctx *volu; + enum ec3_status status = ec3_image_ioctx_open_tag_by_type( + img, + EC3_TAG_VOLU, + 0, + EC3_TAG_IO_READ, + &volu); + + if (status != EC3_SUCCESS) { + return status; + } + + const struct ec3_image_info *img_info = ec3_image_ioctx_get_info(img); + unsigned char *cluster_buf = malloc(img_info->img_cluster_size); + if (!cluster_buf) { + return EC3_ERR_NO_MEMORY; + } + + size_t nr_clusters = 0; + ec3_tag_ioctx_get_nr_clusters(volu, &nr_clusters); + enum ec3_cluster_size cluster_size + = ec3_cluster_size_bytes_to_id(img_info->img_cluster_size); + + for (size_t cluster = 0; cluster < nr_clusters; cluster++) { + size_t nr_read; + char chunk_id[100]; + status = ec3_tag_ioctx_read_cluster( + volu, + cluster, + cluster_buf, + &nr_read); + + if (status != EC3_SUCCESS) { + free(cluster_buf); + return status; + } + + struct ec3_vnode_group *group + = (struct ec3_vnode_group *)cluster_buf; + struct ec3_bin_vnode *vnodes = NULL; + size_t nr_vnodes = b_i16_btoh(group->g_nr_vnodes); + + switch (cluster_size) { + case EC3_CLUSTER_4K: + vnodes = group->g_4k.g_vnodes; + break; + case EC3_CLUSTER_8K: + vnodes = group->g_8k.g_vnodes; + break; + case EC3_CLUSTER_16K: + vnodes = group->g_16k.g_vnodes; + break; + case EC3_CLUSTER_32K: + vnodes = group->g_32k.g_vnodes; + break; + case EC3_CLUSTER_64K: + vnodes = group->g_64k.g_vnodes; + break; + default: + abort(); + } + + for (size_t i = 0; i < nr_vnodes; i++) { + struct ec3_vnode vnode; + ec3_vnode_decode(&vnodes[i], &vnode); + printf(" [%06lu] gid:%u, uid:%u, mode:0x%x, " + "ctime:%lu, " + "mtime:%lu\n", + vnode.v_id, + vnode.v_gid, + vnode.v_uid, + vnode.v_mode, + vnode.v_ctime, + vnode.v_mtime); + + ec3_chunk_id_to_string( + vnode.v_data, + chunk_id, + sizeof chunk_id); + printf(" data:%s\n", chunk_id); + } + } + + free(cluster_buf); + return EC3_SUCCESS; +} + static int query( const b_command *self, const b_arglist *opt, @@ -298,6 +632,23 @@ static int query( print_strings_info(reader); } + if (selected_info & INFO_CHUNKS) { + bool in_order = b_arglist_get_count( + opt, + OPT_IN_ORDER, + B_COMMAND_INVALID_ID) + > 0; + if (in_order) { + print_chunk_info_inorder(reader); + } else { + print_chunk_info(reader); + } + } + + if (selected_info & INFO_VNODES) { + print_vnode_info(reader); + } + ec3_image_ioctx_close(reader); fclose(fp); @@ -326,7 +677,8 @@ B_COMMAND(CMD_QUERY, CMD_ROOT) B_OPTION_LONG_NAME("all"); B_OPTION_SHORT_NAME('a'); B_OPTION_DESC( - "print all available information about the image."); + "print all available information about the " + "image."); } B_COMMAND_OPTION(OPT_INFO_IMAGE) @@ -368,6 +720,22 @@ B_COMMAND(CMD_QUERY, CMD_ROOT) B_OPTION_DESC("print the contents of the image chunk table."); } + B_COMMAND_OPTION(OPT_INFO_VNODES) + { + B_OPTION_LONG_NAME("vnodes"); + B_OPTION_SHORT_NAME('v'); + B_OPTION_DESC("print the contents of a volume vnode table."); + } + + B_COMMAND_OPTION(OPT_IN_ORDER) + { + B_OPTION_LONG_NAME("in-order"); + B_OPTION_SHORT_NAME('i'); + B_OPTION_DESC( + "sort the chunk table output by hash. this will " + "make the process slower and more memory-intensive."); + } + B_COMMAND_USAGE() { B_COMMAND_USAGE_ARG(ARG_CONTAINER);