create: update to use new builder interface

This commit is contained in:
2025-07-04 15:39:22 +01:00
parent 54d5c081eb
commit 809e4425bf

View File

@@ -1,250 +1,461 @@
#if 0 #include "builder.h"
#include "b-tree.h"
#include "bin.h"
#include "commands.h" #include "commands.h"
#include "exe.h"
#include "image.h"
#include "substitute.h"
#include <blue/cmd.h> #include <blue/cmd.h>
#include <blue/io/file.h>
#include <blue/io/path.h>
#include <blue/object/dict.h>
#include <blue/object/number.h>
#include <blue/object/string.h>
#include <blue/serial.h>
#include <blue/term/print.h>
#include <errno.h>
#include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <zstd.h>
#define INVALID_NODE_PTR 0xFFFF
#define ORDER 6
#define MAX (ORDER - 1)
#define MIN (MAX / 2)
enum { enum {
ARG_INPATH, OPT_SPECIFICATION,
OPT_SPECIFICATION_PATH,
OPT_PARAMETER,
OPT_PARAMETER_NAME,
OPT_PARAMETER_VALUE,
OPT_OUTPATH, OPT_OUTPATH,
OPT_OUTPATH_PATH, OPT_OUTPATH_PATH,
}; };
struct entry { struct create_ctx {
uint32_t key; struct ec3_image_builder *ctx_builder;
uint32_t value; struct substitution_list ctx_sub_list;
b_dict *ctx_specification;
}; };
struct node { static enum ec3_status create_ctx_init(
uint16_t nr_entries; struct create_ctx *ctx,
struct entry entries[MAX]; const b_arglist *args)
uint16_t children[ORDER];
};
struct data_tree {
struct b_tree base;
FILE *fp;
};
static int node_init(struct node *n)
{ {
memset(n, 0x0, sizeof *n); enum ec3_status status = substitution_list_init(&ctx->ctx_sub_list);
for (int i = 0; i < ORDER; i++) { b_arglist_option_iterator it;
n->children[i] = INVALID_NODE_PTR; b_arglist_option_foreach_filtered(&it, args, OPT_PARAMETER)
{
b_arglist_value *name, *value;
b_arglist_option_get_value(
it.opt,
OPT_PARAMETER_NAME,
0,
&name);
b_arglist_option_get_value(
it.opt,
OPT_PARAMETER_VALUE,
0,
&value);
status = substitution_list_add_substitution(
&ctx->ctx_sub_list,
name->val_str,
value->val_str);
if (status != EC3_SUCCESS) {
return status;
}
} }
return 0; return EC3_SUCCESS;
} }
static int tree_get_node(struct b_tree *p, unsigned long id, b_tree_node *n) static void create_ctx_finish(struct create_ctx *ctx)
{ {
struct data_tree *tree = (struct data_tree *)p; if (ctx->ctx_builder) {
size_t offset = (id * sizeof(struct node)); ec3_image_builder_destroy(ctx->ctx_builder);
fseek(tree->fp, offset, SEEK_SET);
size_t r = fread(n, sizeof(struct node), 1, tree->fp);
return r == 1 ? 0 : -1;
}
static int tree_put_node(
struct b_tree *p,
unsigned long id,
const b_tree_node *n)
{
struct data_tree *tree = (struct data_tree *)p;
size_t offset = (id * sizeof(struct node));
fseek(tree->fp, offset, SEEK_SET);
size_t r = fwrite(n, sizeof(struct node), 1, tree->fp);
return r == 1 ? 0 : -1;
}
static long tree_alloc_node(struct b_tree *p)
{
struct data_tree *tree = (struct data_tree *)p;
size_t pos = ftell(tree->fp);
fseek(tree->fp, 0, SEEK_END);
size_t len = ftell(tree->fp);
struct node n;
node_init(&n);
fwrite(&n, sizeof n, 1, tree->fp);
fseek(tree->fp, pos, SEEK_SET);
len /= sizeof(struct node);
return (long)len;
}
static unsigned long node_get_nr_entries(b_tree_node *n)
{
struct node *node = (struct node *)n;
return node->nr_entries;
}
static void node_set_nr_entries(b_tree_node *n, unsigned long val)
{
struct node *node = (struct node *)n;
node->nr_entries = val;
}
static b_tree_node_entry *node_get_entry(b_tree_node *n, unsigned long index)
{
struct node *node = (struct node *)n;
return (b_tree_node_entry *)&node->entries[index];
}
static void node_set_entry(
b_tree_node *n,
unsigned long index,
const b_tree_node_entry *entry)
{
struct node *node = (struct node *)n;
memmove(&node->entries[index], entry, sizeof(struct entry));
}
static unsigned long node_get_child(b_tree_node *n, unsigned long index)
{
struct node *node = (struct node *)n;
uint16_t child = node->children[index];
return child == INVALID_NODE_PTR ? B_TREE_INVALID_PTR : child;
}
static void node_set_child(
b_tree_node *n,
unsigned long index,
unsigned long ptr)
{
struct node *node = (struct node *)n;
uint16_t child
= ptr == B_TREE_INVALID_PTR ? INVALID_NODE_PTR : (uint16_t)ptr;
node->children[index] = child;
}
static int entry_compare(
const b_tree_node_entry *e0,
const b_tree_node_entry *e1)
{
struct entry *a = (struct entry *)e0, *b = (struct entry *)e1;
if (a->key < b->key) {
return -1;
} }
if (a->key > b->key) { if (ctx->ctx_specification) {
return 1; b_dict_release(ctx->ctx_specification);
} }
return 0; substitution_list_finish(&ctx->ctx_sub_list);
} }
static const struct b_tree_ops ops = { static enum ec3_status configure_image_parameters(
.tree_get_node = tree_get_node, struct create_ctx *ctx,
.tree_put_node = tree_put_node, struct ec3_parameters *param)
.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_get_child = node_get_child,
.node_set_child = node_set_child,
.entry_compare = entry_compare,
};
void indent(int depth)
{ {
for (int i = 0; i < depth; i++) { enum ec3_status status = EC3_SUCCESS;
fputs(" ", stdout);
b_string *ident = B_STRING(b_dict_at(ctx->ctx_specification, "id"));
if (ident) {
if (B_TYPEID(ident) != B_OBJECT_TYPE_STRING) {
return EC3_ERR_BAD_FORMAT;
} }
status = substitution_list_substitute(
&ctx->ctx_sub_list,
ident);
if (status != EC3_SUCCESS) {
return status;
}
status = ec3_identifier_from_string(
b_string_ptr(ident),
&param->p_ident);
if (status != EC3_SUCCESS) {
return status;
}
}
b_number *cluster_size_p
= B_NUMBER(b_dict_at(ctx->ctx_specification, "clusterSize"));
if (B_TYPEID(cluster_size_p) != B_OBJECT_TYPE_NUMBER) {
return EC3_ERR_BAD_FORMAT;
}
param->p_cluster_size = ec3_cluster_size_bytes_to_id(
b_number_get_int(cluster_size_p));
if (param->p_cluster_size == -1) {
return EC3_ERR_NOT_SUPPORTED;
}
b_string *compression_p
= B_STRING(b_dict_at(ctx->ctx_specification, "compression"));
const char *compression = NULL;
if (compression_p) {
if (B_TYPEID(compression_p) != B_OBJECT_TYPE_STRING) {
return EC3_ERR_BAD_FORMAT;
}
compression = b_string_ptr(compression_p);
if (!strcmp(compression, "zstd")) {
param->p_compression_func = EC3_COMPRESSION_ZSTD;
} else {
return EC3_ERR_NOT_SUPPORTED;
}
}
b_string *encryption_p
= B_STRING(b_dict_at(ctx->ctx_specification, "encryption"));
const char *encryption = NULL;
if (encryption_p) {
if (B_TYPEID(encryption_p) != B_OBJECT_TYPE_STRING) {
return EC3_ERR_BAD_FORMAT;
}
encryption = b_string_ptr(encryption_p);
if (!strcmp(encryption, "aes256")) {
param->p_compression_func = EC3_ENCRYPTION_AES256;
} else {
return EC3_ERR_NOT_SUPPORTED;
}
}
return EC3_SUCCESS;
} }
int node_print(struct b_tree *tree, struct node *n, int depth) static b_status read_specification(const char *path_cstr, b_dict **out)
{ {
int c_before = (n->nr_entries + 1) / 2; b_path *path = b_path_create_from_cstr(path_cstr);
int c_after = n->nr_entries + 1 - c_before; b_file *file = NULL;
int i_before = 0; b_status status = b_file_open(NULL, path, B_FILE_READ_ONLY, &file);
int i_after = i_before + c_before; b_path_release(path);
int err = 0; if (!B_OK(status)) {
return status;
for (unsigned short i = 0; i < c_before; i++) {
struct node child;
int index = i_before + i;
if (n->children[index] == INVALID_NODE_PTR) {
// indent(depth + 1);
continue;
} }
err = tree_get_node( b_stream *src = NULL;
tree, status = b_file_open_stream(file, &src);
n->children[index], b_file_release(file);
(b_tree_node *)&child);
if (err != 0) { if (!B_OK(status)) {
printf("ERR: failed to read node %u\n", return status;
n->children[index]);
continue;
} }
node_print(tree, &child, depth + 1); b_serial_ctx *ctx = NULL;
b_serial_ctx_create(&ctx);
b_object *data = NULL;
status = b_serial_ctx_deserialise(
ctx,
B_SERIAL_FORMAT_JSON,
src,
&data,
0);
if (!B_OK(status)) {
return status;
} }
indent(depth); b_stream_close(src);
printf("[");
for (unsigned short i = 0; i < n->nr_entries; i++) { if (!B_OBJECT_IS(data, DICT)) {
if (i > 0) { b_release(data);
printf(" "); return B_ERR_BAD_FORMAT;
} }
printf("%d", n->entries[i].key); *out = B_DICT(data);
} return B_SUCCESS;
printf("]\n");
for (unsigned short i = 0; i < c_after; i++) {
struct node child;
int index = i_after + i;
if (n->children[index] == INVALID_NODE_PTR) {
// indent(depth + 1);
continue;
}
err = tree_get_node(
tree,
n->children[index],
(b_tree_node *)&child);
if (err != 0) {
printf("ERR: failed to read node %u\n",
n->children[index]);
continue;
}
node_print(tree, &child, depth + 1);
}
return 0;
} }
int tree_print(struct b_tree *tree) #define ASSERT_OBJECT(object, status) \
if (!object) { \
return status; \
}
#define ASSERT_OBJECT_TYPE(object, type, status) \
if (!B_OBJECT_IS(object, type)) { \
return status; \
}
static enum ec3_status capture_volumes(struct create_ctx *ctx)
{ {
struct node root; enum ec3_status status = EC3_SUCCESS;
tree_get_node(tree, 0, (b_tree_node *)&root);
return node_print(tree, &root, 0); b_array *volumes
= B_ARRAY(b_dict_at(ctx->ctx_specification, "volumes"));
if (!volumes) {
return EC3_SUCCESS;
}
ASSERT_OBJECT_TYPE(volumes, ARRAY, EC3_ERR_BAD_FORMAT);
b_array_iterator it;
b_array_foreach(&it, volumes)
{
b_dict *volume = B_DICT(it.value);
ASSERT_OBJECT_TYPE(volume, DICT, EC3_ERR_BAD_FORMAT);
uint64_t id = 0;
b_object *id_object = b_dict_at(volume, "id");
ASSERT_OBJECT(id_object, EC3_ERR_BAD_FORMAT);
if (B_OBJECT_IS(id_object, NUMBER)) {
id = (uint64_t)b_number_get_int64(B_NUMBER(id_object));
} else if (B_OBJECT_IS(id_object, STRING)) {
substitution_list_substitute(
&ctx->ctx_sub_list,
B_STRING(id_object));
status = ec3_identifier_from_string(
b_string_ptr(B_STRING(id_object)),
&id);
}
if (status != EC3_SUCCESS) {
return status;
}
b_string *src_path = B_STRING(b_dict_at(volume, "sourcePath"));
ASSERT_OBJECT(src_path, EC3_ERR_BAD_FORMAT);
ASSERT_OBJECT_TYPE(src_path, STRING, EC3_ERR_BAD_FORMAT);
substitution_list_substitute(&ctx->ctx_sub_list, src_path);
char id_str[32];
ec3_identifier_to_string(id, id_str, sizeof id_str);
printf("adding volume 0x%" PRIx64 "(%s) -> %s\n",
id,
id_str,
b_string_ptr(src_path));
status = ec3_image_builder_capture_directory(
ctx->ctx_builder,
id,
b_string_ptr(src_path));
if (status != EC3_SUCCESS) {
b_err("cannot capture directory '%s'",
b_string_ptr(src_path));
b_i("reason: %s", ec3_status_to_string(status));
return status;
}
}
return EC3_SUCCESS;
}
static enum ec3_status capture_blobs(struct create_ctx *ctx)
{
enum ec3_status status = EC3_SUCCESS;
b_array *blobs = B_ARRAY(b_dict_at(ctx->ctx_specification, "blobs"));
if (!blobs) {
return EC3_SUCCESS;
}
ASSERT_OBJECT_TYPE(blobs, ARRAY, EC3_ERR_BAD_FORMAT);
b_array_iterator it;
b_array_foreach(&it, blobs)
{
b_dict *blob = B_DICT(it.value);
ASSERT_OBJECT_TYPE(blob, DICT, EC3_ERR_BAD_FORMAT);
uint64_t id = 0;
b_object *id_object = b_dict_at(blob, "id");
if (!id_object) {
return EC3_ERR_BAD_FORMAT;
}
if (B_OBJECT_IS(id_object, NUMBER)) {
id = (uint64_t)b_number_get_int64(B_NUMBER(id_object));
} else if (B_OBJECT_IS(id_object, STRING)) {
substitution_list_substitute(
&ctx->ctx_sub_list,
B_STRING(id_object));
status = ec3_identifier_from_string(
b_string_ptr(B_STRING(id_object)),
&id);
}
if (status != EC3_SUCCESS) {
return status;
}
b_string *src_path = B_STRING(b_dict_at(blob, "sourcePath"));
ASSERT_OBJECT(src_path, EC3_ERR_BAD_FORMAT);
ASSERT_OBJECT_TYPE(src_path, STRING, EC3_ERR_BAD_FORMAT);
substitution_list_substitute(&ctx->ctx_sub_list, src_path);
char id_str[32];
ec3_identifier_to_string(id, id_str, sizeof id_str);
printf("adding blob 0x%" PRIx64 "(%s) -> %s\n",
id,
id_str,
b_string_ptr(src_path));
FILE *fp = fopen(b_string_ptr(src_path), "rb");
if (!fp) {
b_err("cannot open file '%s'", b_string_ptr(src_path));
b_i("reason: %s", strerror(errno));
return EC3_ERR_NO_ENTRY;
}
status = ec3_image_builder_add_blob_from_file(
ctx->ctx_builder,
id,
fp);
fclose(fp);
if (status != EC3_SUCCESS) {
b_err("cannot capture blob '%s'",
b_string_ptr(src_path));
b_i("reason: %s", ec3_status_to_string(status));
return status;
}
}
return EC3_SUCCESS;
}
static enum ec3_executable_format executable_format_string_to_id(const char *s)
{
if (!strcmp(s, "elf")) {
return EC3_EXEC_ELF;
}
return EC3_EXEC_OTHER;
}
static enum ec3_status capture_executables(struct create_ctx *ctx)
{
enum ec3_status status = EC3_SUCCESS;
b_array *executables
= B_ARRAY(b_dict_at(ctx->ctx_specification, "executables"));
if (!executables) {
return EC3_SUCCESS;
}
ASSERT_OBJECT_TYPE(executables, ARRAY, EC3_ERR_BAD_FORMAT);
b_array_iterator it;
b_array_foreach(&it, executables)
{
b_dict *executable = B_DICT(it.value);
ASSERT_OBJECT_TYPE(executable, DICT, EC3_ERR_BAD_FORMAT);
uint64_t id = 0;
b_object *id_object = b_dict_at(executable, "id");
if (!id_object) {
return EC3_ERR_BAD_FORMAT;
}
if (B_OBJECT_IS(id_object, NUMBER)) {
id = (uint64_t)b_number_get_int64(B_NUMBER(id_object));
} else if (B_OBJECT_IS(id_object, STRING)) {
substitution_list_substitute(
&ctx->ctx_sub_list,
B_STRING(id_object));
status = ec3_identifier_from_string(
b_string_ptr(B_STRING(id_object)),
&id);
}
if (status != EC3_SUCCESS) {
return status;
}
b_string *src_path
= B_STRING(b_dict_at(executable, "sourcePath"));
ASSERT_OBJECT(src_path, EC3_ERR_BAD_FORMAT);
ASSERT_OBJECT_TYPE(src_path, STRING, EC3_ERR_BAD_FORMAT);
substitution_list_substitute(&ctx->ctx_sub_list, src_path);
b_string *format = B_STRING(b_dict_at(executable, "format"));
ASSERT_OBJECT(format, EC3_ERR_BAD_FORMAT);
ASSERT_OBJECT_TYPE(format, STRING, EC3_ERR_BAD_FORMAT);
substitution_list_substitute(&ctx->ctx_sub_list, format);
b_string_tolower(format);
char id_str[32];
ec3_identifier_to_string(id, id_str, sizeof id_str);
printf("adding %s executable 0x%" PRIx64 "(%s) -> %s\n",
b_string_ptr(format),
id,
id_str,
b_string_ptr(src_path));
FILE *fp = fopen(b_string_ptr(src_path), "rb");
if (!fp) {
b_err("cannot open file '%s'", b_string_ptr(src_path));
b_i("reason: %s", strerror(errno));
return EC3_ERR_NO_ENTRY;
}
enum ec3_executable_format exe_format
= executable_format_string_to_id(b_string_ptr(format));
struct ec3_tag_executable_info exe_info = {0};
status = get_executable_info_from_file(
fp,
exe_format,
&exe_info);
if (status != EC3_SUCCESS) {
return status;
}
fseek(fp, 0, SEEK_SET);
status = ec3_image_builder_add_executable_from_file(
ctx->ctx_builder,
id,
&exe_info,
fp);
fclose(fp);
if (status != EC3_SUCCESS) {
b_err("cannot capture executable '%s'",
b_string_ptr(src_path));
b_i("reason: %s", ec3_status_to_string(status));
return status;
}
}
return EC3_SUCCESS;
} }
static int create( static int create(
@@ -252,48 +463,74 @@ static int create(
const b_arglist *opt, const b_arglist *opt,
const b_array *args) const b_array *args)
{ {
printf("%zu\n", sizeof(struct ec3_cluster_group)); const char *out_path_cstr = NULL;
return 0; const char *specification_path_cstr = NULL;
const char *in_path = NULL, *out_path = NULL; b_status status = b_arglist_get_string(
b_arglist_get_string(
opt, opt,
B_COMMAND_INVALID_ID, OPT_SPECIFICATION,
ARG_INPATH, OPT_SPECIFICATION_PATH,
0, 0,
&in_path); &specification_path_cstr);
b_arglist_get_string(opt, OPT_OUTPATH, OPT_OUTPATH_PATH, 0, &out_path); if (!B_OK(status)) {
b_err("no specification file specified\n");
printf("in path: %s\n", in_path); return -1;
printf("out path: %s\n", out_path);
FILE *fp = fopen(out_path, "w+b");
struct node root;
node_init(&root);
fwrite(&root, sizeof root, 1, fp);
struct data_tree tree;
b_tree_init(
&tree.base,
&ops,
sizeof(struct node),
sizeof(struct entry),
ORDER);
tree.fp = fp;
for (int i = 1; i <= 32; i++) {
int key = rand() % 65535;
// int key = i;
// printf("%d: adding [%d]\n", i, key);
struct entry e = {.key = key, .value = key * 2};
b_tree_put(&tree.base, (b_tree_node_entry *)&e);
} }
tree_print(&tree.base); status = b_arglist_get_string(
opt,
OPT_OUTPATH,
OPT_OUTPATH_PATH,
0,
&out_path_cstr);
if (!B_OK(status)) {
b_err("no output file path specified\n");
return -1;
}
fclose(fp); struct create_ctx ctx = {0};
enum ec3_status status2 = create_ctx_init(&ctx, opt);
status = read_specification(
specification_path_cstr,
&ctx.ctx_specification);
if (!B_OK(status)) {
b_err("cannot read specification file.");
b_i("reason: %s", b_status_to_string(status));
return -1;
}
struct ec3_parameters param = {};
status2 = configure_image_parameters(&ctx, &param);
if (status2 != EC3_SUCCESS) {
return -1;
}
status2 = ec3_image_builder_create(
out_path_cstr,
&param,
&ctx.ctx_builder);
if (status2 != EC3_SUCCESS) {
b_err("cannot initialise EC3 writer");
return -1;
}
status2 = capture_volumes(&ctx);
if (status2 != EC3_SUCCESS) {
return -1;
}
status2 = capture_blobs(&ctx);
if (status2 != EC3_SUCCESS) {
return -1;
}
status2 = capture_executables(&ctx);
if (status2 != EC3_SUCCESS) {
return -1;
}
create_ctx_finish(&ctx);
return 0; return 0;
} }
@@ -320,16 +557,50 @@ B_COMMAND(CMD_CREATE, CMD_ROOT)
} }
} }
B_COMMAND_ARG(ARG_INPATH) B_COMMAND_OPTION(OPT_SPECIFICATION)
{ {
B_ARG_NAME("input file"); B_OPTION_SHORT_NAME('s');
B_OPTION_LONG_NAME("specification");
B_OPTION_DESC(
"the path to a specification file describing the "
"layout and parameters of the new file");
B_OPTION_ARG(OPT_SPECIFICATION_PATH)
{
B_ARG_NAME("path");
B_ARG_NR_VALUES(1); B_ARG_NR_VALUES(1);
} }
}
B_COMMAND_OPTION(OPT_PARAMETER)
{
B_OPTION_SHORT_NAME('p');
B_OPTION_LONG_NAME("parameter");
B_OPTION_DESC("a parameter to use when creating the new file.");
B_OPTION_ARG(OPT_PARAMETER_NAME)
{
B_ARG_NAME("name");
B_ARG_NR_VALUES(1);
}
B_OPTION_ARG(OPT_PARAMETER_VALUE)
{
B_ARG_NAME("value");
B_ARG_NR_VALUES(1);
}
}
B_COMMAND_USAGE() B_COMMAND_USAGE()
{ {
B_COMMAND_USAGE_ARG(ARG_INPATH); B_COMMAND_USAGE_OPT(OPT_SPECIFICATION);
B_COMMAND_USAGE_OPT(OPT_OUTPATH);
}
B_COMMAND_USAGE()
{
B_COMMAND_USAGE_OPT(OPT_SPECIFICATION);
B_COMMAND_USAGE_OPT(OPT_PARAMETER);
B_COMMAND_USAGE_OPT(OPT_OUTPATH); B_COMMAND_USAGE_OPT(OPT_OUTPATH);
} }
} }
#endif