Add ASL FS metadata and layout helper

This commit is contained in:
Carl Niklas Rydberg 2025-12-20 07:54:23 +01:00
parent 428f657f4f
commit c6f9c6a696
6 changed files with 1039 additions and 161 deletions

View file

@ -96,6 +96,8 @@ set(AMDUAT_TGK_SRCS
set(AMDUAT_ASL_STORE_FS_SRCS set(AMDUAT_ASL_STORE_FS_SRCS
src/adapters/asl_store_fs/asl_store_fs.c src/adapters/asl_store_fs/asl_store_fs.c
src/adapters/asl_store_fs/asl_store_fs_layout.c
src/adapters/asl_store_fs/asl_store_fs_meta.c
) )
set(AMDUAT_TGK_STORE_MEM_SRCS set(AMDUAT_TGK_STORE_MEM_SRCS

View file

@ -0,0 +1,42 @@
#ifndef AMDUAT_ASL_STORE_FS_META_H
#define AMDUAT_ASL_STORE_FS_META_H
#include "amduat/asl/core.h"
#include "amduat/asl/store.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
enum { AMDUAT_ASL_STORE_FS_STORE_ID_MAX = 256 };
typedef struct {
amduat_asl_store_config_t config;
char store_id[AMDUAT_ASL_STORE_FS_STORE_ID_MAX + 1];
} amduat_asl_store_fs_config_t;
typedef struct {
bool has_snapshot;
amduat_reference_t snapshot;
} amduat_asl_store_fs_head_t;
bool amduat_asl_store_fs_init_root(
const char *root_path,
const amduat_asl_store_fs_config_t *cfg_in,
amduat_asl_store_fs_config_t *cfg_out);
bool amduat_asl_store_fs_load_config(
const char *root_path,
amduat_asl_store_fs_config_t *out_cfg);
bool amduat_asl_store_fs_load_head(
const char *root_path,
amduat_asl_store_fs_head_t *out_head);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_ASL_STORE_FS_META_H */

View file

@ -1,5 +1,6 @@
#include "amduat/asl/asl_store_fs.h" #include "amduat/asl/asl_store_fs.h"
#include "asl_store_fs_layout.h"
#include "amduat/asl/core.h" #include "amduat/asl/core.h"
#include "amduat/enc/asl1_core.h" #include "amduat/enc/asl1_core.h"
#include "amduat/enc/asl1_core_codec.h" #include "amduat/enc/asl1_core_codec.h"
@ -36,60 +37,6 @@ typedef enum {
AMDUAT_ASL_STORE_FS_READ_ERR = 2 AMDUAT_ASL_STORE_FS_READ_ERR = 2
} amduat_asl_store_fs_read_status_t; } amduat_asl_store_fs_read_status_t;
static void amduat_asl_store_fs_format_hex(const uint8_t *bytes,
size_t count,
char *out) {
static const char k_hex[] = "0123456789abcdef";
size_t i;
for (i = 0; i < count; ++i) {
const uint8_t value = bytes[i];
out[i * 2u] = k_hex[value >> 4u];
out[i * 2u + 1u] = k_hex[value & 0x0fu];
}
out[count * 2u] = '\0';
}
static bool amduat_asl_store_fs_join_path(const char *base,
const char *segment,
char **out_path) {
size_t base_len;
size_t seg_len;
bool needs_sep;
size_t total_len;
char *buffer;
size_t offset;
if (base == NULL || segment == NULL || out_path == NULL) {
return false;
}
if (base[0] == '\0' || segment[0] == '\0') {
return false;
}
base_len = strlen(base);
seg_len = strlen(segment);
needs_sep = base[base_len - 1u] != '/';
total_len = base_len + (needs_sep ? 1u : 0u) + seg_len + 1u;
buffer = (char *)malloc(total_len);
if (buffer == NULL) {
return false;
}
offset = 0u;
memcpy(buffer + offset, base, base_len);
offset += base_len;
if (needs_sep) {
buffer[offset++] = '/';
}
memcpy(buffer + offset, segment, seg_len);
offset += seg_len;
buffer[offset] = '\0';
*out_path = buffer;
return true;
}
static bool amduat_asl_store_fs_require_directory(const char *path) { static bool amduat_asl_store_fs_require_directory(const char *path) {
struct stat st; struct stat st;
@ -313,97 +260,6 @@ static amduat_asl_store_fs_read_status_t amduat_asl_store_fs_read_file(
return AMDUAT_ASL_STORE_FS_READ_OK; return AMDUAT_ASL_STORE_FS_READ_OK;
} }
static bool amduat_asl_store_fs_build_paths(
const amduat_asl_store_fs_t *fs,
const uint8_t *digest,
size_t digest_len,
char **out_profile_path,
char **out_hash_path,
char **out_level1_path,
char **out_level2_path,
char **out_object_path) {
char profile_hex[5];
char hash_hex[5];
char level1_segment[3];
char level2_segment[3];
char *digest_hex;
char *profile_path;
char *hash_path;
char *level1_path;
char *level2_path;
char *object_path;
bool ok;
if (fs == NULL || digest == NULL || out_profile_path == NULL ||
out_hash_path == NULL || out_level1_path == NULL ||
out_level2_path == NULL || out_object_path == NULL) {
return false;
}
*out_profile_path = NULL;
*out_hash_path = NULL;
*out_level1_path = NULL;
*out_level2_path = NULL;
*out_object_path = NULL;
if (digest_len < AMDUAT_ASL_STORE_FS_MIN_DIGEST_BYTES) {
return false;
}
if (digest_len > (SIZE_MAX - 1u) / 2u) {
return false;
}
snprintf(profile_hex, sizeof(profile_hex), "%04x",
(unsigned int)fs->config.encoding_profile_id);
snprintf(hash_hex, sizeof(hash_hex), "%04x",
(unsigned int)fs->config.hash_id);
amduat_asl_store_fs_format_hex(digest, 1u, level1_segment);
amduat_asl_store_fs_format_hex(digest + 1u, 1u, level2_segment);
digest_hex = (char *)malloc(digest_len * 2u + 1u);
if (digest_hex == NULL) {
return false;
}
amduat_asl_store_fs_format_hex(digest, digest_len, digest_hex);
profile_path = NULL;
hash_path = NULL;
level1_path = NULL;
level2_path = NULL;
object_path = NULL;
ok = amduat_asl_store_fs_join_path(fs->root_path, profile_hex, &profile_path);
if (ok) {
ok = amduat_asl_store_fs_join_path(profile_path, hash_hex, &hash_path);
}
if (ok) {
ok = amduat_asl_store_fs_join_path(hash_path, level1_segment, &level1_path);
}
if (ok) {
ok =
amduat_asl_store_fs_join_path(level1_path, level2_segment, &level2_path);
}
if (ok) {
ok = amduat_asl_store_fs_join_path(level2_path, digest_hex, &object_path);
}
free(digest_hex);
if (!ok) {
free(profile_path);
free(hash_path);
free(level1_path);
free(level2_path);
free(object_path);
return false;
}
*out_profile_path = profile_path;
*out_hash_path = hash_path;
*out_level1_path = level1_path;
*out_level2_path = level2_path;
*out_object_path = object_path;
return true;
}
static amduat_asl_store_error_t amduat_asl_store_fs_compare_existing( static amduat_asl_store_error_t amduat_asl_store_fs_compare_existing(
const char *object_path, const char *object_path,
@ -447,6 +303,7 @@ static amduat_asl_store_error_t amduat_asl_store_fs_put_impl(
amduat_octets_t artifact_bytes; amduat_octets_t artifact_bytes;
uint8_t *digest; uint8_t *digest;
bool ok; bool ok;
char *objects_path;
char *profile_path; char *profile_path;
char *hash_path; char *hash_path;
char *level1_path; char *level1_path;
@ -490,14 +347,17 @@ static amduat_asl_store_error_t amduat_asl_store_fs_put_impl(
return AMDUAT_ASL_STORE_ERR_INTEGRITY; return AMDUAT_ASL_STORE_ERR_INTEGRITY;
} }
objects_path = NULL;
profile_path = NULL; profile_path = NULL;
hash_path = NULL; hash_path = NULL;
level1_path = NULL; level1_path = NULL;
level2_path = NULL; level2_path = NULL;
object_path = NULL; object_path = NULL;
ok = amduat_asl_store_fs_build_paths(fs, ok = amduat_asl_store_fs_layout_build_paths(fs->root_path,
fs->config,
digest, digest,
hash_desc->digest_len, hash_desc->digest_len,
&objects_path,
&profile_path, &profile_path,
&hash_path, &hash_path,
&level1_path, &level1_path,
@ -510,12 +370,14 @@ static amduat_asl_store_error_t amduat_asl_store_fs_put_impl(
} }
if (!amduat_asl_store_fs_require_directory(fs->root_path) || if (!amduat_asl_store_fs_require_directory(fs->root_path) ||
!amduat_asl_store_fs_ensure_directory(objects_path) ||
!amduat_asl_store_fs_ensure_directory(profile_path) || !amduat_asl_store_fs_ensure_directory(profile_path) ||
!amduat_asl_store_fs_ensure_directory(hash_path) || !amduat_asl_store_fs_ensure_directory(hash_path) ||
!amduat_asl_store_fs_ensure_directory(level1_path) || !amduat_asl_store_fs_ensure_directory(level1_path) ||
!amduat_asl_store_fs_ensure_directory(level2_path)) { !amduat_asl_store_fs_ensure_directory(level2_path)) {
free((void *)artifact_bytes.data); free((void *)artifact_bytes.data);
free(digest); free(digest);
free(objects_path);
free(profile_path); free(profile_path);
free(hash_path); free(hash_path);
free(level1_path); free(level1_path);
@ -531,6 +393,7 @@ static amduat_asl_store_error_t amduat_asl_store_fs_put_impl(
out_ref->hash_id = fs->config.hash_id; out_ref->hash_id = fs->config.hash_id;
out_ref->digest = amduat_octets(digest, hash_desc->digest_len); out_ref->digest = amduat_octets(digest, hash_desc->digest_len);
free((void *)artifact_bytes.data); free((void *)artifact_bytes.data);
free(objects_path);
free(profile_path); free(profile_path);
free(hash_path); free(hash_path);
free(level1_path); free(level1_path);
@ -541,6 +404,7 @@ static amduat_asl_store_error_t amduat_asl_store_fs_put_impl(
if (cmp_err == AMDUAT_ASL_STORE_ERR_INTEGRITY) { if (cmp_err == AMDUAT_ASL_STORE_ERR_INTEGRITY) {
free((void *)artifact_bytes.data); free((void *)artifact_bytes.data);
free(digest); free(digest);
free(objects_path);
free(profile_path); free(profile_path);
free(hash_path); free(hash_path);
free(level1_path); free(level1_path);
@ -560,6 +424,7 @@ static amduat_asl_store_error_t amduat_asl_store_fs_put_impl(
if (cmp_err != AMDUAT_ASL_STORE_OK) { if (cmp_err != AMDUAT_ASL_STORE_OK) {
free((void *)artifact_bytes.data); free((void *)artifact_bytes.data);
free(digest); free(digest);
free(objects_path);
free(profile_path); free(profile_path);
free(hash_path); free(hash_path);
free(level1_path); free(level1_path);
@ -570,6 +435,7 @@ static amduat_asl_store_error_t amduat_asl_store_fs_put_impl(
} else if (write_status != AMDUAT_ASL_STORE_FS_WRITE_OK) { } else if (write_status != AMDUAT_ASL_STORE_FS_WRITE_OK) {
free((void *)artifact_bytes.data); free((void *)artifact_bytes.data);
free(digest); free(digest);
free(objects_path);
free(profile_path); free(profile_path);
free(hash_path); free(hash_path);
free(level1_path); free(level1_path);
@ -582,6 +448,7 @@ static amduat_asl_store_error_t amduat_asl_store_fs_put_impl(
unlink(object_path); unlink(object_path);
free((void *)artifact_bytes.data); free((void *)artifact_bytes.data);
free(digest); free(digest);
free(objects_path);
free(profile_path); free(profile_path);
free(hash_path); free(hash_path);
free(level1_path); free(level1_path);
@ -593,6 +460,7 @@ static amduat_asl_store_error_t amduat_asl_store_fs_put_impl(
unlink(object_path); unlink(object_path);
free((void *)artifact_bytes.data); free((void *)artifact_bytes.data);
free(digest); free(digest);
free(objects_path);
free(profile_path); free(profile_path);
free(hash_path); free(hash_path);
free(level1_path); free(level1_path);
@ -604,6 +472,7 @@ static amduat_asl_store_error_t amduat_asl_store_fs_put_impl(
out_ref->hash_id = fs->config.hash_id; out_ref->hash_id = fs->config.hash_id;
out_ref->digest = amduat_octets(digest, hash_desc->digest_len); out_ref->digest = amduat_octets(digest, hash_desc->digest_len);
free((void *)artifact_bytes.data); free((void *)artifact_bytes.data);
free(objects_path);
free(profile_path); free(profile_path);
free(hash_path); free(hash_path);
free(level1_path); free(level1_path);
@ -618,6 +487,7 @@ static amduat_asl_store_error_t amduat_asl_store_fs_get_impl(
amduat_artifact_t *out_artifact) { amduat_artifact_t *out_artifact) {
amduat_asl_store_fs_t *fs; amduat_asl_store_fs_t *fs;
const amduat_hash_asl1_desc_t *hash_desc; const amduat_hash_asl1_desc_t *hash_desc;
char *objects_path;
char *profile_path; char *profile_path;
char *hash_path; char *hash_path;
char *level1_path; char *level1_path;
@ -654,14 +524,17 @@ static amduat_asl_store_error_t amduat_asl_store_fs_get_impl(
return AMDUAT_ASL_STORE_ERR_UNSUPPORTED; return AMDUAT_ASL_STORE_ERR_UNSUPPORTED;
} }
objects_path = NULL;
profile_path = NULL; profile_path = NULL;
hash_path = NULL; hash_path = NULL;
level1_path = NULL; level1_path = NULL;
level2_path = NULL; level2_path = NULL;
object_path = NULL; object_path = NULL;
if (!amduat_asl_store_fs_build_paths(fs, if (!amduat_asl_store_fs_layout_build_paths(fs->root_path,
fs->config,
ref.digest.data, ref.digest.data,
ref.digest.len, ref.digest.len,
&objects_path,
&profile_path, &profile_path,
&hash_path, &hash_path,
&level1_path, &level1_path,
@ -675,6 +548,7 @@ static amduat_asl_store_error_t amduat_asl_store_fs_get_impl(
read_status = read_status =
amduat_asl_store_fs_read_file(object_path, &stored_bytes, &stored_len); amduat_asl_store_fs_read_file(object_path, &stored_bytes, &stored_len);
if (read_status == AMDUAT_ASL_STORE_FS_READ_NOT_FOUND) { if (read_status == AMDUAT_ASL_STORE_FS_READ_NOT_FOUND) {
free(objects_path);
free(profile_path); free(profile_path);
free(hash_path); free(hash_path);
free(level1_path); free(level1_path);
@ -683,6 +557,7 @@ static amduat_asl_store_error_t amduat_asl_store_fs_get_impl(
return AMDUAT_ASL_STORE_ERR_NOT_FOUND; return AMDUAT_ASL_STORE_ERR_NOT_FOUND;
} }
if (read_status != AMDUAT_ASL_STORE_FS_READ_OK) { if (read_status != AMDUAT_ASL_STORE_FS_READ_OK) {
free(objects_path);
free(profile_path); free(profile_path);
free(hash_path); free(hash_path);
free(level1_path); free(level1_path);
@ -694,6 +569,7 @@ static amduat_asl_store_error_t amduat_asl_store_fs_get_impl(
computed_digest = (uint8_t *)malloc(hash_desc->digest_len); computed_digest = (uint8_t *)malloc(hash_desc->digest_len);
if (computed_digest == NULL) { if (computed_digest == NULL) {
free(stored_bytes); free(stored_bytes);
free(objects_path);
free(profile_path); free(profile_path);
free(hash_path); free(hash_path);
free(level1_path); free(level1_path);
@ -709,6 +585,7 @@ static amduat_asl_store_error_t amduat_asl_store_fs_get_impl(
hash_desc->digest_len)) { hash_desc->digest_len)) {
free(computed_digest); free(computed_digest);
free(stored_bytes); free(stored_bytes);
free(objects_path);
free(profile_path); free(profile_path);
free(hash_path); free(hash_path);
free(level1_path); free(level1_path);
@ -720,6 +597,7 @@ static amduat_asl_store_error_t amduat_asl_store_fs_get_impl(
if (memcmp(computed_digest, ref.digest.data, hash_desc->digest_len) != 0) { if (memcmp(computed_digest, ref.digest.data, hash_desc->digest_len) != 0) {
free(computed_digest); free(computed_digest);
free(stored_bytes); free(stored_bytes);
free(objects_path);
free(profile_path); free(profile_path);
free(hash_path); free(hash_path);
free(level1_path); free(level1_path);
@ -733,6 +611,7 @@ static amduat_asl_store_error_t amduat_asl_store_fs_get_impl(
amduat_octets(stored_bytes, stored_len), amduat_octets(stored_bytes, stored_len),
out_artifact); out_artifact);
free(stored_bytes); free(stored_bytes);
free(objects_path);
free(profile_path); free(profile_path);
free(hash_path); free(hash_path);
free(level1_path); free(level1_path);

View file

@ -0,0 +1,193 @@
#include "asl_store_fs_layout.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { AMDUAT_ASL_STORE_FS_LAYOUT_MIN_DIGEST_BYTES = 2 };
static void amduat_asl_store_fs_layout_format_hex(const uint8_t *bytes,
size_t count,
char *out) {
static const char k_hex[] = "0123456789abcdef";
size_t i;
for (i = 0; i < count; ++i) {
const uint8_t value = bytes[i];
out[i * 2u] = k_hex[value >> 4u];
out[i * 2u + 1u] = k_hex[value & 0x0fu];
}
out[count * 2u] = '\0';
}
static bool amduat_asl_store_fs_layout_join_path(const char *base,
const char *segment,
char **out_path) {
size_t base_len;
size_t seg_len;
bool needs_sep;
size_t total_len;
char *buffer;
size_t offset;
if (base == NULL || segment == NULL || out_path == NULL) {
return false;
}
if (base[0] == '\0' || segment[0] == '\0') {
return false;
}
base_len = strlen(base);
seg_len = strlen(segment);
needs_sep = base[base_len - 1u] != '/';
total_len = base_len + (needs_sep ? 1u : 0u) + seg_len + 1u;
buffer = (char *)malloc(total_len);
if (buffer == NULL) {
return false;
}
offset = 0u;
memcpy(buffer + offset, base, base_len);
offset += base_len;
if (needs_sep) {
buffer[offset++] = '/';
}
memcpy(buffer + offset, segment, seg_len);
offset += seg_len;
buffer[offset] = '\0';
*out_path = buffer;
return true;
}
bool amduat_asl_store_fs_layout_build_config_path(const char *root_path,
char **out_path) {
return amduat_asl_store_fs_layout_join_path(root_path, "CONFIG", out_path);
}
bool amduat_asl_store_fs_layout_build_head_path(const char *root_path,
char **out_path) {
return amduat_asl_store_fs_layout_join_path(root_path, "HEAD", out_path);
}
bool amduat_asl_store_fs_layout_build_objects_path(const char *root_path,
char **out_path) {
return amduat_asl_store_fs_layout_join_path(root_path, "objects", out_path);
}
bool amduat_asl_store_fs_layout_build_paths(
const char *root_path,
amduat_asl_store_config_t config,
const uint8_t *digest,
size_t digest_len,
char **out_objects_path,
char **out_profile_path,
char **out_hash_path,
char **out_level1_path,
char **out_level2_path,
char **out_object_path) {
char profile_hex[5];
char hash_hex[5];
char level1_segment[3];
char level2_segment[3];
char *digest_hex;
char *objects_path;
char *profile_path;
char *hash_path;
char *level1_path;
char *level2_path;
char *object_path;
bool ok;
if (root_path == NULL || digest == NULL || out_objects_path == NULL ||
out_profile_path == NULL || out_hash_path == NULL ||
out_level1_path == NULL || out_level2_path == NULL ||
out_object_path == NULL) {
return false;
}
*out_objects_path = NULL;
*out_profile_path = NULL;
*out_hash_path = NULL;
*out_level1_path = NULL;
*out_level2_path = NULL;
*out_object_path = NULL;
if (root_path[0] == '\0') {
return false;
}
if (digest_len < AMDUAT_ASL_STORE_FS_LAYOUT_MIN_DIGEST_BYTES) {
return false;
}
if (digest_len > (SIZE_MAX - 1u) / 2u) {
return false;
}
snprintf(profile_hex, sizeof(profile_hex), "%04x",
(unsigned int)config.encoding_profile_id);
snprintf(hash_hex, sizeof(hash_hex), "%04x", (unsigned int)config.hash_id);
amduat_asl_store_fs_layout_format_hex(digest, 1u, level1_segment);
amduat_asl_store_fs_layout_format_hex(digest + 1u, 1u, level2_segment);
digest_hex = (char *)malloc(digest_len * 2u + 1u);
if (digest_hex == NULL) {
return false;
}
amduat_asl_store_fs_layout_format_hex(digest, digest_len, digest_hex);
objects_path = NULL;
profile_path = NULL;
hash_path = NULL;
level1_path = NULL;
level2_path = NULL;
object_path = NULL;
ok = amduat_asl_store_fs_layout_build_objects_path(root_path, &objects_path);
if (ok) {
ok = amduat_asl_store_fs_layout_join_path(objects_path,
profile_hex,
&profile_path);
}
if (ok) {
ok = amduat_asl_store_fs_layout_join_path(profile_path,
hash_hex,
&hash_path);
}
if (ok) {
ok = amduat_asl_store_fs_layout_join_path(hash_path,
level1_segment,
&level1_path);
}
if (ok) {
ok = amduat_asl_store_fs_layout_join_path(level1_path,
level2_segment,
&level2_path);
}
if (ok) {
ok = amduat_asl_store_fs_layout_join_path(level2_path,
digest_hex,
&object_path);
}
free(digest_hex);
if (!ok) {
free(objects_path);
free(profile_path);
free(hash_path);
free(level1_path);
free(level2_path);
free(object_path);
return false;
}
*out_objects_path = objects_path;
*out_profile_path = profile_path;
*out_hash_path = hash_path;
*out_level1_path = level1_path;
*out_level2_path = level2_path;
*out_object_path = object_path;
return true;
}

View file

@ -0,0 +1,39 @@
#ifndef AMDUAT_ASL_STORE_FS_LAYOUT_H
#define AMDUAT_ASL_STORE_FS_LAYOUT_H
#include "amduat/asl/store.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
bool amduat_asl_store_fs_layout_build_paths(
const char *root_path,
amduat_asl_store_config_t config,
const uint8_t *digest,
size_t digest_len,
char **out_objects_path,
char **out_profile_path,
char **out_hash_path,
char **out_level1_path,
char **out_level2_path,
char **out_object_path);
bool amduat_asl_store_fs_layout_build_config_path(const char *root_path,
char **out_path);
bool amduat_asl_store_fs_layout_build_head_path(const char *root_path,
char **out_path);
bool amduat_asl_store_fs_layout_build_objects_path(const char *root_path,
char **out_path);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_ASL_STORE_FS_LAYOUT_H */

View file

@ -0,0 +1,723 @@
#include "amduat/asl/asl_store_fs_meta.h"
#include "asl_store_fs_layout.h"
#include "amduat/enc/asl1_core.h"
#include "amduat/hash/asl1.h"
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#ifndef O_DIRECTORY
#define O_DIRECTORY 0
#endif
typedef enum {
AMDUAT_ASL_STORE_FS_META_READ_OK = 0,
AMDUAT_ASL_STORE_FS_META_READ_NOT_FOUND = 1,
AMDUAT_ASL_STORE_FS_META_READ_ERR = 2
} amduat_asl_store_fs_meta_read_status_t;
static bool amduat_asl_store_fs_meta_is_store_id_char(char c) {
if (c >= 'a' && c <= 'z') {
return true;
}
if (c >= 'A' && c <= 'Z') {
return true;
}
if (c >= '0' && c <= '9') {
return true;
}
return c == '.' || c == '_' || c == '-';
}
static bool amduat_asl_store_fs_meta_validate_store_id(const char *store_id) {
size_t i;
if (store_id == NULL || store_id[0] == '\0') {
return false;
}
for (i = 0; store_id[i] != '\0'; ++i) {
if (i >= AMDUAT_ASL_STORE_FS_STORE_ID_MAX) {
return false;
}
if (!amduat_asl_store_fs_meta_is_store_id_char(store_id[i])) {
return false;
}
}
return true;
}
static bool amduat_asl_store_fs_meta_copy_store_id(
char out_id[AMDUAT_ASL_STORE_FS_STORE_ID_MAX + 1],
const char *store_id) {
size_t len;
if (out_id == NULL || store_id == NULL) {
return false;
}
if (!amduat_asl_store_fs_meta_validate_store_id(store_id)) {
return false;
}
len = strlen(store_id);
if (len > AMDUAT_ASL_STORE_FS_STORE_ID_MAX) {
return false;
}
memcpy(out_id, store_id, len);
out_id[len] = '\0';
return true;
}
static bool amduat_asl_store_fs_meta_parse_hex4_lower(const char *value,
uint16_t *out) {
size_t i;
uint16_t accum;
if (value == NULL || out == NULL) {
return false;
}
if (strlen(value) != 4u) {
return false;
}
accum = 0u;
for (i = 0; i < 4u; ++i) {
char c = value[i];
uint8_t nibble;
if (c >= '0' && c <= '9') {
nibble = (uint8_t)(c - '0');
} else if (c >= 'a' && c <= 'f') {
nibble = (uint8_t)(c - 'a' + 10);
} else {
return false;
}
accum = (uint16_t)((accum << 4u) | nibble);
}
*out = accum;
return true;
}
static bool amduat_asl_store_fs_meta_ensure_directory(const char *path) {
struct stat st;
if (path == NULL || path[0] == '\0') {
return false;
}
if (stat(path, &st) == 0) {
return S_ISDIR(st.st_mode);
}
if (errno != ENOENT) {
return false;
}
if (mkdir(path, 0755) != 0) {
if (errno == EEXIST) {
return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
}
return false;
}
return true;
}
static bool amduat_asl_store_fs_meta_fsync_directory(const char *path) {
int fd;
int fsync_errno;
if (path == NULL || path[0] == '\0') {
return false;
}
fd = open(path, O_RDONLY | O_DIRECTORY);
if (fd < 0) {
return false;
}
while (fsync(fd) != 0) {
if (errno == EINTR) {
continue;
}
fsync_errno = errno;
close(fd);
errno = fsync_errno;
return false;
}
if (close(fd) != 0) {
return false;
}
return true;
}
static amduat_asl_store_fs_meta_read_status_t
amduat_asl_store_fs_meta_read_file(const char *path,
char **out_bytes,
size_t *out_size) {
struct stat st;
size_t file_size;
char *buffer;
size_t total_read;
int fd;
if (path == NULL || out_bytes == NULL || out_size == NULL) {
return AMDUAT_ASL_STORE_FS_META_READ_ERR;
}
*out_bytes = NULL;
*out_size = 0;
if (stat(path, &st) != 0) {
if (errno == ENOENT || errno == ENOTDIR) {
return AMDUAT_ASL_STORE_FS_META_READ_NOT_FOUND;
}
return AMDUAT_ASL_STORE_FS_META_READ_ERR;
}
if (!S_ISREG(st.st_mode)) {
return AMDUAT_ASL_STORE_FS_META_READ_ERR;
}
if (st.st_size <= 0) {
return AMDUAT_ASL_STORE_FS_META_READ_ERR;
}
if ((uintmax_t)st.st_size > SIZE_MAX) {
return AMDUAT_ASL_STORE_FS_META_READ_ERR;
}
file_size = (size_t)st.st_size;
buffer = (char *)malloc(file_size + 1u);
if (buffer == NULL) {
return AMDUAT_ASL_STORE_FS_META_READ_ERR;
}
fd = open(path, O_RDONLY);
if (fd < 0) {
free(buffer);
if (errno == ENOENT || errno == ENOTDIR) {
return AMDUAT_ASL_STORE_FS_META_READ_NOT_FOUND;
}
return AMDUAT_ASL_STORE_FS_META_READ_ERR;
}
total_read = 0u;
while (total_read < file_size) {
ssize_t rc = read(fd, buffer + total_read, file_size - total_read);
if (rc < 0) {
if (errno == EINTR) {
continue;
}
close(fd);
free(buffer);
return AMDUAT_ASL_STORE_FS_META_READ_ERR;
}
if (rc == 0) {
close(fd);
free(buffer);
return AMDUAT_ASL_STORE_FS_META_READ_ERR;
}
total_read += (size_t)rc;
}
if (close(fd) != 0) {
free(buffer);
return AMDUAT_ASL_STORE_FS_META_READ_ERR;
}
buffer[file_size] = '\0';
*out_bytes = buffer;
*out_size = file_size;
return AMDUAT_ASL_STORE_FS_META_READ_OK;
}
static bool amduat_asl_store_fs_meta_write_atomic(const char *dir_path,
const char *final_path,
const uint8_t *bytes,
size_t size) {
static const char suffix[] = "tmp.XXXXXX";
size_t dir_len;
bool need_sep;
size_t template_len;
char *template_path;
int temp_fd;
size_t written;
if (dir_path == NULL || final_path == NULL) {
return false;
}
if (size != 0u && bytes == NULL) {
return false;
}
dir_len = strlen(dir_path);
if (dir_len == 0u) {
return false;
}
need_sep = dir_path[dir_len - 1u] != '/';
template_len = dir_len + (need_sep ? 1u : 0u) + sizeof(suffix);
template_path = (char *)malloc(template_len);
if (template_path == NULL) {
return false;
}
if (need_sep) {
snprintf(template_path, template_len, "%s/%s", dir_path, suffix);
} else {
snprintf(template_path, template_len, "%s%s", dir_path, suffix);
}
temp_fd = mkstemp(template_path);
if (temp_fd < 0) {
free(template_path);
return false;
}
written = 0u;
while (written < size) {
ssize_t rc = write(temp_fd, bytes + written, size - written);
if (rc < 0) {
if (errno == EINTR) {
continue;
}
close(temp_fd);
unlink(template_path);
free(template_path);
return false;
}
written += (size_t)rc;
}
if (fsync(temp_fd) != 0) {
close(temp_fd);
unlink(template_path);
free(template_path);
return false;
}
if (close(temp_fd) != 0) {
unlink(template_path);
free(template_path);
return false;
}
if (rename(template_path, final_path) != 0) {
unlink(template_path);
free(template_path);
return false;
}
free(template_path);
return amduat_asl_store_fs_meta_fsync_directory(dir_path);
}
static bool amduat_asl_store_fs_meta_random_bytes(uint8_t *out, size_t len) {
size_t read_total;
int fd;
if (out == NULL || len == 0u) {
return false;
}
fd = open("/dev/urandom", O_RDONLY);
if (fd >= 0) {
read_total = 0u;
while (read_total < len) {
ssize_t rc = read(fd, out + read_total, len - read_total);
if (rc < 0) {
if (errno == EINTR) {
continue;
}
close(fd);
fd = -1;
break;
}
if (rc == 0) {
close(fd);
fd = -1;
break;
}
read_total += (size_t)rc;
}
if (fd >= 0) {
close(fd);
return true;
}
}
{
unsigned int seed;
size_t i;
seed = (unsigned int)time(NULL);
seed ^= (unsigned int)getpid();
seed ^= (unsigned int)(uintptr_t)out;
srand(seed);
for (i = 0; i < len; ++i) {
out[i] = (uint8_t)(rand() & 0xff);
}
}
return true;
}
static char amduat_asl_store_fs_meta_hex_lower(uint8_t nibble) {
static const char k_hex[] = "0123456789abcdef";
return k_hex[nibble & 0x0fu];
}
static bool amduat_asl_store_fs_meta_generate_uuid(
char out_id[AMDUAT_ASL_STORE_FS_STORE_ID_MAX + 1]) {
uint8_t bytes[16];
char *out;
size_t i;
if (out_id == NULL) {
return false;
}
if (!amduat_asl_store_fs_meta_random_bytes(bytes, sizeof(bytes))) {
return false;
}
bytes[6] = (uint8_t)((bytes[6] & 0x0fu) | 0x40u);
bytes[8] = (uint8_t)((bytes[8] & 0x3fu) | 0x80u);
out = out_id;
for (i = 0; i < sizeof(bytes); ++i) {
if (i == 4u || i == 6u || i == 8u || i == 10u) {
*out++ = '-';
}
*out++ = amduat_asl_store_fs_meta_hex_lower((uint8_t)(bytes[i] >> 4u));
*out++ = amduat_asl_store_fs_meta_hex_lower(bytes[i]);
}
*out = '\0';
return amduat_asl_store_fs_meta_validate_store_id(out_id);
}
static bool amduat_asl_store_fs_meta_parse_config_text(
char *text,
amduat_asl_store_fs_config_t *out_cfg) {
char *saveptr;
char *token;
bool got_store_id;
bool got_profile;
bool got_hash;
if (text == NULL || out_cfg == NULL) {
return false;
}
memset(out_cfg, 0, sizeof(*out_cfg));
saveptr = NULL;
token = strtok_r(text, " \t\r\n", &saveptr);
if (token == NULL || strcmp(token, "amduat-asl-config-v1") != 0) {
return false;
}
got_store_id = false;
got_profile = false;
got_hash = false;
while ((token = strtok_r(NULL, " \t\r\n", &saveptr)) != NULL) {
if (strncmp(token, "store_id=", 9u) == 0) {
const char *value = token + 9u;
if (!amduat_asl_store_fs_meta_copy_store_id(out_cfg->store_id, value)) {
return false;
}
got_store_id = true;
continue;
}
if (strncmp(token, "profile=", 8u) == 0) {
const char *value = token + 8u;
uint16_t profile_id;
if (!amduat_asl_store_fs_meta_parse_hex4_lower(value, &profile_id)) {
return false;
}
out_cfg->config.encoding_profile_id = profile_id;
got_profile = true;
continue;
}
if (strncmp(token, "hash=", 5u) == 0) {
const char *value = token + 5u;
uint16_t hash_id;
if (!amduat_asl_store_fs_meta_parse_hex4_lower(value, &hash_id)) {
return false;
}
out_cfg->config.hash_id = hash_id;
got_hash = true;
continue;
}
}
return got_store_id && got_profile && got_hash;
}
static bool amduat_asl_store_fs_meta_parse_head_text(
char *text,
amduat_asl_store_fs_head_t *out_head) {
char *saveptr;
char *token;
bool got_snapshot;
if (text == NULL || out_head == NULL) {
return false;
}
memset(out_head, 0, sizeof(*out_head));
saveptr = NULL;
token = strtok_r(text, " \t\r\n", &saveptr);
if (token == NULL || strcmp(token, "amduat-asl-head-v1") != 0) {
return false;
}
got_snapshot = false;
while ((token = strtok_r(NULL, " \t\r\n", &saveptr)) != NULL) {
if (strncmp(token, "snapshot=", 9u) == 0) {
const char *value = token + 9u;
if (strcmp(value, "none") != 0) {
return false;
}
out_head->has_snapshot = false;
got_snapshot = true;
}
}
return got_snapshot;
}
static bool amduat_asl_store_fs_meta_write_config(
const char *root_path,
const amduat_asl_store_fs_config_t *cfg) {
char *config_path;
int needed;
size_t len;
char *buffer;
bool ok;
if (root_path == NULL || cfg == NULL) {
return false;
}
config_path = NULL;
if (!amduat_asl_store_fs_layout_build_config_path(root_path, &config_path)) {
return false;
}
needed = snprintf(NULL,
0,
"amduat-asl-config-v1\nstore_id=%s\nprofile=%04x\nhash=%04x\n",
cfg->store_id,
(unsigned int)cfg->config.encoding_profile_id,
(unsigned int)cfg->config.hash_id);
if (needed < 0) {
free(config_path);
return false;
}
len = (size_t)needed;
buffer = (char *)malloc(len + 1u);
if (buffer == NULL) {
free(config_path);
return false;
}
snprintf(buffer,
len + 1u,
"amduat-asl-config-v1\nstore_id=%s\nprofile=%04x\nhash=%04x\n",
cfg->store_id,
(unsigned int)cfg->config.encoding_profile_id,
(unsigned int)cfg->config.hash_id);
ok = amduat_asl_store_fs_meta_write_atomic(root_path,
config_path,
(const uint8_t *)buffer,
len);
free(buffer);
free(config_path);
return ok;
}
bool amduat_asl_store_fs_init_root(
const char *root_path,
const amduat_asl_store_fs_config_t *cfg_in,
amduat_asl_store_fs_config_t *cfg_out) {
char *objects_path;
char *config_path;
amduat_asl_store_fs_meta_read_status_t read_status;
char *config_text;
size_t config_size;
amduat_asl_store_fs_config_t loaded_cfg;
amduat_asl_store_fs_config_t new_cfg;
if (root_path == NULL || root_path[0] == '\0' || cfg_out == NULL) {
return false;
}
if (!amduat_asl_store_fs_meta_ensure_directory(root_path)) {
return false;
}
objects_path = NULL;
if (!amduat_asl_store_fs_layout_build_objects_path(root_path,
&objects_path)) {
return false;
}
if (!amduat_asl_store_fs_meta_ensure_directory(objects_path)) {
free(objects_path);
return false;
}
free(objects_path);
config_path = NULL;
if (!amduat_asl_store_fs_layout_build_config_path(root_path,
&config_path)) {
return false;
}
config_text = NULL;
config_size = 0u;
read_status =
amduat_asl_store_fs_meta_read_file(config_path,
&config_text,
&config_size);
(void)config_size;
if (read_status == AMDUAT_ASL_STORE_FS_META_READ_OK) {
bool ok = amduat_asl_store_fs_meta_parse_config_text(config_text,
&loaded_cfg);
free(config_text);
free(config_path);
if (!ok) {
return false;
}
if (cfg_in != NULL) {
if (cfg_in->store_id[0] != '\0') {
if (!amduat_asl_store_fs_meta_validate_store_id(cfg_in->store_id)) {
return false;
}
if (strcmp(cfg_in->store_id, loaded_cfg.store_id) != 0) {
return false;
}
}
if (cfg_in->config.encoding_profile_id != 0 &&
cfg_in->config.encoding_profile_id !=
loaded_cfg.config.encoding_profile_id) {
return false;
}
if (cfg_in->config.hash_id != 0 &&
cfg_in->config.hash_id != loaded_cfg.config.hash_id) {
return false;
}
}
*cfg_out = loaded_cfg;
return true;
}
if (read_status != AMDUAT_ASL_STORE_FS_META_READ_NOT_FOUND) {
free(config_path);
return false;
}
free(config_path);
memset(&new_cfg, 0, sizeof(new_cfg));
new_cfg.config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
new_cfg.config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (cfg_in != NULL) {
if (cfg_in->config.encoding_profile_id != 0) {
new_cfg.config.encoding_profile_id =
cfg_in->config.encoding_profile_id;
}
if (cfg_in->config.hash_id != 0) {
new_cfg.config.hash_id = cfg_in->config.hash_id;
}
if (cfg_in->store_id[0] != '\0') {
if (!amduat_asl_store_fs_meta_copy_store_id(new_cfg.store_id,
cfg_in->store_id)) {
return false;
}
}
}
if (new_cfg.store_id[0] == '\0') {
if (!amduat_asl_store_fs_meta_generate_uuid(new_cfg.store_id)) {
return false;
}
}
if (!amduat_asl_store_fs_meta_write_config(root_path, &new_cfg)) {
return false;
}
*cfg_out = new_cfg;
return true;
}
bool amduat_asl_store_fs_load_config(
const char *root_path,
amduat_asl_store_fs_config_t *out_cfg) {
char *config_path;
amduat_asl_store_fs_meta_read_status_t read_status;
char *config_text;
size_t config_size;
bool ok;
if (root_path == NULL || root_path[0] == '\0' || out_cfg == NULL) {
return false;
}
config_path = NULL;
if (!amduat_asl_store_fs_layout_build_config_path(root_path, &config_path)) {
return false;
}
config_text = NULL;
config_size = 0u;
read_status =
amduat_asl_store_fs_meta_read_file(config_path,
&config_text,
&config_size);
(void)config_size;
free(config_path);
if (read_status != AMDUAT_ASL_STORE_FS_META_READ_OK) {
return false;
}
ok = amduat_asl_store_fs_meta_parse_config_text(config_text, out_cfg);
free(config_text);
return ok;
}
bool amduat_asl_store_fs_load_head(
const char *root_path,
amduat_asl_store_fs_head_t *out_head) {
char *head_path;
amduat_asl_store_fs_meta_read_status_t read_status;
char *head_text;
size_t head_size;
bool ok;
if (root_path == NULL || root_path[0] == '\0' || out_head == NULL) {
return false;
}
head_path = NULL;
if (!amduat_asl_store_fs_layout_build_head_path(root_path, &head_path)) {
return false;
}
head_text = NULL;
head_size = 0u;
read_status =
amduat_asl_store_fs_meta_read_file(head_path, &head_text, &head_size);
(void)head_size;
free(head_path);
if (read_status == AMDUAT_ASL_STORE_FS_META_READ_NOT_FOUND) {
memset(out_head, 0, sizeof(*out_head));
out_head->has_snapshot = false;
return true;
}
if (read_status != AMDUAT_ASL_STORE_FS_META_READ_OK) {
return false;
}
ok = amduat_asl_store_fs_meta_parse_head_text(head_text, out_head);
free(head_text);
return ok;
}