query: add support for querying chunk and vnode table contents

This commit is contained in:
2025-06-23 13:39:19 +01:00
parent bad37dfeb6
commit 5fa6421f6c

View File

@@ -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 <blue/cmd.h>
#include <blue/core/endian.h>
@@ -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);