#include "amduat/asl/asl_store_index_fs.h" #include "amduat/asl/index_replay.h" #include "amduat/asl/ref_derive.h" #include "amduat/asl/store.h" #include "amduat/enc/asl_core_index.h" #include "amduat/enc/asl_log.h" #include "amduat/enc/asl1_core.h" #include "amduat/hash/asl1.h" #include "asl_store_index_fs_layout.h" #include #include #include #include #include #include #include #include #include #include static void store_u16_le(uint8_t *out, uint16_t value) { out[0] = (uint8_t)(value & 0xffu); out[1] = (uint8_t)((value >> 8) & 0xffu); } static void store_u32_le(uint8_t *out, uint32_t value) { out[0] = (uint8_t)(value & 0xffu); out[1] = (uint8_t)((value >> 8) & 0xffu); out[2] = (uint8_t)((value >> 16) & 0xffu); out[3] = (uint8_t)((value >> 24) & 0xffu); } static void store_u64_le(uint8_t *out, uint64_t value) { out[0] = (uint8_t)(value & 0xffu); out[1] = (uint8_t)((value >> 8) & 0xffu); out[2] = (uint8_t)((value >> 16) & 0xffu); out[3] = (uint8_t)((value >> 24) & 0xffu); out[4] = (uint8_t)((value >> 32) & 0xffu); out[5] = (uint8_t)((value >> 40) & 0xffu); out[6] = (uint8_t)((value >> 48) & 0xffu); out[7] = (uint8_t)((value >> 56) & 0xffu); } static size_t build_artifact_ref(uint8_t *out, size_t out_cap, amduat_hash_id_t hash_id, const uint8_t *digest, size_t digest_len) { size_t total = 4 + 2 + 2 + digest_len; if (out_cap < total) { return 0; } store_u32_le(out, (uint32_t)hash_id); store_u16_le(out + 4, (uint16_t)digest_len); store_u16_le(out + 6, 0); memcpy(out + 8, digest, digest_len); return total; } static size_t build_tombstone_payload(uint8_t *out, size_t out_cap, amduat_hash_id_t hash_id, const uint8_t *digest, size_t digest_len) { size_t offset = 0; size_t ref_len = build_artifact_ref(out, out_cap, hash_id, digest, digest_len); if (ref_len == 0 || out_cap < ref_len + 8) { return 0; } offset += ref_len; store_u32_le(out + offset, 0); offset += 4; store_u32_le(out + offset, 0); offset += 4; return offset; } static size_t build_tombstone_lift_payload(uint8_t *out, size_t out_cap, amduat_hash_id_t hash_id, const uint8_t *digest, size_t digest_len, uint64_t tombstone_logseq) { size_t offset = 0; size_t ref_len = build_artifact_ref(out, out_cap, hash_id, digest, digest_len); if (ref_len == 0 || out_cap < ref_len + 8) { return 0; } offset += ref_len; store_u64_le(out + offset, tombstone_logseq); offset += 8; return offset; } static size_t build_segment_seal_payload(uint8_t *out, size_t out_cap, uint64_t segment_id, const uint8_t hash[32]) { if (out_cap < 8 + 32) { return 0; } store_u64_le(out, segment_id); memcpy(out + 8, hash, 32); return 8 + 32; } static bool 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 remove_tree(const char *path) { struct stat st; DIR *dir; struct dirent *entry; if (path == NULL) { return false; } if (lstat(path, &st) != 0) { return errno == ENOENT; } if (!S_ISDIR(st.st_mode)) { return unlink(path) == 0; } dir = opendir(path); if (dir == NULL) { return false; } while ((entry = readdir(dir)) != NULL) { char *child = NULL; if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } if (!join_path(path, entry->d_name, &child)) { closedir(dir); return false; } if (!remove_tree(child)) { free(child); closedir(dir); return false; } free(child); } if (closedir(dir) != 0) { return false; } return rmdir(path) == 0; } static char *make_temp_root(void) { char *templ; const char template_prefix[] = "/tmp/amduat_test_asl_index_replay_XXXXXX"; templ = (char *)malloc(sizeof(template_prefix)); if (templ == NULL) { return NULL; } memcpy(templ, template_prefix, sizeof(template_prefix)); if (mkdtemp(templ) == NULL) { free(templ); return NULL; } return templ; } static bool ensure_dir(const char *path) { if (mkdir(path, 0700) == 0) { return true; } return errno == EEXIST; } static bool write_file(const char *path, const uint8_t *data, size_t len) { FILE *file; size_t written; file = fopen(path, "wb"); if (file == NULL) { return false; } written = 0u; if (len != 0) { written = fwrite(data, 1u, len, file); } if (written != len) { fclose(file); return false; } return fclose(file) == 0; } static bool read_file(const char *path, uint8_t **out_bytes, size_t *out_len) { FILE *file; long size; uint8_t *buffer; if (out_bytes == NULL || out_len == NULL) { return false; } *out_bytes = NULL; *out_len = 0u; file = fopen(path, "rb"); if (file == NULL) { return false; } if (fseek(file, 0, SEEK_END) != 0) { fclose(file); return false; } size = ftell(file); if (size < 0) { fclose(file); return false; } if (fseek(file, 0, SEEK_SET) != 0) { fclose(file); return false; } buffer = (uint8_t *)malloc((size_t)size); if (buffer == NULL) { fclose(file); return false; } if (size != 0 && fread(buffer, 1u, (size_t)size, file) != (size_t)size) { free(buffer); fclose(file); return false; } if (fclose(file) != 0) { free(buffer); return false; } *out_bytes = buffer; *out_len = (size_t)size; return true; } static bool prepare_index_tree(const char *root) { char *index_path = NULL; char *segments_path = NULL; char *blocks_path = NULL; bool ok = false; if (!amduat_asl_store_index_fs_layout_build_index_path(root, &index_path) || !amduat_asl_store_index_fs_layout_build_segments_path(root, &segments_path) || !amduat_asl_store_index_fs_layout_build_blocks_path(root, &blocks_path)) { goto cleanup; } if (!ensure_dir(index_path) || !ensure_dir(segments_path) || !ensure_dir(blocks_path)) { goto cleanup; } ok = true; cleanup: free(index_path); free(segments_path); free(blocks_path); return ok; } static bool write_log(const char *root, amduat_asl_log_record_t *records, size_t record_count) { char *log_path = NULL; amduat_octets_t bytes; bool ok = false; if (!amduat_enc_asl_log_encode_v1(records, record_count, &bytes)) { return false; } if (!amduat_asl_store_index_fs_layout_build_log_path(root, &log_path)) { free((void *)bytes.data); return false; } ok = write_file(log_path, bytes.data, bytes.len); free((void *)bytes.data); free(log_path); return ok; } static bool load_log_records(const char *root, amduat_asl_log_record_t **out_records, size_t *out_count) { char *log_path = NULL; uint8_t *bytes = NULL; size_t len = 0; bool ok = false; if (!amduat_asl_store_index_fs_layout_build_log_path(root, &log_path)) { return false; } if (!read_file(log_path, &bytes, &len)) { free(log_path); return false; } ok = amduat_enc_asl_log_decode_v1(amduat_octets(bytes, len), out_records, out_count); free(bytes); free(log_path); return ok; } static bool write_block_file(const char *root, uint64_t block_id, amduat_octets_t bytes) { char *block_path = NULL; bool ok = false; if (!amduat_asl_store_index_fs_layout_build_block_path(root, block_id, &block_path)) { return false; } ok = write_file(block_path, bytes.data, bytes.len); free(block_path); return ok; } static bool write_segment_file(const char *root, uint64_t segment_id, const amduat_asl_core_index_segment_t *segment, uint8_t out_hash[32]) { char *segment_path = NULL; amduat_octets_t encoded; bool ok = false; if (!amduat_enc_asl_core_index_encode_v1(segment, &encoded)) { return false; } if (!amduat_hash_asl1_digest(AMDUAT_HASH_ASL1_ID_SHA256, encoded, out_hash, 32)) { free((void *)encoded.data); return false; } if (!amduat_asl_store_index_fs_layout_build_segment_path(root, segment_id, &segment_path)) { free((void *)encoded.data); return false; } ok = write_file(segment_path, encoded.data, encoded.len); free((void *)encoded.data); free(segment_path); return ok; } static bool build_segment_for_artifact( amduat_asl_core_index_segment_t *segment, amduat_asl_index_record_t *record, amduat_asl_extent_record_t *extent, amduat_reference_t ref, amduat_octets_t artifact_bytes, uint64_t snapshot_id, uint64_t block_id) { if (segment == NULL || record == NULL || extent == NULL) { return false; } memset(segment, 0, sizeof(*segment)); memset(record, 0, sizeof(*record)); memset(extent, 0, sizeof(*extent)); segment->header.snapshot_min = snapshot_id; segment->header.snapshot_max = snapshot_id; segment->header.segment_domain_id = 0; segment->header.segment_visibility = 1; segment->header.federation_version = 0; segment->header.flags = 0; segment->header.reserved0 = 0; record->hash_id = ref.hash_id; record->digest_len = (uint16_t)ref.digest.len; record->extent_count = 1; record->total_length = (uint32_t)artifact_bytes.len; record->domain_id = 0; record->visibility = 1; record->has_cross_domain_source = 0; record->cross_domain_source = 0; record->flags = 0; extent->block_id = block_id; extent->offset = 0; extent->length = (uint32_t)artifact_bytes.len; segment->records = record; segment->record_count = 1; segment->digests = ref.digest; segment->extents = extent; segment->extent_count = 1; segment->footer.seal_snapshot = snapshot_id; segment->footer.seal_time_ns = snapshot_id; return true; } static bool write_artifact_segment(const char *root, amduat_artifact_t artifact, uint64_t segment_id, uint64_t block_id, uint64_t snapshot_id, uint8_t out_hash[32], amduat_reference_t *out_ref) { amduat_reference_t ref; amduat_octets_t artifact_bytes; amduat_asl_core_index_segment_t segment; amduat_asl_index_record_t record; amduat_asl_extent_record_t extent; if (out_ref == NULL) { return false; } if (!amduat_asl_ref_derive(artifact, AMDUAT_ENC_ASL1_CORE_V1, AMDUAT_HASH_ASL1_ID_SHA256, &ref, &artifact_bytes)) { return false; } if (!write_block_file(root, block_id, artifact_bytes)) { amduat_octets_free(&artifact_bytes); amduat_reference_free(&ref); return false; } if (!build_segment_for_artifact(&segment, &record, &extent, ref, artifact_bytes, snapshot_id, block_id) || !write_segment_file(root, segment_id, &segment, out_hash)) { amduat_octets_free(&artifact_bytes); amduat_reference_free(&ref); return false; } amduat_octets_free(&artifact_bytes); *out_ref = ref; return true; } static bool replay_state_equal(const amduat_asl_replay_state_t *lhs, const amduat_asl_replay_state_t *rhs) { size_t i; if (lhs->segments_len != rhs->segments_len || lhs->tombstones_len != rhs->tombstones_len || lhs->state.log_position != rhs->state.log_position) { return false; } for (i = 0; i < lhs->segments_len; ++i) { const amduat_asl_segment_seal_t *left = &lhs->segments[i]; const amduat_asl_segment_seal_t *right = &rhs->segments[i]; if (left->segment_id != right->segment_id || memcmp(left->segment_hash, right->segment_hash, sizeof(left->segment_hash)) != 0) { return false; } } for (i = 0; i < lhs->tombstones_len; ++i) { const amduat_asl_tombstone_entry_t *left = &lhs->tombstones[i]; const amduat_asl_tombstone_entry_t *right = &rhs->tombstones[i]; if (left->tombstone_logseq != right->tombstone_logseq || !amduat_reference_eq(left->ref, right->ref)) { return false; } } return true; } static int test_replay_determinism(void) { char *root; amduat_artifact_t artifact_a; amduat_artifact_t artifact_b; amduat_reference_t ref_a; amduat_reference_t ref_b; uint8_t hash_a[32]; uint8_t hash_b[32]; uint8_t seal_payload_a[8 + 32]; uint8_t seal_payload_b[8 + 32]; size_t seal_len_a; size_t seal_len_b; amduat_asl_log_record_t records[2]; amduat_asl_log_record_t *loaded_records = NULL; size_t loaded_count = 0; amduat_asl_replay_state_t first; amduat_asl_replay_state_t second; uint8_t payload_a[4] = {0x01, 0x02, 0x03, 0x04}; uint8_t payload_b[4] = {0x10, 0x20, 0x30, 0x40}; int exit_code = 1; ref_a = amduat_reference(0u, amduat_octets(NULL, 0u)); ref_b = amduat_reference(0u, amduat_octets(NULL, 0u)); root = make_temp_root(); if (root == NULL) { fprintf(stderr, "temp root failed\n"); return 1; } if (!prepare_index_tree(root)) { fprintf(stderr, "prepare index tree failed\n"); goto cleanup; } artifact_a = amduat_artifact(amduat_octets(payload_a, sizeof(payload_a))); artifact_b = amduat_artifact(amduat_octets(payload_b, sizeof(payload_b))); if (!write_artifact_segment(root, artifact_a, 1, 1, 1, hash_a, &ref_a) || !write_artifact_segment(root, artifact_b, 2, 2, 2, hash_b, &ref_b)) { fprintf(stderr, "write artifact segments failed\n"); goto cleanup; } seal_len_a = build_segment_seal_payload(seal_payload_a, sizeof(seal_payload_a), 1, hash_a); seal_len_b = build_segment_seal_payload(seal_payload_b, sizeof(seal_payload_b), 2, hash_b); if (seal_len_a == 0 || seal_len_b == 0) { fprintf(stderr, "seal payload build failed\n"); goto cleanup; } memset(records, 0, sizeof(records)); records[0].logseq = 10; records[0].record_type = AMDUAT_ASL_LOG_RECORD_SEGMENT_SEAL; records[0].payload = amduat_octets(seal_payload_a, seal_len_a); records[1].logseq = 20; records[1].record_type = AMDUAT_ASL_LOG_RECORD_SEGMENT_SEAL; records[1].payload = amduat_octets(seal_payload_b, seal_len_b); if (!write_log(root, records, 2)) { fprintf(stderr, "write log failed\n"); goto cleanup; } if (!load_log_records(root, &loaded_records, &loaded_count)) { fprintf(stderr, "log decode failed\n"); goto cleanup; } if (!amduat_asl_replay_init(&first) || !amduat_asl_replay_init(&second)) { fprintf(stderr, "replay init failed\n"); goto cleanup; } if (!amduat_asl_replay_apply_log(loaded_records, loaded_count, 20, &first) || !amduat_asl_replay_apply_log(loaded_records, loaded_count, 20, &second)) { fprintf(stderr, "replay apply failed\n"); amduat_asl_replay_free(&first); amduat_asl_replay_free(&second); goto cleanup; } if (!replay_state_equal(&first, &second)) { fprintf(stderr, "replay state mismatch\n"); amduat_asl_replay_free(&first); amduat_asl_replay_free(&second); goto cleanup; } amduat_asl_replay_free(&first); amduat_asl_replay_free(&second); exit_code = 0; cleanup: if (loaded_records != NULL) { amduat_enc_asl_log_free(loaded_records, loaded_count); } amduat_reference_free(&ref_a); amduat_reference_free(&ref_b); if (!remove_tree(root)) { fprintf(stderr, "cleanup failed\n"); exit_code = 1; } free(root); return exit_code; } static int test_tombstone_lift_boundary(void) { char *root; amduat_artifact_t artifact; amduat_reference_t ref; uint8_t hash[32]; uint8_t seal_payload[8 + 32]; uint8_t tombstone_payload[4 + 2 + 2 + 32 + 4 + 4]; uint8_t lift_payload[4 + 2 + 2 + 32 + 8]; size_t seal_len; size_t tombstone_len; size_t lift_len; amduat_asl_log_record_t records[4]; amduat_asl_store_config_t config; amduat_asl_store_index_fs_t fs; amduat_asl_store_t store; amduat_asl_index_state_t cutoff; amduat_artifact_t loaded; amduat_asl_store_error_t err; uint8_t payload[5] = {0x11, 0x22, 0x33, 0x44, 0x55}; int exit_code = 1; ref = amduat_reference(0u, amduat_octets(NULL, 0u)); root = make_temp_root(); if (root == NULL) { fprintf(stderr, "temp root failed\n"); return 1; } if (!prepare_index_tree(root)) { fprintf(stderr, "prepare index tree failed\n"); goto cleanup; } artifact = amduat_artifact(amduat_octets(payload, sizeof(payload))); if (!write_artifact_segment(root, artifact, 5, 5, 5, hash, &ref)) { fprintf(stderr, "write artifact segment failed\n"); goto cleanup; } seal_len = build_segment_seal_payload(seal_payload, sizeof(seal_payload), 5, hash); tombstone_len = build_tombstone_payload(tombstone_payload, sizeof(tombstone_payload), ref.hash_id, ref.digest.data, ref.digest.len); lift_len = build_tombstone_lift_payload(lift_payload, sizeof(lift_payload), ref.hash_id, ref.digest.data, ref.digest.len, 20); if (seal_len == 0 || tombstone_len == 0 || lift_len == 0) { fprintf(stderr, "log payload build failed\n"); goto cleanup; } memset(records, 0, sizeof(records)); records[0].logseq = 10; records[0].record_type = AMDUAT_ASL_LOG_RECORD_SEGMENT_SEAL; records[0].payload = amduat_octets(seal_payload, seal_len); records[1].logseq = 20; records[1].record_type = AMDUAT_ASL_LOG_RECORD_TOMBSTONE; records[1].payload = amduat_octets(tombstone_payload, tombstone_len); records[2].logseq = 30; records[2].record_type = AMDUAT_ASL_LOG_RECORD_SNAPSHOT_ANCHOR; records[2].payload = amduat_octets(NULL, 0u); records[3].logseq = 40; records[3].record_type = AMDUAT_ASL_LOG_RECORD_TOMBSTONE_LIFT; records[3].payload = amduat_octets(lift_payload, lift_len); if (!write_log(root, records, 4)) { fprintf(stderr, "write log failed\n"); goto cleanup; } memset(&config, 0, sizeof(config)); config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1; config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256; if (!amduat_asl_store_index_fs_init(&fs, config, root)) { fprintf(stderr, "index fs init failed\n"); goto cleanup; } amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs); cutoff.snapshot_id = 0; cutoff.log_position = 30; loaded = amduat_artifact(amduat_octets(NULL, 0u)); err = amduat_asl_store_get_indexed(&store, ref, cutoff, &loaded); if (err != AMDUAT_ASL_STORE_ERR_NOT_FOUND) { fprintf(stderr, "tombstone cutoff expected not found: %d\n", err); amduat_artifact_free(&loaded); goto cleanup; } cutoff.log_position = 40; loaded = amduat_artifact(amduat_octets(NULL, 0u)); err = amduat_asl_store_get_indexed(&store, ref, cutoff, &loaded); if (err != AMDUAT_ASL_STORE_OK) { fprintf(stderr, "lift lookup failed: %d\n", err); amduat_artifact_free(&loaded); goto cleanup; } if (!amduat_artifact_eq(artifact, loaded)) { fprintf(stderr, "lifted artifact mismatch\n"); amduat_artifact_free(&loaded); goto cleanup; } amduat_artifact_free(&loaded); exit_code = 0; cleanup: amduat_reference_free(&ref); if (!remove_tree(root)) { fprintf(stderr, "cleanup failed\n"); exit_code = 1; } free(root); return exit_code; } static int test_crash_recovery_unsealed(void) { char *root; amduat_artifact_t artifact_a; amduat_artifact_t artifact_b; amduat_reference_t ref_a; amduat_reference_t ref_b; uint8_t hash_a[32]; uint8_t hash_b[32]; uint8_t seal_payload[8 + 32]; size_t seal_len; amduat_asl_log_record_t records[1]; amduat_asl_log_record_t *loaded_records = NULL; size_t loaded_count = 0; amduat_asl_replay_state_t replay_state; amduat_asl_store_config_t config; amduat_asl_store_index_fs_t fs; amduat_asl_store_t store; amduat_asl_index_state_t current; amduat_artifact_t loaded; amduat_asl_store_error_t err; uint8_t payload_a[3] = {0x61, 0x62, 0x63}; uint8_t payload_b[3] = {0x64, 0x65, 0x66}; int exit_code = 1; ref_a = amduat_reference(0u, amduat_octets(NULL, 0u)); ref_b = amduat_reference(0u, amduat_octets(NULL, 0u)); root = make_temp_root(); if (root == NULL) { fprintf(stderr, "temp root failed\n"); return 1; } if (!prepare_index_tree(root)) { fprintf(stderr, "prepare index tree failed\n"); goto cleanup; } artifact_a = amduat_artifact(amduat_octets(payload_a, sizeof(payload_a))); artifact_b = amduat_artifact(amduat_octets(payload_b, sizeof(payload_b))); if (!write_artifact_segment(root, artifact_a, 1, 1, 1, hash_a, &ref_a) || !write_artifact_segment(root, artifact_b, 9, 9, 9, hash_b, &ref_b)) { fprintf(stderr, "write artifact segments failed\n"); goto cleanup; } seal_len = build_segment_seal_payload(seal_payload, sizeof(seal_payload), 1, hash_a); if (seal_len == 0) { fprintf(stderr, "seal payload build failed\n"); goto cleanup; } memset(records, 0, sizeof(records)); records[0].logseq = 10; records[0].record_type = AMDUAT_ASL_LOG_RECORD_SEGMENT_SEAL; records[0].payload = amduat_octets(seal_payload, seal_len); if (!write_log(root, records, 1)) { fprintf(stderr, "write log failed\n"); goto cleanup; } memset(&config, 0, sizeof(config)); config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1; config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256; if (!amduat_asl_store_index_fs_init(&fs, config, root)) { fprintf(stderr, "index fs init failed\n"); goto cleanup; } amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs); if (!amduat_asl_index_current_state(&store, ¤t)) { fprintf(stderr, "current state failed\n"); goto cleanup; } loaded = amduat_artifact(amduat_octets(NULL, 0u)); err = amduat_asl_store_get_indexed(&store, ref_b, current, &loaded); if (err != AMDUAT_ASL_STORE_ERR_NOT_FOUND) { fprintf(stderr, "unsealed segment should be ignored: %d\n", err); amduat_artifact_free(&loaded); goto cleanup; } if (!load_log_records(root, &loaded_records, &loaded_count)) { fprintf(stderr, "log decode failed\n"); goto cleanup; } if (!amduat_asl_replay_init(&replay_state)) { fprintf(stderr, "replay init failed\n"); goto cleanup; } if (!amduat_asl_replay_apply_log(loaded_records, loaded_count, current.log_position, &replay_state)) { fprintf(stderr, "replay apply failed\n"); amduat_asl_replay_free(&replay_state); goto cleanup; } if (replay_state.segments_len != 1 || replay_state.segments[0].segment_id != 1 || memcmp(replay_state.segments[0].segment_hash, hash_a, sizeof(hash_a)) != 0) { fprintf(stderr, "replay should only include sealed segment\n"); amduat_asl_replay_free(&replay_state); goto cleanup; } amduat_asl_replay_free(&replay_state); exit_code = 0; cleanup: if (loaded_records != NULL) { amduat_enc_asl_log_free(loaded_records, loaded_count); } amduat_reference_free(&ref_a); amduat_reference_free(&ref_b); if (!remove_tree(root)) { fprintf(stderr, "cleanup failed\n"); exit_code = 1; } free(root); return exit_code; } int main(void) { if (test_replay_determinism() != 0) { return 1; } if (test_tombstone_lift_boundary() != 0) { return 1; } if (test_crash_recovery_unsealed() != 0) { return 1; } return 0; }