diff options
author | Dana Robinson <derobins@hdfgroup.org> | 2021-09-08 04:07:26 (GMT) |
---|---|---|
committer | Dana Robinson <derobins@hdfgroup.org> | 2021-09-08 04:07:26 (GMT) |
commit | 342ac95e148473b9fdffee502e953094f50ef98e (patch) | |
tree | 09f6eacb9ba9978da820356662022e96cade2721 /test/onion.c | |
parent | 163d40dd4255cf5623b3b8a6dc83ffbd8926bb1f (diff) | |
download | hdf5-342ac95e148473b9fdffee502e953094f50ef98e.zip hdf5-342ac95e148473b9fdffee502e953094f50ef98e.tar.gz hdf5-342ac95e148473b9fdffee502e953094f50ef98e.tar.bz2 |
Brings onion VFD files over from private branch
Diffstat (limited to 'test/onion.c')
-rw-r--r-- | test/onion.c | 3493 |
1 files changed, 3493 insertions, 0 deletions
diff --git a/test/onion.c b/test/onion.c new file mode 100644 index 0000000..ae492ba --- /dev/null +++ b/test/onion.c @@ -0,0 +1,3493 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the COPYING file, which can be found at the root of the source code * + * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. * + * If you do not have access to either file, you may request a copy from * + * help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Onion Virtual File Driver (VFD) + * + * Purpose: + * + * Verify Onion VFD behavior that is not involved with operations on the + * backing store. + */ + +#include "h5test.h" + +#include "H5Fprivate.h" /* encode/decode macros */ +#include "H5FDonion.h" /* This file driver's utilities */ +#include "H5FDonion_priv.h" /* Onion file driver internals */ + +/* 2^n for uint64_t types -- H5_EXP2 unsafe past 32 bits */ +#define U64_EXP2(n) ((uint64_t)1 << (n)) + +#define ONION_TEST_PAGE_SIZE_1 (uint32_t)4 +#define ONION_TEST_PAGE_SIZE_5 (uint32_t)32 +#define ONION_TEST_BASENAME_SIZE 32 +#define ONION_TEST_FIXNAME_SIZE 1024 +#define ONION_TEST_EXPECTED_HISTORY_REVISIONS_MAX 16 +#define ONION_TEST_REV_REV_WRITES_MAX 8 +#define ONION_TEST_REV_REV_MAGIC 0xDEADBEEF + +#define WIP 1 /* development toggle */ + +/* Structure to collect the onion filepaths in one place. */ +struct onion_filepaths { + char *canon; + char *onion; + char *recovery; +}; + +struct expected_revision { + uint64_t revision_id; + uint64_t parent_revision_id; + uint64_t logi_eof; + uint64_t n_index_entries; + const char *comment; +}; +struct expected_history { + uint64_t page_size; + uint64_t n_revisions; + uint64_t origin_eof; + struct expected_revision revisions[ONION_TEST_EXPECTED_HISTORY_REVISIONS_MAX]; +}; + +struct write_info { + haddr_t offset; + haddr_t size; + const unsigned char *buf; +}; +struct revise_revision { + uint64_t magic; + hbool_t truncate; /* onion-create, truncating any existing data */ + uint64_t revision_id; + size_t n_writes; + struct write_info writes[ONION_TEST_REV_REV_WRITES_MAX]; + const char *comment; +}; + +static int compare_file_bytes_exactly(const char *, hid_t, size_t, + const unsigned char *); +static int do_onion_open_and_writes(const char *filename, + H5FD_onion_fapl_info_t *onion_info_p, size_t n_ops, + struct revise_revision *about); +static void onion_filepaths_destroy(struct onion_filepaths *); +static struct onion_filepaths *onion_filepaths_init(const char *, + H5FD_onion_fapl_info_t *); +#if 0 +static int is_onion_data_page_aligned( + const struct H5FD__onion_history_header *); +static uint32_t up_size_to_page_boundary(uint64_t, uint32_t); +#endif + + + +/* set at runtime in main() */ +static unsigned int flags_create_s = 0; + + +/* NOTE: b_list must be longer than a_list. + * Sizes must match respective buffer lengths. + */ + +/* twenty-six four-character words beginning with 'a' -> 104 bytes */ +const unsigned char *a_list_s = (const unsigned char *) \ + "abetableacedacesacheacidacneadzeafaragedagesaidsairsajarallyalum" \ + "amokantsapesarcsareaartsasksaspsavidaxes"; +uint64_t a_list_size_s = 104; + +/* fifty-three four-character words beginning with 'b' -> 212 bytes */ +const unsigned char *b_list_s = (const unsigned char *) \ + "badebailbaitbalebanebarebaskbeambeanbearbeenbeerbeltbentbestbide" \ + "bikebilebindbirdbiteblipblueboarboatbobsbodyboilboldbollboltbond" \ + "boneboobboorboosbootbradbragbratbraybrewbritbrowbuckbudsbunkbunt" \ + "buoyburnburybustbuys"; +uint64_t b_list_size_s = 212; + + +/* Allocate and populate filepaths with h5_fixname'd strings as appropriate. + * Should be released with onion_filepaths_destroy() when done. + */ +static struct onion_filepaths * +onion_filepaths_init(const char *basename, H5FD_onion_fapl_info_t *fa_info) +{ + struct onion_filepaths *paths = NULL; + + paths = (struct onion_filepaths *)HDmalloc(sizeof(struct onion_filepaths)); + if (NULL == paths) + TEST_ERROR; + paths->canon = NULL; + paths->onion = NULL; + paths->recovery = NULL; + + paths->canon = (char *)HDmalloc(sizeof(char) * ONION_TEST_FIXNAME_SIZE); + if (NULL == paths->canon) + TEST_ERROR; + if (!h5_fixname_no_suffix(basename, fa_info->backing_fapl_id, \ + paths->canon, ONION_TEST_FIXNAME_SIZE)) + TEST_ERROR; + + paths->onion = (char *)HDmalloc(sizeof(char) * ONION_TEST_FIXNAME_SIZE); + HDsnprintf(paths->onion, ONION_TEST_FIXNAME_SIZE, "%s.onion", \ + paths->canon); + + paths->recovery = (char *)HDmalloc(sizeof(char) * ONION_TEST_FIXNAME_SIZE); + HDsnprintf(paths->recovery, ONION_TEST_FIXNAME_SIZE, "%s.onion.recovery", \ + paths->canon); + + return paths; + +error: + if (paths != NULL) { + if (paths->canon != NULL) + HDfree(paths->canon); + if (paths->onion != NULL) + HDfree(paths->onion); + if (paths->recovery != NULL) + HDfree(paths->recovery); + } + return NULL; +} + +static void +onion_filepaths_destroy(struct onion_filepaths *s) +{ + HDfree(s->canon); + HDfree(s->onion); + HDfree(s->recovery); + HDfree(s); +} + + +#if 0 +static int +is_onion_data_page_aligned(const struct H5FD__onion_history_header *header) +{ + return header->flags & H5FD__ONION_HEADER_FLAG_PAGE_ALIGNMENT; +} + +static uint32_t +up_size_to_page_boundary(uint64_t size_in, uint32_t page_size) +{ + uint32_t _i = 0; /* number of pages occupied */ + for (_i = 1; (page_size * _i) < size_in; _i++); + return page_size * _i; +} +#endif + +/*----------------------------------------------------------------------------- + * + * Function: test_archival_index() + * + * Purpose: Unit-test mechanisms for the onion archival index. + * Specifies and verifies index-validation and -search routines. + * + * Return: PASSED : 0 + * FAILED : -1 + * + *----------------------------------------------------------------------------- + */ +static int +test_archival_index(void) +{ + /* We can ignore each entry's physical address and checksum values */ + struct H5FD__onion_index_entry e0 = { 1, 474}; + struct H5FD__onion_index_entry e1 = { 4, 558}; + struct H5FD__onion_index_entry e2 = { 5, 306}; + struct H5FD__onion_index_entry e3 = { 9, 515}; + struct H5FD__onion_index_entry e4 = {14, 386}; + struct H5FD__onion_index_entry e5 = {18, 90}; + struct H5FD__onion_index_entry e6 = {19, 94}; + struct H5FD__onion_index_entry e7 = {20, 509}; + struct H5FD__onion_index_entry empty[8]; + struct H5FD__onion_index_entry sorted[8] = { + e0, e1, e2, e3, e4, e5, e6, e7}; + struct H5FD__onion_index_entry sorted_duplicates[8] = { + e0, e1, e2, e2, e4, e5, e6, e7}; + struct H5FD__onion_index_entry sorted_incomplete[8] = { + e1, e3, e4, e5}; + /* partially-sorted list also aligned to 2 * page-size */ + struct H5FD__onion_index_entry sorted_partial[8] = { + e1, e4, e5, e7, e0, e6, e2, e3}; /* 0..3 sorted */ + struct H5FD__onion_index_entry unsorted[8] = { + e3, e1, e4, e5, e0, e6, e2, e7}; + struct H5FD__onion_archival_index aix = { + H5FD__ONION_ARCHIVAL_INDEX_MAGIC, + H5FD__ONION_ARCHIVAL_INDEX_VERSION_CURR, + 1, /* page_size_log2 */ + 8, /* list must be populated and sorted through 0 .. (count-1) */ + sorted, /* list */ + }; + const struct H5FD__onion_index_entry *entry_out_p = NULL; + + TESTING("archival index"); + + /* + * Failing validity checks + */ + + aix.magic++; + if (H5FD_onion_archival_index_is_valid(&aix)) + TEST_ERROR; /* invalid magic should fail */ + aix.magic--; + + aix.version++; + if (H5FD_onion_archival_index_is_valid(&aix)) + TEST_ERROR; /* invalid version should fail */ + aix.version = 0; + if (H5FD_onion_archival_index_is_valid(&aix)) + TEST_ERROR; /* invalid version should fail */ + aix.version = H5FD__ONION_ARCHIVAL_INDEX_VERSION_CURR; + + aix.list = NULL; + if (H5FD_onion_archival_index_is_valid(&aix)) + TEST_ERROR; /* list cannot be NULL */ + + aix.list = empty; + if (H5FD_onion_archival_index_is_valid(&aix)) + TEST_ERROR; /* list cannot be empty */ + + aix.list = sorted_incomplete; + if (H5FD_onion_archival_index_is_valid(&aix)) + TEST_ERROR; /* list must be full */ + + aix.list = unsorted; + if (H5FD_onion_archival_index_is_valid(&aix)) + TEST_ERROR; /* list must be sorted */ + + aix.list = sorted_duplicates; + if (H5FD_onion_archival_index_is_valid(&aix)) + TEST_ERROR; /* list cannot have duplicates */ + + /* + * Passing validity checks + */ + + aix.list = sorted; + if (!H5FD_onion_archival_index_is_valid(&aix)) + TEST_ERROR; /* everything in order should report valid */ + + aix.list = sorted_partial; + aix.n_entries = 4; + if (!H5FD_onion_archival_index_is_valid(&aix)) + TEST_ERROR; /* elements after n_entries are ignored */ + + /* + * Archival index search routine + */ + + aix.list = sorted; + aix.n_entries = 8; + + if (H5FD_onion_archival_index_find(&aix, 3, &entry_out_p) != 0) + TEST_ERROR; /* address not in array -> returns 0 */ + if (entry_out_p != NULL) + TEST_ERROR; /* pointer should remain unset */ + + if (H5FD_onion_archival_index_find(&aix, 4, &entry_out_p) != 1) + TEST_ERROR; /* address found -> should return 1 */ + if (NULL == entry_out_p) + TEST_ERROR; /* pointer should be set */ + if (558 != entry_out_p->phys_addr) + TEST_ERROR; /* incorrect address recorded */ + + + /* + * Test search edge cases + */ + + aix.list = sorted_incomplete; + aix.n_entries = 4; + + if (H5FD_onion_archival_index_find(&aix, 1, &entry_out_p) != 0) + TEST_ERROR; /* address not in array -> returns 0 */ + + if (H5FD_onion_archival_index_find(&aix, 101, &entry_out_p) != 0) + TEST_ERROR; /* address not in array -> returns 0 */ + + /* + * Empty archival index + */ + + entry_out_p = NULL; + aix.n_entries = 0; /* actually populated list is irrelevant */ + if (H5FD_onion_archival_index_find(&aix, 3, &entry_out_p) != 0) + TEST_ERROR; /* address not in array -> returns 0 */ + if (entry_out_p != NULL) + TEST_ERROR; /* pointer should remain unset */ + + PASSED(); + return 0; + +error: + return -1; +} /* end test_archival_index() */ + +/*----------------------------------------------------------------------------- + * + * Function: test_revision_index() + * + * Purpose: TBD + * + * Return: PASSED : 0 + * FAILED : -1 + * + *----------------------------------------------------------------------------- + */ +static int +test_revision_index(void) +{ + struct H5FD__onion_revision_index *rix_p = NULL; + struct H5FD__onion_index_entry entry = { + 42, /* logi_page */ + 111112, /* phys_addr */ + }; + const struct H5FD__onion_index_entry *entry_out_p = NULL; + + TESTING("revision index"); + + /* Test index creation + */ + + rix_p = H5FD_onion_revision_index_init(ONION_TEST_PAGE_SIZE_5); + if (NULL == rix_p) + TEST_ERROR; /* unable to initialize working index */ + if (H5FD__ONION_REVISION_INDEX_MAGIC != rix_p->magic) + TEST_ERROR; + if (H5FD__ONION_REVISION_INDEX_VERSION_CURR != rix_p->version) + TEST_ERROR; + if (0 != rix_p->n_entries) + TEST_ERROR; + + /* Test obviously failing inserts + */ + + if (H5FD_onion_revision_index_insert(rix_p, NULL) != FAIL) + TEST_ERROR; /* cannot be NULL */ + + /* Test missed search + */ + + if (H5FD_onion_revision_index_find(rix_p, entry.logi_page, &entry_out_p) \ + != 0) + TEST_ERROR; + + /* Test successful insertion and lookup + */ + + if (H5FD_onion_revision_index_insert(rix_p, &entry) < 0) + TEST_ERROR; /* insertion failed */ + if (1 != rix_p->n_entries) + TEST_ERROR; + if (H5FD_onion_revision_index_find(rix_p, entry.logi_page, &entry_out_p) \ + < 0) + TEST_ERROR; /* lookup failed */ + if (NULL == entry_out_p) + TEST_ERROR; /* failure to set output parameter */ + if (entry.logi_page != entry_out_p->logi_page) + TEST_ERROR; + if (H5FD_onion_revision_index_find(rix_p, entry.logi_page + 1, \ + &entry_out_p) != 0) + TEST_ERROR; /* seeking other, absent page should miss */ + + /* Test / demonstrate stored entry independent of user object + */ + + entry.logi_page = 100; + entry.phys_addr = 101; + if (H5FD_onion_revision_index_insert(rix_p, &entry) < 0) + TEST_ERROR; + if (2 != rix_p->n_entries) + TEST_ERROR; + entry.logi_page = 500; + entry.phys_addr = 501; + if (H5FD_onion_revision_index_find(rix_p, 100, &entry_out_p) < 0) + TEST_ERROR; + if (100 != entry_out_p->logi_page || 101 != entry_out_p->phys_addr) + TEST_ERROR; + + /* Demonstrate updating an entry + */ + + /* Error cases */ + + entry.logi_page = 100; /* phys_addr still 501, checksum bbbbbbbb */ + if (H5FD_onion_revision_index_insert(rix_p, &entry) >= 0) + TEST_ERROR; /* all components but sum must match */ + entry.phys_addr = 101; + + /* Successful update */ + + entry.logi_page = 100; + entry.phys_addr = 101; + if (H5FD_onion_revision_index_insert(rix_p, &entry) < 0) + TEST_ERROR; + if (2 != rix_p->n_entries) + TEST_ERROR; /* Should still be two unique entries, not three */ + if (H5FD_onion_revision_index_find(rix_p, 100, &entry_out_p) < 0) + TEST_ERROR; + if (100 != entry_out_p->logi_page || 101 != entry_out_p->phys_addr) + TEST_ERROR; + + if (H5FD_onion_revision_index_destroy(rix_p) < 0) + TEST_ERROR; + rix_p = NULL; + + PASSED(); + return 0; + +error: + if (rix_p != NULL) { + (void)H5FD_onion_revision_index_destroy(rix_p); + } + return -1; +} /* end test_revision_index() */ + +/*----------------------------------------------------------------------------- + * + * Function: test_revision_index_collisions() + * + * Purpose: With knowledge of the revision index implementation, test + * hash key collisions. + * + * Return: PASSED : 0 + * FAILED : -1 + * + *----------------------------------------------------------------------------- + */ +static int +test_revision_index_collisions(void) +{ + H5FD__onion_revision_index_t *rix_p = NULL; + struct H5FD__onion_index_entry entry = { + 0, /* logi_page */ + 0, /* phys_addr */ + }; + const struct H5FD__onion_index_entry *entry_out_p = NULL; + uint64_t i = 0; + const uint64_t n_insert = 40; + const uint64_t offset_from_power = 5; + + TESTING("revision index collisions"); + + rix_p = H5FD_onion_revision_index_init(ONION_TEST_PAGE_SIZE_5); + if (NULL == rix_p) + TEST_ERROR; /* unable to initialize working index */ + + for (i = 0; i < n_insert; i++) { + entry.phys_addr = i; + entry.logi_page = U64_EXP2(i) + offset_from_power; + if (H5FD_onion_revision_index_insert(rix_p, &entry) < 0) + TEST_ERROR; + } + + if (n_insert != rix_p->n_entries) + TEST_ERROR; + + for (i = 0; i < n_insert; i++) { + uint64_t page_id = U64_EXP2(i) + offset_from_power; + + if (H5FD_onion_revision_index_find(rix_p, page_id, &entry_out_p) != 1) + TEST_ERROR; + if (entry_out_p->phys_addr != i) + TEST_ERROR; + } + + if (H5FD_onion_revision_index_destroy(rix_p) < 0) + TEST_ERROR; + rix_p = NULL; + + PASSED(); + return 0; + +error: + if (rix_p != NULL) { + (void)H5FD_onion_revision_index_destroy(rix_p); + } + return -1; +} /* end test_revision_index_collisions() */ + +/*----------------------------------------------------------------------------- + * + * Function: test_revision_index_resizing() + * + * Purpose: With knowledge of the revision index implementation, test + * one or more resizig of the index. + * + * Return: PASSED : 0 + * FAILED : -1 + * + *----------------------------------------------------------------------------- + */ +static int +test_revision_index_resizing(void) +{ + H5FD__onion_revision_index_t *rix_p = NULL; + struct H5FD__onion_index_entry entry = { + 0, /* logi_page */ + 0, /* phys_addr */ + }; + const struct H5FD__onion_index_entry *entry_out_p = NULL; + uint64_t i = 0; + const uint64_t n_insert = U64_EXP2( \ + (H5FD__ONION_REVISION_INDEX_STARTING_SIZE_LOG2 + 3)); + + TESTING("revision index resizing"); + + rix_p = H5FD_onion_revision_index_init(ONION_TEST_PAGE_SIZE_5); + if (NULL == rix_p) + TEST_ERROR; /* unable to initialize working index */ + + for (i = 0; i < n_insert; i++) { + entry.logi_page = i; + entry.phys_addr = ((uint64_t)(-1) - i); + if (H5FD_onion_revision_index_insert(rix_p, &entry) < 0) + TEST_ERROR; + } + + if (n_insert != rix_p->n_entries) + TEST_ERROR; + + for (i = 0; i < n_insert; i++) { + uint64_t page_id = i; + + if (H5FD_onion_revision_index_find(rix_p, page_id, &entry_out_p) != 1) + TEST_ERROR; + if (entry_out_p->phys_addr != ((uint64_t)(-1) - i)) + TEST_ERROR; + } + + if (H5FD_onion_revision_index_destroy(rix_p) < 0) + TEST_ERROR; + rix_p = NULL; + + PASSED(); + return 0; + +error: + if (rix_p != NULL) { + (void)H5FD_onion_revision_index_destroy(rix_p); + } + return -1; +} /* end test_revision_index_resizing() */ + +/*----------------------------------------------------------------------------- + * + * Function: test_revision_index_to_archival_index() + * + * Purpose: Verify to_archival_index(). + * + * Return: PASSED : 0 + * FAILED : -1 + * + *----------------------------------------------------------------------------- + */ +static int +test_revision_index_to_archival_index(void) +{ + H5FD__onion_revision_index_t *rix_p = NULL; + struct H5FD__onion_index_entry rix_entry = { + 0, /* logi_page */ + 0, /* phys_addr */ + }; + struct H5FD__onion_archival_index aix = { + H5FD__ONION_ARCHIVAL_INDEX_MAGIC, + H5FD__ONION_ARCHIVAL_INDEX_VERSION_CURR, + 5, /* page_size_log2 */ + 0, /* n_entries to be set */ + NULL, + }; + struct H5FD__onion_index_entry *_list = NULL; + const uint64_t n_insert = 10; + uint64_t i = 0; + + TESTING("revision index to archival index"); + + /* + * SETUP + */ + + rix_p = H5FD_onion_revision_index_init(ONION_TEST_PAGE_SIZE_5); + if (NULL == rix_p) + TEST_ERROR; /* unable to initialize working index */ + + /* Add scattered entries in reverse order. */ + for (i = 0; i < n_insert; i++) { + uint64_t n = 2003 * (n_insert - i) + 47; + + rix_entry.logi_page = n; + rix_entry.phys_addr = n * 13; + if (H5FD_onion_revision_index_insert(rix_p, &rix_entry) < 0) + TEST_ERROR; + } + + if (n_insert != rix_p->n_entries) + TEST_ERROR; + + aix.list = (struct H5FD__onion_index_entry *)HDmalloc(0); + if (NULL == aix.list) + TEST_ERROR; + + aix.n_entries = 0; + + /* + * FAILING CASES + */ + + if (H5FD_onion_merge_revision_index_into_archival_index(NULL, NULL) \ + != FAIL) + TEST_ERROR; /* both cannot be null */ + + if (H5FD_onion_merge_revision_index_into_archival_index(NULL, &aix) \ + != FAIL) + TEST_ERROR; /* revision index cannot be null */ + + rix_p->magic++; + if (H5FD_onion_merge_revision_index_into_archival_index(rix_p, &aix) \ + != FAIL) + TEST_ERROR; /* revision index magic must be valid */ + rix_p->magic--; + + rix_p->version++; + if (H5FD_onion_merge_revision_index_into_archival_index(rix_p, &aix) \ + != FAIL) + TEST_ERROR; /* revision index version must be valid */ + rix_p->version--; + + if (H5FD_onion_merge_revision_index_into_archival_index(rix_p, NULL) \ + != FAIL) + TEST_ERROR; /* archival index cannot be null */ + + aix.magic++; + if (H5FD_onion_merge_revision_index_into_archival_index(rix_p, &aix) \ + != FAIL) + TEST_ERROR; /* archival index magic must be valid */ + aix.magic--; + + aix.version++; + if (H5FD_onion_merge_revision_index_into_archival_index(rix_p, &aix) \ + != FAIL) + TEST_ERROR; /* archival index version must be valid */ + aix.version--; + + _list = aix.list; + aix.list = NULL; + if (H5FD_onion_merge_revision_index_into_archival_index(rix_p, &aix) \ + != FAIL) + TEST_ERROR; /* list pointer must exist */ + aix.list = _list; + _list = NULL; + + aix.page_size_log2 += 1; + if (H5FD_onion_merge_revision_index_into_archival_index(rix_p, &aix) \ + != FAIL) + TEST_ERROR; /* page sizes must match */ + aix.page_size_log2 -= 1; + + /* Successful merge into empty archival index + */ + + if (H5FD_onion_merge_revision_index_into_archival_index(rix_p, &aix) < 0) + TEST_ERROR; + + if (!H5FD_onion_archival_index_is_valid(&aix)) + TEST_ERROR; /* entries not sorted, or other obscure issue */ + + if (n_insert != aix.n_entries) + TEST_ERROR; /* failed to resize and/or update archival index info */ + + for (i = 0; i < n_insert; i++) { + const struct H5FD__onion_index_entry *aix_entry_p = NULL; + uint64_t n = 2003 * (i + 1) + 47; + + aix_entry_p = &aix.list[i]; + + if (aix_entry_p->logi_page != n) + TEST_ERROR; + if (aix_entry_p->phys_addr != (n * 13)) + TEST_ERROR; + } + + /* Successful merge into populated archival index + */ + + HDfree(aix.list); + aix.list = NULL; + aix.list = (struct H5FD__onion_index_entry *)HDmalloc( + sizeof(struct H5FD__onion_index_entry) * 2); + if (NULL == aix.list) + TEST_ERROR; + aix.list[0].logi_page = 47; + aix.list[0].phys_addr = 47 * 13; + aix.list[1].logi_page = (2003 * (n_insert + 1) + 47); + aix.list[1].phys_addr = (2003 * (n_insert + 1) + 47) * 13; + aix.n_entries = 2; + + if (!H5FD_onion_archival_index_is_valid(&aix)) + TEST_ERROR; /* entries not sorted, or other obscure issue */ + + if (H5FD_onion_merge_revision_index_into_archival_index(rix_p, &aix) < 0) + TEST_ERROR; + + if (!H5FD_onion_archival_index_is_valid(&aix)) + TEST_ERROR; /* entries not sorted, or other obscure issue */ + + if (n_insert + 2 != aix.n_entries) + TEST_ERROR; + + for (i = 0; i < (n_insert + 2); i++) { + const struct H5FD__onion_index_entry *aix_entry_p = NULL; + uint64_t n = 2003 * i + 47; + + aix_entry_p = &aix.list[i]; + + if (aix_entry_p->logi_page != n) + TEST_ERROR; + if (aix_entry_p->phys_addr != (n * 13)) + TEST_ERROR; + } + + /* Merged enties from revision index replace existing entries + */ + + HDfree(aix.list); + aix.list = NULL; + aix.list = (struct H5FD__onion_index_entry *)HDmalloc( + sizeof(struct H5FD__onion_index_entry) * 2); + if (NULL == aix.list) + TEST_ERROR; + aix.list[0].logi_page = 2003 * (n_insert / 2) + 47; + aix.list[0].phys_addr = 103; + aix.list[1].logi_page = 2003 * (n_insert / 2 + 1) + 47; + aix.list[1].phys_addr = 101; + aix.n_entries = 2; + + if (!H5FD_onion_archival_index_is_valid(&aix)) + TEST_ERROR; /* entries not sorted, or other obscure issue */ + + if (H5FD_onion_merge_revision_index_into_archival_index(rix_p, &aix) < 0) + TEST_ERROR; + + if (!H5FD_onion_archival_index_is_valid(&aix)) + TEST_ERROR; /* entries not sorted, or other obscure issue */ + + if (n_insert != aix.n_entries) + TEST_ERROR; + + for (i = 0; i < n_insert; i++) { + const struct H5FD__onion_index_entry *aix_entry_p = NULL; + uint64_t n = 2003 * (i + 1) + 47; + + aix_entry_p = &aix.list[i]; + + if (aix_entry_p->logi_page != n) + TEST_ERROR; + if (aix_entry_p->phys_addr != (n * 13)) + TEST_ERROR; + } + + /* + * CLEANUP + */ + + if (H5FD_onion_revision_index_destroy(rix_p) < 0) + TEST_ERROR; + rix_p = NULL; + + HDfree(aix.list); + aix.list = NULL; + aix.magic++; + + PASSED(); + return 0; + +error: + if (rix_p != NULL) { + (void)H5FD_onion_revision_index_destroy(rix_p); + } + if (aix.list != NULL) { + HDfree(aix.list); + } + return -1; +} /* end test_revision_index_to_archival_index() */ + +/*----------------------------------------------------------------------------- + * + * Function: test_fapl() + * + * Purpose: Verify H5Pget and set behavior, and data-consistency checks. + * + * Return: PASSED : 0 + * FAILED : -1 + * + *----------------------------------------------------------------------------- + */ +static int +test_fapl(void) +{ + H5FD_onion_fapl_info_t info_in = { + H5FD_ONION_FAPL_INFO_MAGIC, + H5FD_ONION_FAPL_INFO_VERSION_CURR, + H5P_DEFAULT, /* backing_fapl_id */ + ONION_TEST_PAGE_SIZE_1, /* page_size */ + H5FD_ONION_STORE_TARGET_ONION, /* store_target */ + H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST, + 0, /* force_write_open */ + 0, /* creation_flags */ + "indoor speaking voices", /* comment */ + }; + H5FD_onion_fapl_info_t info_out; + hid_t dxpl_id = H5I_INVALID_HID; + hid_t fapl_id = H5I_INVALID_HID; + hid_t fapl_id_sec2 = H5I_INVALID_HID; + herr_t ret = FAIL; + + TESTING("file access property list"); + + dxpl_id = H5Pcreate(H5P_DATASET_XFER); + if (H5I_INVALID_HID == dxpl_id) + TEST_ERROR; + + fapl_id_sec2 = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == fapl_id_sec2) + TEST_ERROR; + if (H5Pset_fapl_sec2(fapl_id_sec2)) + TEST_ERROR; + + fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == fapl_id) + TEST_ERROR; + + /* + * Set FAPL + */ + + H5E_BEGIN_TRY { + ret = H5Pset_fapl_onion(H5I_INVALID_HID, &info_in); + } H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; /* fapl_id must be valid */ + + H5E_BEGIN_TRY { + ret = H5Pset_fapl_onion(fapl_id, NULL); + } H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; /* info pointer cannot be NULL */ + + info_in.magic++; + H5E_BEGIN_TRY { + ret = H5Pset_fapl_onion(fapl_id, &info_in); + } H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; /* info magic must be valid */ + info_in.magic--; + + info_in.version++; + H5E_BEGIN_TRY { + ret = H5Pset_fapl_onion(fapl_id, &info_in); + } H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; /* info version must be valid */ + info_in.version--; + + info_in.page_size = 7; + H5E_BEGIN_TRY { + ret = H5Pset_fapl_onion(fapl_id, &info_in); + } H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; /* page size must be valid power of 2 */ + + info_in.page_size = 0; + H5E_BEGIN_TRY { + ret = H5Pset_fapl_onion(fapl_id, &info_in); + } H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; /* page size must be greater than zero */ + info_in.page_size = ONION_TEST_PAGE_SIZE_1; + + info_in.backing_fapl_id = H5I_INVALID_HID; + H5E_BEGIN_TRY { + ret = H5Pset_fapl_onion(fapl_id, &info_in); + } H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; /* backing fapl ID cannot be invalid */ + + info_in.backing_fapl_id = dxpl_id; + H5E_BEGIN_TRY { + ret = H5Pset_fapl_onion(fapl_id, &info_in); + } H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; /* backing fapl ID must be file access proptery list ID */ + info_in.backing_fapl_id = H5P_DEFAULT; + + + if (H5Pset_fapl_onion(fapl_id, &info_in) < 0) + TEST_ERROR; + + /* + * Get onion fapl info + */ + + H5E_BEGIN_TRY { + ret = H5Pget_fapl_onion(fapl_id, NULL); + } H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; /* info_out pointer cannot be NULL */ + + H5E_BEGIN_TRY { + ret = H5Pget_fapl_onion(H5I_INVALID_HID, &info_out); + } H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; /* fapl_id must be valid */ + + H5E_BEGIN_TRY { + ret = H5Pget_fapl_onion(fapl_id_sec2, &info_out); + } H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; /* wrong fapl_id in */ + + if (H5Pget_fapl_onion(fapl_id, &info_out) < 0) + TEST_ERROR; + if (H5FD_ONION_FAPL_INFO_MAGIC != info_out.magic) + TEST_ERROR; + if (H5FD_ONION_FAPL_INFO_VERSION_CURR != info_out.version) + TEST_ERROR; + if (H5P_DEFAULT != info_out.backing_fapl_id) + TEST_ERROR; + if (ONION_TEST_PAGE_SIZE_1 != info_out.page_size) + TEST_ERROR; + if (H5FD_ONION_STORE_TARGET_ONION != info_out.store_target) + TEST_ERROR; + if (H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST != info_out.revision_id) + TEST_ERROR; + if (0 != info_out.creation_flags) + TEST_ERROR; + if (0 != info_out.force_write_open) + TEST_ERROR; + if (HDstrcmp(info_in.comment, info_out.comment)) + TEST_ERROR; + + /* + * Cleanup + */ + + if (H5Pclose(dxpl_id) < 0) + TEST_ERROR; + dxpl_id = H5I_INVALID_HID; + + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + fapl_id = H5I_INVALID_HID; + + if (H5Pclose(fapl_id_sec2) < 0) + TEST_ERROR; + fapl_id_sec2 = H5I_INVALID_HID; + + PASSED(); + return 0; + +error: + if (H5I_INVALID_HID != dxpl_id) + (void)H5Pclose(dxpl_id); + if (H5I_INVALID_HID != fapl_id) + (void)H5Pclose(fapl_id); + if (H5I_INVALID_HID != fapl_id_sec2) + (void)H5Pclose(fapl_id_sec2); + + return -1; +} /* end test_fapl() */ + +/*----------------------------------------------------------------------------- + * + * Function: test_header_encode_decode() + * + * Purpose: Verify onion header encoding and decoding behavior. + * + * Return: PASSED : 0 + * FAILED : -1 + * + *----------------------------------------------------------------------------- + */ +static int +test_header_encode_decode(void) +{ + unsigned char buf[64]; + unsigned char exp[64] = { /* bogus but unique values */ + 'O', 'H', 'D', 'H', /* NOTE: match signature define in onion_priv.h */ + 1, 12, 0, 0, /* NOTE: update version w/ "current" as needed */ + 0, 16, 0, 0, + 0x11, 0x00, 0, 0, 0x02, 0, 0, 0, /* origin_eof */ + 0x40, 0xe2, 0x01, 0, 0, 0, 0, 0, /* whole_history_addr */ + 88, 0, 0, 0, 0, 0, 0, 0, /* whole_history_size */ + 0, 0, 0, 0 /* sum populated below */ + }; + unsigned char *ptr = NULL; + uint32_t sum = 0; + uint32_t sum_out = 0; + size_t i = 0; + uint64_t size_ret = 0; + struct H5FD__onion_history_header hdr; + struct H5FD__onion_history_header hdr_out; + + TESTING("encode/decode history header"); + + sum = H5_checksum_fletcher32(exp, H5FD__ONION_ENCODED_SIZE_HEADER - 4); + ptr = exp + H5FD__ONION_ENCODED_SIZE_HEADER - 4; + UINT32ENCODE(ptr, sum); + + hdr.magic = H5FD__ONION_HEADER_MAGIC; + hdr.version = H5FD__ONION_HEADER_VERSION_CURR; + hdr.flags = 12; + hdr.origin_eof = 8589934609ull, + hdr.page_size = 4096; + hdr.whole_history_addr = 123456; + hdr.whole_history_size = 88; + + if (H5FD_onion_history_header_encode(&hdr, buf, &sum_out) \ + != H5FD__ONION_ENCODED_SIZE_HEADER) + TEST_ERROR; + + if (sum != sum_out) + TEST_ERROR; + + for (i = 0; i < H5FD__ONION_ENCODED_SIZE_HEADER; i++) { + if (exp[i] != buf[i]) { + HDprintf("first mismatched byte at %llu: %02x %02x\n", i, exp[i], \ + buf[i]); + TEST_ERROR; + } + } + + hdr_out.magic = H5FD__ONION_HEADER_MAGIC; + hdr_out.version = H5FD__ONION_HEADER_VERSION_CURR; + hdr_out.flags = 0; + hdr_out.page_size = 0; + hdr_out.whole_history_addr = 0; + hdr_out.whole_history_size = 0; + + /* Invalid header signature prevents decoding. + */ + + exp[3] = 'X'; /* invalidate encoded signature */ + H5E_BEGIN_TRY { + size_ret = H5FD_onion_history_header_decode(exp, &hdr_out); + } H5E_END_TRY; + if (0 != size_ret) + TEST_ERROR; + + exp[3] = 'H'; /* reset */ + + /* Invalid header version prevents decoding. + */ + + exp[4] = 0; /* encoded version 0?!? */ + H5E_BEGIN_TRY { + size_ret = H5FD_onion_history_header_decode(exp, &hdr_out); + } H5E_END_TRY; + if (0 != size_ret) + TEST_ERROR; + + exp[4] = H5FD__ONION_HEADER_VERSION_CURR + 1; /* encoded super-version?! */ + H5E_BEGIN_TRY { + size_ret = H5FD_onion_history_header_decode(exp, &hdr_out); + } H5E_END_TRY; + if (0 != size_ret) + TEST_ERROR; + + exp[4] = H5FD__ONION_HEADER_VERSION_CURR; /* reset */ + + /* Valid header can be decoded. + */ + + if (H5FD_onion_history_header_decode(buf, &hdr_out) \ + != H5FD__ONION_ENCODED_SIZE_HEADER) + TEST_ERROR; + if (H5FD__ONION_HEADER_MAGIC != hdr_out.magic) + TEST_ERROR; + if (H5FD__ONION_HEADER_VERSION_CURR != hdr_out.version) + TEST_ERROR; + if (hdr.flags != hdr_out.flags) + TEST_ERROR; + if (hdr.page_size != hdr_out.page_size) + TEST_ERROR; + if (hdr.whole_history_addr != hdr_out.whole_history_addr) + TEST_ERROR; + if (hdr.whole_history_size != hdr_out.whole_history_size) + TEST_ERROR; + + PASSED(); + return 0; + +error: + return -1; +} /* end test_header_encode_decode() */ + +/*----------------------------------------------------------------------------- + * + * Function: test_whole_history_encode_decode_empty() + * + * Purpose: Verify onion whole-history encoding and decoding behavior. + * Tests the case of the "empty" whole-history. + * Verifies behavior in standard error cases. + * + * Return: PASSED : 0 + * FAILED : -1 + * + *----------------------------------------------------------------------------- + */ +static int +test_whole_history_encode_decode_empty(void) +{ + unsigned char buf[32]; + unsigned char exp[32] = { + 'O', 'W', 'H', 'S', /* NOTE: match signature define in onion_priv.h */ + 1, 0, 0, 0, /* NOTE: update version w/ "current" as needed */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 /* sum populated below */ + }; + unsigned char *ptr = NULL; + uint32_t sum = 0; + uint32_t sum_out = 0; + size_t i = 0; + uint64_t size_ret = 0; + struct H5FD__onion_whole_history whs = { + H5FD__ONION_WHOLE_HISTORY_MAGIC, + H5FD__ONION_WHOLE_HISTORY_VERSION_CURR, + 0, /* n_revisions */ + NULL, /* list */ + 0, /* checksum */ + }; + struct H5FD__onion_whole_history whs_out = { + H5FD__ONION_WHOLE_HISTORY_MAGIC, + H5FD__ONION_WHOLE_HISTORY_VERSION_CURR, + 0, /* n_revisions */ + NULL, /* list */ + 0, /* checksum */ + }; + + TESTING("encode/decode whole-history (empty and failures)"); + + /* Generage checksum but don't store it yet */ + sum = H5_checksum_fletcher32(exp, \ + H5FD__ONION_ENCODED_SIZE_WHOLE_HISTORY - 4); + ptr = exp + H5FD__ONION_ENCODED_SIZE_WHOLE_HISTORY - 4; + UINT32ENCODE(ptr, sum); + + if (H5FD_onion_whole_history_encode(&whs, buf, &sum_out) \ + != H5FD__ONION_ENCODED_SIZE_WHOLE_HISTORY) + TEST_ERROR; + for (i = 0; i < 20; i++) { + if (exp[i] != buf[i]) { + HDprintf("first mismatched byte at %llu: %02x %02x\n", i, exp[i], \ + buf[i]); + TEST_ERROR; + } + } + if (sum != sum_out) + TEST_ERROR; + whs.checksum = sum; /* set to compare later */ + + /* Invalid signature prevents decoding. + */ + + exp[3] = 'X'; /* invalidate encoded signature */ + H5E_BEGIN_TRY { + size_ret = H5FD_onion_whole_history_decode(exp, &whs_out); + } H5E_END_TRY; + if (0 != size_ret) + TEST_ERROR; + + exp[3] = 'H'; /* reset */ + + /* Invalid version prevents decoding. + */ + + exp[4] = 0; /* encoded version 0?!? */ + H5E_BEGIN_TRY { + size_ret = H5FD_onion_whole_history_decode(exp, &whs_out); + } H5E_END_TRY; + if (0 != size_ret) + TEST_ERROR; + + exp[4] = H5FD__ONION_WHOLE_HISTORY_VERSION_CURR + 1; + H5E_BEGIN_TRY { + size_ret = H5FD_onion_whole_history_decode(exp, &whs_out); + } H5E_END_TRY; + if (0 != size_ret) + TEST_ERROR; + + exp[4] = H5FD__ONION_WHOLE_HISTORY_VERSION_CURR; /* reset */ + + /* Valid summary can be decoded. + */ + + if (H5FD_onion_whole_history_decode(buf, &whs_out) \ + != H5FD__ONION_ENCODED_SIZE_WHOLE_HISTORY) + TEST_ERROR; + if (H5FD__ONION_WHOLE_HISTORY_MAGIC != whs_out.magic) + TEST_ERROR; + if (H5FD__ONION_WHOLE_HISTORY_VERSION_CURR != whs_out.version) + TEST_ERROR; + if (whs.n_revisions != whs_out.n_revisions) + TEST_ERROR; + if (whs.checksum != whs_out.checksum) + TEST_ERROR; + if (NULL != whs_out.record_pointer_list) + TEST_ERROR; + + PASSED(); + return 0; + +error: + return -1; +} /* end test_whole_history_encode_decode_empty() */ + +/*----------------------------------------------------------------------------- + * + * Function: test_whole_history_encode_decode() + * + * Purpose: Verify onion whole-history encoding and decoding behavior. + * Encode/decode with some set of revision record pointers. + * + * Return: PASSED : 0 + * FAILED : -1 + * + *----------------------------------------------------------------------------- + */ +static int +test_whole_history_encode_decode(void) +{ + unsigned char *buf = NULL; + unsigned char exp[80] = { + 'O', 'W', 'H', 'S', /* NOTE: match signature define in onion_priv.h */ + 1, 0, 0, 0, /* NOTE: update version w/ "current" as needed */ + 3, 0, 0, 0, 0, 0, 0, 0, + /* rev0 pointer */ + 56, 2, 0, 0, 0, 0, 0, 0, + 238, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, /* sum populated below */ + /* rev1 pointer */ + 121, 173, 3, 0, 0, 0, 0, 0, + 203, 17, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, /* sum populated below */ + /* rev2 pointer */ + 96, 158, 52, 198, 213, 0, 0, 0, + 240, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, /* sum populated below */ + /* final checksum */ + 0, 0, 0, 0 /* sum populated below */ + }; + unsigned char *buf_p = NULL; + uint32_t sum_out = 0; + size_t i = 0; + struct H5FD__onion_whole_history whs = { + H5FD__ONION_WHOLE_HISTORY_MAGIC, + H5FD__ONION_WHOLE_HISTORY_VERSION_CURR, + 3, /* n_revisions */ + NULL, /* list set below */ + 0, /* checksum not set by us */ + }; + struct H5FD__onion_whole_history whs_out = { + H5FD__ONION_WHOLE_HISTORY_MAGIC, + H5FD__ONION_WHOLE_HISTORY_VERSION_CURR, + 0, /* n_revisions must start as zero */ + NULL, /* list */ + 0, /* checksum */ + }; + uint64_t exp_size = H5FD__ONION_ENCODED_SIZE_WHOLE_HISTORY + \ + H5FD__ONION_ENCODED_SIZE_RECORD_POINTER * whs.n_revisions; + + TESTING("encode/decode whole-history"); + + if (80 != exp_size) + TEST_ERROR; + + whs.record_pointer_list = (struct H5FD__onion_record_pointer *)HDcalloc( \ + whs.n_revisions, sizeof(struct H5FD__onion_record_pointer)); + if (NULL == whs.record_pointer_list) + TEST_ERROR; + + /* must match values in exp */ + whs.record_pointer_list[0].phys_addr = 568ull; + whs.record_pointer_list[0].record_size = 238ull; + whs.record_pointer_list[1].phys_addr = 241017ull; + whs.record_pointer_list[1].record_size = 4555ull; + whs.record_pointer_list[2].phys_addr = 918153371232ull; + whs.record_pointer_list[2].record_size = 240ull; + + /* populate revision pointer sums in exp */ + for (i = 0; i < whs.n_revisions; i++) { + uint64_t whs_pre = H5FD__ONION_ENCODED_SIZE_WHOLE_HISTORY - 4; + uint64_t ptr_pre = H5FD__ONION_ENCODED_SIZE_RECORD_POINTER - 4; + uint64_t ptr_size = H5FD__ONION_ENCODED_SIZE_RECORD_POINTER; + + buf_p = exp + whs_pre + ptr_size * i; + whs.record_pointer_list[i].checksum = H5_checksum_fletcher32(buf_p, \ + ptr_pre); + buf_p += ptr_pre; + UINT32ENCODE(buf_p, whs.record_pointer_list[i].checksum); + } + + /* compute, populate, and store exp final sum */ + whs.checksum = H5_checksum_fletcher32(exp, exp_size - 4); + buf_p = exp + exp_size - 4; + UINT32ENCODE(buf_p, whs.checksum); + + buf = (unsigned char *)HDmalloc(exp_size); + if (NULL == buf) + TEST_ERROR; + + if (H5FD_onion_whole_history_encode(&whs, buf, &sum_out) != exp_size) + TEST_ERROR; + for (i = 0; i < exp_size; i++) { + if (exp[i] != buf[i]) + TEST_ERROR; + } + if (whs.checksum != sum_out) + TEST_ERROR; + + /* Initial decode, gets always-present components. + */ + + whs_out.n_revisions = 0; /* must be initialized to 0 */ + if (H5FD_onion_whole_history_decode(exp, &whs_out) != exp_size) + TEST_ERROR; + if (H5FD__ONION_WHOLE_HISTORY_MAGIC != whs_out.magic) + TEST_ERROR; + if (H5FD__ONION_WHOLE_HISTORY_VERSION_CURR != whs_out.version) + TEST_ERROR; + if (whs.n_revisions != whs_out.n_revisions) + TEST_ERROR; + if (NULL != whs_out.record_pointer_list) + TEST_ERROR; /* Must be created by us */ + + /* True decode requires allocating space for record pointers + */ + + whs_out.record_pointer_list = (struct H5FD__onion_record_pointer *) \ + HDcalloc(whs_out.n_revisions, \ + sizeof(struct H5FD__onion_record_pointer)); + if (NULL == whs_out.record_pointer_list) + TEST_ERROR; + + if (H5FD_onion_whole_history_decode(exp, &whs_out) != exp_size) + TEST_ERROR; + if (H5FD__ONION_WHOLE_HISTORY_MAGIC != whs_out.magic) + TEST_ERROR; + if (H5FD__ONION_WHOLE_HISTORY_VERSION_CURR != whs_out.version) + TEST_ERROR; + if (whs.n_revisions != whs_out.n_revisions) + TEST_ERROR; + if (whs.checksum != whs_out.checksum) + TEST_ERROR; + if (NULL == whs_out.record_pointer_list) + TEST_ERROR; + for (i = 0; i < whs.n_revisions; i++) { + struct H5FD__onion_record_pointer exp_rp = whs.record_pointer_list[i]; + struct H5FD__onion_record_pointer act_rp = \ + whs_out.record_pointer_list[i]; + + if (exp_rp.phys_addr != act_rp.phys_addr) + TEST_ERROR; + if (exp_rp.record_size != act_rp.record_size) + TEST_ERROR; + if (exp_rp.checksum != act_rp.checksum) + TEST_ERROR; + } + + HDfree(whs_out.record_pointer_list); + whs_out.record_pointer_list = NULL; + + HDfree(buf); + buf = NULL; + + HDfree(whs.record_pointer_list); + whs.record_pointer_list = NULL; + + PASSED(); + return 0; + +error: + if (whs_out.record_pointer_list != NULL) + HDfree(whs_out.record_pointer_list); + if (buf != NULL) + HDfree(buf); + if (whs.record_pointer_list != NULL) + HDfree(whs.record_pointer_list); + return -1; +} /* end test_whole_history_encode_decode() */ + +/*----------------------------------------------------------------------------- + * + * Function: test_revision_record_encode_decode() + * + * Purpose: Verify onion revision-record encoding and decoding behavior. + * + * Return: PASSED : 0 + * FAILED : -1 + * + *----------------------------------------------------------------------------- + */ +static int +test_revision_record_encode_decode(void) +{ + unsigned char *buf = NULL; + unsigned char exp[189] = { + 'O', 'R', 'R', 'S', /* signature */ + 1, 0, 0, 0, /* NOTE: update version w/ "current" as needed */ + 5, 0, 0, 0, 0, 0, 0, 0, /* revision ID */ + 2, 0, 0, 0, 0, 0, 0, 0, /* parent revision ID */ + '1', '9', '4', '1', '1', '2', '0', '7', /* Time of Creation */ + 'T', '1', '9', '0', '6', '4', '3', 'Z', /* ... */ + 0x11, 0x00, 0, 0, 0x02, 0, 0, 0, /* logical file size */ + 0, 16, 0, 0, /* page size */ + 143, 25, 0, 0, /* user ID */ + 4, 0, 0, 0, 0, 0, 0, 0, /* n_entries */ + 8, 0, 0, 0, /* username size */ + 25, 0, 0, 0, /* comment size */ + /* entry0 pointer */ + 0, 0xB0, 0x1E, 0, 0, 0, 0, 0, /* logical offset */ + 0x4B, 0x02, 0, 0, 0, 0, 0, 0, /* physical address */ + 0, 0, 0, 0, /* sum populated below */ /* checksum */ + /* entry1 pointer */ + 0, 0xF0, 0x2E, 0, 0, 0, 0, 0, + 0xA7, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, /* sum populated below */ + /* entry2 pointer */ + 0, 0x50, 0x15, 0, 0, 0x20, 0, 0, + 0x11, 0, 0, 0, 0x02, 0, 0, 0, + 0, 0, 0, 0, /* sum populated below */ + /* entry3 pointer */ + 0, 0xE0, 0x24, 0, 0, 0, 0, 0, + 0xB1, 0x01, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, /* sum populated below */ + 'J', 'o', 'h', 'n', 'D', 'o', 'e', '\0',/* username */ + 'E', 'x', 'a', 'm', 'p', 'l', 'e', ' ', /* comment */ + 'c', 'o', 'm', 'm', 'e', 'n', 't', ' ', /* ... */ + 'm', 'e', 's', 's', 'a', 'g', 'e', '.', /* ... */ + '\0', /* ... */ + /* final checksum */ + 0, 0, 0, 0 /* sum populated below */ /* checksum */ + }; + unsigned char *buf_p = NULL; + size_t i = 0; + uint64_t size_ret; + struct H5FD__onion_revision_record r_out; + uint32_t sum_out = 0; + struct H5FD__onion_revision_record record = { + H5FD__ONION_REVISION_RECORD_MAGIC, + H5FD__ONION_REVISION_RECORD_VERSION_CURR, + 5, /* revision ID */ + 2, /* parent revision ID */ + {'\0'}, /* time of creation - populated below */ + 8589934609ull, /* logical file size */ + 6543, /* user ID */ + 8, /* username size */ + 25, /* comment size */ + { H5FD__ONION_ARCHIVAL_INDEX_MAGIC, + H5FD__ONION_ARCHIVAL_INDEX_VERSION_CURR, + 12, /* page_size_log2 */ + 4, /* n_entries */ + NULL, /* list - populated below */ + }, /* archival index struct */ + (char *)"JohnDoe", /* username */ /* cast OK --JOS */ + (char *)"Example comment message.", /* comment */ /* cast OK --JOS */ + 0, /* checksum - computed for us */ + }; + uint64_t exp_size = H5FD__ONION_ENCODED_SIZE_REVISION_RECORD \ + + (H5FD__ONION_ENCODED_SIZE_INDEX_ENTRY \ + * record.archival_index.n_entries) \ + + strlen("JohnDoe") + 1 \ + + strlen("Example comment message.") + 1; + + r_out.archival_index.list = NULL; + r_out.comment = NULL; + r_out.username = NULL; + + + TESTING("encode/decode revision record"); + + if (189 != exp_size) + TEST_ERROR; + + HDmemcpy(record.time_of_creation, "19411207T190643Z", 16); + record.archival_index.list = (struct H5FD__onion_index_entry *)HDcalloc( \ + record.archival_index.n_entries, \ + sizeof(struct H5FD__onion_index_entry)); + if (NULL == record.archival_index.list) + TEST_ERROR; + /* convert logi_page and should match address in expected buffer */ + record.archival_index.list[0].logi_page = 491ull; + record.archival_index.list[0].phys_addr = 587ull; + record.archival_index.list[1].logi_page = 751ull; + record.archival_index.list[1].phys_addr = 167ull; + record.archival_index.list[2].logi_page = 8589934933ull; + record.archival_index.list[2].phys_addr = 8589934609ull; + record.archival_index.list[3].logi_page = 590ull; + record.archival_index.list[3].phys_addr = 433ull; + + /* Set expected checksum for each archival index entry in buffer */ + for (i = 0; i < record.archival_index.n_entries; i ++) { + uint64_t rec_pre = H5FD__ONION_ENCODED_SIZE_REVISION_RECORD - 4; + uint64_t idx_pre = H5FD__ONION_ENCODED_SIZE_INDEX_ENTRY - 4; + uint64_t idx_size = H5FD__ONION_ENCODED_SIZE_INDEX_ENTRY; + + buf_p = exp + rec_pre + idx_size * i; + sum_out = H5_checksum_fletcher32(buf_p, idx_pre); + buf_p += idx_pre; + UINT32ENCODE(buf_p, sum_out); + } + + sum_out = 0; + + record.checksum = H5_checksum_fletcher32(exp, exp_size - 4); + buf_p = exp + exp_size - 4; + UINT32ENCODE(buf_p, record.checksum); + + /* required initialization for record-out structure */ + r_out.magic = H5FD__ONION_REVISION_RECORD_MAGIC; + r_out.version = H5FD__ONION_REVISION_RECORD_VERSION_CURR; + r_out.username_size = 0; + r_out.comment_size = 0; + r_out.username = NULL; + r_out.comment = NULL; + r_out.archival_index.magic = H5FD__ONION_ARCHIVAL_INDEX_MAGIC; + r_out.archival_index.version = H5FD__ONION_ARCHIVAL_INDEX_VERSION_CURR; + r_out.archival_index.n_entries = 0; + r_out.archival_index.list = NULL; + + buf = (unsigned char *)HDmalloc(sizeof(unsigned char) * exp_size); + if (NULL == buf) + TEST_ERROR; + + /* Test encode + */ + + if (H5FD_onion_revision_record_encode(&record, buf, &sum_out) != exp_size) + TEST_ERROR; + for (i = 0; i < exp_size; i++) { + if (exp[i] != buf[i]) + TEST_ERROR; + } + if (record.checksum != sum_out) + TEST_ERROR; + + HDfree(buf); + buf = NULL; + + /* Test decode (malformed encoding) + */ + + /* invalid signature */ + exp[2] = 'Y'; + H5E_BEGIN_TRY { + size_ret = H5FD_onion_revision_record_decode(exp, &r_out); + } H5E_END_TRY; + if (0 != size_ret) + TEST_ERROR; + exp[2] = 'R'; /* reset */ + + /* zero version */ + exp[4] = 0; + H5E_BEGIN_TRY { + size_ret = H5FD_onion_revision_record_decode(exp, &r_out); + } H5E_END_TRY; + if (0 != size_ret) + TEST_ERROR; + + /* advance version */ + exp[4] = H5FD__ONION_REVISION_RECORD_VERSION_CURR + 1; + H5E_BEGIN_TRY { + size_ret = H5FD_onion_revision_record_decode(exp, &r_out); + } H5E_END_TRY; + if (0 != size_ret) + TEST_ERROR; + exp[4] = H5FD__ONION_REVISION_RECORD_VERSION_CURR; /* reset */ + + /* Test successful decode + */ + + /* Initial decode; get variable-length component sizes */ + if (H5FD_onion_revision_record_decode(exp, &r_out) != exp_size) + TEST_ERROR; + if (record.username_size != r_out.username_size) + TEST_ERROR; + if (record.comment_size != r_out.comment_size) + TEST_ERROR; + if (record.archival_index.n_entries != r_out.archival_index.n_entries) + TEST_ERROR; + + /* Allocate variable-length components */ + r_out.username = (char *)HDcalloc(r_out.username_size, sizeof(char)); + if (NULL == r_out.username) + TEST_ERROR; + r_out.comment = (char *)HDcalloc(r_out.comment_size, sizeof(char)); + if (NULL == r_out.comment) + TEST_ERROR; + r_out.archival_index.list = (struct H5FD__onion_index_entry *)HDcalloc( \ + r_out.archival_index.n_entries, \ + sizeof(struct H5FD__onion_index_entry)); + if (NULL == r_out.archival_index.list) + TEST_ERROR; + + /* Decode into all components */ + if (H5FD_onion_revision_record_decode(exp, &r_out) != exp_size) + TEST_ERROR; + if (H5FD__ONION_REVISION_RECORD_MAGIC != r_out.magic) + TEST_ERROR; + if (H5FD__ONION_REVISION_RECORD_VERSION_CURR != r_out.version) + TEST_ERROR; + if (record.user_id != r_out.user_id) + TEST_ERROR; + if (record.revision_id != r_out.revision_id) + TEST_ERROR; + if (record.parent_revision_id != r_out.parent_revision_id) + TEST_ERROR; + if (record.parent_revision_id != r_out.parent_revision_id) + TEST_ERROR; + if (record.checksum != r_out.checksum) + TEST_ERROR; + if (HDstrncmp(record.time_of_creation, r_out.time_of_creation, 16) != 0) + TEST_ERROR; + if (record.username_size != r_out.username_size) + TEST_ERROR; + if (record.username_size != HDstrlen(r_out.username) + 1) + TEST_ERROR; + if (HDstrlen(record.username) != HDstrlen(r_out.username)) + TEST_ERROR; + if (HDstrcmp(record.username, r_out.username) != 0) + TEST_ERROR; + if (record.comment_size != r_out.comment_size) + TEST_ERROR; + if (record.comment_size != HDstrlen(r_out.comment) + 1) + TEST_ERROR; + if (HDstrlen(record.comment) != HDstrlen(r_out.comment)) + TEST_ERROR; + if (HDstrcmp(record.comment, r_out.comment) != 0) + TEST_ERROR; + + if (H5FD__ONION_ARCHIVAL_INDEX_MAGIC != r_out.archival_index.magic) + TEST_ERROR; + if (H5FD__ONION_ARCHIVAL_INDEX_VERSION_CURR != \ + r_out.archival_index.version) + TEST_ERROR; + if (record.archival_index.page_size_log2 != \ + r_out.archival_index.page_size_log2) + TEST_ERROR; + if (record.archival_index.n_entries != r_out.archival_index.n_entries) + TEST_ERROR; + for (i = 0; i < record.archival_index.n_entries; i++) { + struct H5FD__onion_index_entry *ep = &record.archival_index.list[i]; + struct H5FD__onion_index_entry *ap = &r_out.archival_index.list[i]; + + if (ep->phys_addr != ap->phys_addr) + TEST_ERROR; + if (ep->logi_page != ap->logi_page) + TEST_ERROR; + } + + /* Cleanup + */ + + HDfree(r_out.archival_index.list); + r_out.archival_index.list = NULL; + + HDfree(r_out.comment); + r_out.comment = NULL; + + HDfree(r_out.username); + r_out.username = NULL; + + HDfree(record.archival_index.list); + record.archival_index.list = NULL; + + PASSED(); + return 0; + +error: + if (r_out.archival_index.list != NULL) + HDfree(r_out.archival_index.list); + if (r_out.comment != NULL) + HDfree(r_out.comment); + if (r_out.username != NULL) + HDfree(r_out.username); + if (buf != NULL) + HDfree(buf); + if (record.archival_index.list != NULL) + HDfree(record.archival_index.list); + return -1; +} /* end test_revision_record_encode_decode() */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Use VFL to open target file and check that its bytes exactly match those + * of given buffer 'exp'[ected]. + * + * Returns 0 if successful, -1 if error or mismatch. + *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + */ +static int +compare_file_bytes_exactly(const char *filepath, hid_t fapl_id, size_t nbytes, + const unsigned char *exp) +{ + H5FD_t *raw_vfile = NULL; /* virtual file to look at raw file contents */ + unsigned char *act_buf = NULL; /* allocated area for actual file bytes */ + size_t i = 0; + uint64_t filesize = 0; + + raw_vfile = H5FDopen(filepath, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF); + if (NULL == raw_vfile) + TEST_ERROR; + + filesize = (uint64_t)H5FDget_eof(raw_vfile, H5FD_MEM_DRAW); + if ((uint64_t)nbytes != filesize) + TEST_ERROR; + + act_buf = (unsigned char *)HDmalloc(nbytes); + if (NULL == act_buf) + TEST_ERROR; + for (i = 0; i < nbytes; i++) + act_buf[i] = (unsigned char)(-1); /* fill with bogus all-1s */ + if (H5FDset_eoa(raw_vfile, H5FD_MEM_DRAW, nbytes) < 0) + TEST_ERROR; + if (H5FDread(raw_vfile, H5FD_MEM_DRAW, H5P_DEFAULT, 0, nbytes, act_buf) + < 0) + TEST_ERROR; + + /* compare raw bytes data */ + for (i = 0; i < nbytes; i++) { + if (exp[i] != act_buf[i]) { + HDprintf("first mismatched byte %llu: expected 0x%02X was 0x%02X\n", + i, exp[i], act_buf[i]); + TEST_ERROR; + } + } + + if (H5FDclose(raw_vfile) < 0) + TEST_ERROR; + raw_vfile = NULL; + + HDfree(act_buf); + act_buf = NULL; + + return 0; + +error: + if (act_buf != NULL) + HDfree(act_buf); + if (raw_vfile != NULL) + H5FDclose(raw_vfile); + return -1; +} /* end compare_file_bytes_exactly() */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Do a manual read of the onion history (separate, single "Onion" file). + * Verify that the history data is well-formed and matches the expected state. + * + * Inspect file contents on backing store. + * Return -1 on problem, 0 if okay. + *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + */ +static int +verify_history_as_expected_onion(H5FD_t *raw_file, \ + struct expected_history *filter) +{ + unsigned char *buf = NULL; /* allocated area for actual file bytes */ + struct H5FD__onion_history_header hdr_out; + struct H5FD__onion_whole_history whs_out; + struct H5FD__onion_revision_record rev_out; + uint64_t filesize = 0; + uint64_t readsize = 0; + size_t i = 0; + + hdr_out.magic = H5FD__ONION_HEADER_MAGIC; + hdr_out.version = H5FD__ONION_HEADER_VERSION_CURR; + + whs_out.magic = H5FD__ONION_WHOLE_HISTORY_MAGIC; + whs_out.version = H5FD__ONION_WHOLE_HISTORY_VERSION_CURR; + whs_out.n_revisions = 0; + whs_out.record_pointer_list = NULL; + + rev_out.magic = H5FD__ONION_REVISION_RECORD_MAGIC; + rev_out.version = H5FD__ONION_REVISION_RECORD_VERSION_CURR; + rev_out.archival_index.magic = H5FD__ONION_ARCHIVAL_INDEX_MAGIC; + rev_out.archival_index.version = H5FD__ONION_ARCHIVAL_INDEX_VERSION_CURR; + + + filesize = (uint64_t)H5FDget_eof(raw_file, H5FD_MEM_DRAW); + if (H5FDset_eoa(raw_file, H5FD_MEM_DRAW, filesize) < 0) + TEST_ERROR; + + /* Injest onion header. + */ + + readsize = MIN(filesize, H5FD__ONION_ENCODED_SIZE_HEADER); + buf = (unsigned char *)HDmalloc(sizeof(unsigned char) * readsize); + if (NULL == buf) + TEST_ERROR; + if (H5FDread(raw_file, H5FD_MEM_DRAW, H5P_DEFAULT, 0, readsize, buf) < 0) + TEST_ERROR; + + readsize = H5FD_onion_history_header_decode(buf, &hdr_out); + if (0 == readsize) + TEST_ERROR; + if (H5FD__ONION_HEADER_VERSION_CURR != hdr_out.version) + TEST_ERROR; + if (HDmemcmp(&hdr_out.checksum, &buf[readsize - 4], 4) != 0) + TEST_ERROR; + if (hdr_out.checksum != H5_checksum_fletcher32(buf, readsize - 4)) + TEST_ERROR; + if (filter->page_size != hdr_out.page_size) + TEST_ERROR; + if (hdr_out.whole_history_addr + hdr_out.whole_history_size != filesize) + TEST_ERROR; + if (filter->origin_eof != hdr_out.origin_eof) + TEST_ERROR; + + HDfree(buf); + buf = NULL; + + /* Injest whole-history. + */ + + readsize = hdr_out.whole_history_size; + buf = (unsigned char *)HDmalloc(sizeof(unsigned char) * readsize); + if (NULL == buf) + TEST_ERROR; + if (H5FDread(raw_file, H5FD_MEM_DRAW, H5P_DEFAULT, \ + hdr_out.whole_history_addr, readsize, buf) < 0) + TEST_ERROR; + + /* Initial read, get count of revisions */ + readsize = H5FD_onion_whole_history_decode(buf, &whs_out); + if (0 == readsize) + TEST_ERROR; + if (H5FD__ONION_WHOLE_HISTORY_VERSION_CURR != whs_out.version) + TEST_ERROR; + if (HDmemcmp(&whs_out.checksum, &buf[readsize - 4], 4) != 0) + TEST_ERROR; + if (whs_out.checksum != H5_checksum_fletcher32(buf, readsize - 4)) + TEST_ERROR; + if (filter->n_revisions != whs_out.n_revisions) + TEST_ERROR; + + /* Final read, populate pointers to revision records */ + whs_out.record_pointer_list = (struct H5FD__onion_record_pointer *) \ + HDcalloc(whs_out.n_revisions, \ + sizeof(struct H5FD__onion_record_pointer)); + if (NULL == whs_out.record_pointer_list) + TEST_ERROR; + if (H5FD_onion_whole_history_decode(buf, &whs_out) != readsize) + TEST_ERROR; + + /* Re-use buffer space to sanity-check checksum for record pointer(s). */ + HDassert(readsize >= sizeof(struct H5FD__onion_record_pointer)); + for (i = 0; i < whs_out.n_revisions; i++) { +#if 0 + uint32_t sum = 0; +#endif + + HDmemcpy(buf, &whs_out.record_pointer_list[i].phys_addr, 8); + HDmemcpy(buf + 8, &whs_out.record_pointer_list[i].record_size, 8); +#if 0 + sum = H5_checksum_fletcher32(buf, 16); +#endif + if (whs_out.record_pointer_list[i].checksum != \ + H5_checksum_fletcher32(buf, 16)) + TEST_ERROR; + } + + HDfree(buf); + buf = NULL; + + /* Injest revision(s). + */ + + for (i = 0; i < whs_out.n_revisions; i++) { + struct H5FD__onion_record_pointer *rpp = \ + &whs_out.record_pointer_list[i]; + struct expected_revision *erp = &filter->revisions[i]; + + rev_out.archival_index.list = NULL; + rev_out.archival_index.n_entries = 0; + rev_out.archival_index.page_size_log2 = 0; + rev_out.comment_size = 0; + rev_out.comment = NULL; + rev_out.username_size = 0; + rev_out.username = NULL; + + readsize = rpp->record_size; + buf = (unsigned char *)HDmalloc((size_t)rpp->record_size); + if (NULL == buf) + TEST_ERROR; + if (H5FDread(raw_file, H5FD_MEM_DRAW, H5P_DEFAULT, rpp->phys_addr, \ + rpp->record_size, buf) < 0) + TEST_ERROR; + + /* Initial revision read -- get fixed components */ + readsize = H5FD_onion_revision_record_decode(buf, &rev_out); + if (0 == readsize) + TEST_ERROR; + if (rpp->record_size != readsize) + TEST_ERROR; + if (H5FD__ONION_REVISION_RECORD_MAGIC != rev_out.magic) + TEST_ERROR; + if (H5FD__ONION_REVISION_RECORD_VERSION_CURR != rev_out.version) + TEST_ERROR; + if (HDmemcmp(&rev_out.checksum, &buf[readsize - 4], 4) != 0) + TEST_ERROR; + if (rev_out.checksum != H5_checksum_fletcher32(buf, readsize - 4)) + TEST_ERROR; + if (erp->revision_id != rev_out.revision_id) + TEST_ERROR; + if (erp->parent_revision_id != rev_out.parent_revision_id) + TEST_ERROR; + if (erp->logi_eof != rev_out.logi_eof) + TEST_ERROR; + + /* Final read, get variable-length data */ + rev_out.comment = (char *)HDmalloc((size_t)rev_out.comment_size); + if (NULL == rev_out.comment) + TEST_ERROR; + rev_out.archival_index.list = \ + (struct H5FD__onion_index_entry *)HDcalloc( \ + rev_out.archival_index.n_entries, \ + sizeof(struct H5FD__onion_index_entry)); + if (NULL == rev_out.archival_index.list) + TEST_ERROR; + rev_out.username = (char *)HDmalloc((size_t)rev_out.username_size); + if (NULL == rev_out.username) + TEST_ERROR; + + readsize = H5FD_onion_revision_record_decode(buf, &rev_out); + if (rpp->record_size != readsize) + TEST_ERROR; + + /* Compare revision info with expected filter */ + if (erp->comment == NULL) { + if (rev_out.comment_size != 0) + TEST_ERROR; + } + else { + if (HDstrlen(rev_out.comment) != HDstrlen(erp->comment)) + TEST_ERROR; + if (HDstrcmp(rev_out.comment, erp->comment) != 0) + TEST_ERROR; + } + if (erp->n_index_entries != (uint64_t)(-1) \ + && erp->n_index_entries != rev_out.archival_index.n_entries) + TEST_ERROR; + + + HDfree(buf); + buf = NULL; + + HDfree(rev_out.comment); + rev_out.comment = NULL; + + HDfree(rev_out.archival_index.list); + rev_out.archival_index.list = NULL; + + HDfree(rev_out.username); + rev_out.username = NULL; + } + + HDfree(whs_out.record_pointer_list); + whs_out.record_pointer_list = NULL; + + return 0; + +error: + if (buf != NULL) + HDfree(buf); + if (rev_out.comment != NULL) + HDfree(rev_out.comment); + if (rev_out.archival_index.list != NULL) + HDfree(rev_out.archival_index.list); + if (rev_out.username != NULL) + HDfree(rev_out.username); + if (whs_out.record_pointer_list != NULL) + HDfree(whs_out.record_pointer_list); + + return -1; + +} /* end verify_history_as_expected_onion() */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Verify file bytes on the backing store + * + onion storage target + * + create from nothing + * + stage 0 (initializing) + * + open (not yet written) + * + "Empty" .h5 file created + * + .onion file created w/ only header (0 whole-hist addr) + * + .onion.recovery created w/ "empty" whole-history + * + Cannot open onionized canonical file (incomplete history, no rev) + * + * Inspect file contents on backing store. + * Return -1 on problem, 0 if okay. + *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + */ +static int +verify_stored_onion_create_0_open(struct onion_filepaths *paths, + H5FD_onion_fapl_info_t *onion_info) +{ + H5FD_t *file = NULL; /* virtual file to look at raw file contents */ + unsigned char *act_buf = NULL; /* allocated area for actual file bytes */ + hid_t fapl_id = onion_info->backing_fapl_id; +#if 0 + herr_t err_ret = FAIL; +#endif + unsigned char hdr_exp_bytes[] = { + 'O', 'H', 'D', 'H', + 1, 1, 0, 0, + 0, 0, 0, 0, /* page-size encoded below */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 20, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, /* checksum encoded below */ + }; + size_t whs_exp_bytes_size = 20; + unsigned char whs_exp_bytes[] = { + 'O', 'W', 'H', 'S', + 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 /* checksum encoded below */ + }; + unsigned char *ptr = NULL; + uint32_t sum = 0; + hid_t onion_fapl_id = H5I_INVALID_HID; + + onion_fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == onion_fapl_id) + TEST_ERROR + if (H5Pset_fapl_onion(onion_fapl_id, onion_info) < 0) + TEST_ERROR + + /* Finish populating expected header bytes + */ + ptr = hdr_exp_bytes + 8; /* WARNING: must match format */ + UINT32ENCODE(ptr, onion_info->page_size); + sum = H5_checksum_fletcher32(hdr_exp_bytes, \ + H5FD__ONION_ENCODED_SIZE_HEADER - 4); + ptr = hdr_exp_bytes + H5FD__ONION_ENCODED_SIZE_HEADER - 4; + UINT32ENCODE(ptr, sum); + ptr = NULL; + + /* Finish populating expected whole-history bytes + */ + sum = H5_checksum_fletcher32(whs_exp_bytes, \ + H5FD__ONION_ENCODED_SIZE_WHOLE_HISTORY - 4); + ptr = whs_exp_bytes + H5FD__ONION_ENCODED_SIZE_WHOLE_HISTORY - 4; + UINT32ENCODE(ptr, sum); + ptr = NULL; + +#if 0 /* TODO: fails because EOA not set, not because it's empty */ + /* Look at h5 file: should have zero bytes. + */ + + file = H5FDopen(paths->canon, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF); + if (NULL == file) + TEST_ERROR; + + act_buf = (unsigned char *)HDcalloc(1, 8); /* any size would do */ + if (NULL == act_buf) + TEST_ERROR; + + H5E_BEGIN_TRY { + err_ret = H5FDread(file, H5FD_MEM_DRAW, H5P_DEFAULT, 0, 1, act_buf); + } H5E_END_TRY; + if (err_ret != FAIL) + TEST_ERROR; /* cannot read from empty file */ + + HDfree(act_buf); + act_buf = NULL; + + if (H5FDclose(file) < 0) + TEST_ERROR; + file = NULL; +#endif /* TODO */ + + /* Look at onion file: should have header. + */ + + if (compare_file_bytes_exactly(paths->onion, fapl_id, \ + H5FD__ONION_ENCODED_SIZE_HEADER, hdr_exp_bytes) < 0) + TEST_ERROR; + + /* Look at history backing file: should have nascent whole-history. + */ + + if (compare_file_bytes_exactly(paths->recovery, fapl_id, \ + whs_exp_bytes_size, whs_exp_bytes) < 0) + TEST_ERROR; + + /* Inspect .h5 file contents + */ + + if (compare_file_bytes_exactly(paths->canon, fapl_id, 8, \ + (const unsigned char *)"ONIONEOF") < 0) + { + TEST_ERROR; + } + + if (H5Pclose(onion_fapl_id) < 0) + TEST_ERROR; + onion_fapl_id = H5I_INVALID_HID; + + return 0; + +error: + if (file != NULL) + (void)H5FDclose(file); + if (act_buf != NULL) + HDfree(act_buf); + if (onion_fapl_id != H5I_INVALID_HID) + (void)H5Pclose(onion_fapl_id); + + return -1; +} /* end verify_stored_onion_create_0_open() */ + +/*----------------------------------------------------------------------------- + * + * Function: test_create_oniontarget() + * + * Purpose: Test the ability of the Onion VFD to create a valid + * 'onionized' file. + * + * When `truncate_canonical` is FALSE, the canonical file is + * nonexistent on the backing store on onion-creation. + * When `truncate_canonical` is TRUE, a canonical file is created + * on the backing store with known contents, which are to be + * truncated on onion-creation. + * + * Return: PASSED : 0 + * FAILED : -1 + * + *----------------------------------------------------------------------------- + */ +static int +test_create_oniontarget(hbool_t truncate_canonical, hbool_t with_initial_data) +{ + const char *basename = "somesuch"; + hid_t fapl_id = H5I_INVALID_HID; + struct onion_filepaths *paths = NULL; + H5FD_onion_fapl_info_t onion_info = { + H5FD_ONION_FAPL_INFO_MAGIC, + H5FD_ONION_FAPL_INFO_VERSION_CURR, + H5I_INVALID_HID, /* backing_fapl_id */ + ONION_TEST_PAGE_SIZE_5, /* page_size */ + H5FD_ONION_STORE_TARGET_ONION, /* store_target */ + H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST, + 0, /* force_write_open */ + 0, /* creation_flags */ + "initial commit" /* comment */ + }; + H5FD_t *vfile_raw = NULL; /* virtual file to look at raw file contents */ + H5FD_t *vfile_rw = NULL; /* Onion virtual file for read/write */ + H5FD_t *vfile_ro = NULL; /* Onion virtual file for read-only */ + struct expected_history filter; + char *buf = NULL; + + if (TRUE == truncate_canonical && TRUE == with_initial_data) { + TESTING("onion creation; truncate extant canonical; w/ initial data"); + } + else + if (TRUE == truncate_canonical) { + TESTING("onion creation; truncate extant canonical; no initial data"); + } + else + if (TRUE == with_initial_data) { + TESTING("onion creation; no extant canonical; w/ initial data"); + } + else { + TESTING("onion creation; no extant canonical; no initial data"); + } + + /********* + * SETUP * + *********/ + + onion_info.backing_fapl_id = h5_fileaccess(); + fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == fapl_id) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + paths = onion_filepaths_init(basename, &onion_info); + if (NULL == paths) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + + /* create canonical file to be truncated */ + if (TRUE == truncate_canonical) { + /* Create canonical file. */ + vfile_raw = H5FDopen(paths->canon, flags_create_s, \ + onion_info.backing_fapl_id, HADDR_UNDEF); + if (NULL == vfile_raw) + TEST_ERROR; + if (H5FDset_eoa(vfile_raw, H5FD_MEM_DRAW, b_list_size_s) < 0) + TEST_ERROR; + if (H5FDwrite(vfile_raw, H5FD_MEM_DRAW, H5P_DEFAULT, 0, \ + b_list_size_s, b_list_s) < 0) + { + TEST_ERROR; + } + if (H5FDclose(vfile_raw) < 0) + TEST_ERROR; + vfile_raw = NULL; + H5E_BEGIN_TRY { + vfile_raw = H5FDopen(paths->canon, H5F_ACC_RDONLY, fapl_id, \ + HADDR_UNDEF); + } H5E_END_TRY; + if (NULL != vfile_raw) + TEST_ERROR; /* no onion history to onion-open created file */ + + /* Create "existing onion file". */ + vfile_raw = H5FDopen(paths->onion, flags_create_s, \ + onion_info.backing_fapl_id, HADDR_UNDEF); + if (NULL == vfile_raw) + TEST_ERROR; + if (H5FDset_eoa(vfile_raw, H5FD_MEM_DRAW, b_list_size_s) < 0) + TEST_ERROR; + if (H5FDwrite(vfile_raw, H5FD_MEM_DRAW, H5P_DEFAULT, 0, \ + 23, "prior history stand-in") < 0) + { + TEST_ERROR; + } + if (H5FDclose(vfile_raw) < 0) + TEST_ERROR; + vfile_raw = NULL; + } /* end if to create canonical file for truncation */ + + /* + * OPENED + */ + + /* Begin creation of onionized file from nothing. + */ + + vfile_rw = H5FDopen(paths->canon, flags_create_s, fapl_id, HADDR_UNDEF); + if (NULL == vfile_rw) + TEST_ERROR; + + if (verify_stored_onion_create_0_open(paths, &onion_info) < 0) + TEST_ERROR + + H5E_BEGIN_TRY { + vfile_ro = H5FDopen(paths->canon, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF); + } H5E_END_TRY; + if (vfile_ro != NULL) + TEST_ERROR; /* onionization (creation) not complete; nothing to open */ + + /* + * WRITING + */ + + if (TRUE == with_initial_data) { + haddr_t half_size = 0; + haddr_t buf_size = 0; + + /* Write the sub-page entry at addr 0 */ + if (4 >= onion_info.page_size) + TEST_ERROR; + if (H5FDset_eoa(vfile_rw, H5FD_MEM_DRAW, 4) < 0) + TEST_ERROR; + if (H5FDwrite(vfile_rw, H5FD_MEM_DRAW, H5P_DEFAULT, 0, 4, a_list_s) \ + < 0) + { + TEST_ERROR; + } + + /* Verify logical file contents. */ + buf = (char *)HDmalloc(sizeof(char) * 4); + if (NULL == buf) + TEST_ERROR; + if (H5FDread(vfile_rw, H5FD_MEM_DRAW, H5P_DEFAULT, 0, 4, buf) < 0) { + TEST_ERROR; + } + if (HDmemcmp(a_list_s, buf, 4) != 0) + TEST_ERROR; + HDfree(buf); + buf = NULL; + + /* Write the latter half of buffer at addr 0 (more than one page) */ + half_size = a_list_size_s / 2; + buf_size = a_list_size_s - half_size; + if (buf_size <= onion_info.page_size) + TEST_ERROR; + if (H5FDset_eoa(vfile_rw, H5FD_MEM_DRAW, buf_size) < 0) + TEST_ERROR; + if (H5FDwrite(vfile_rw, H5FD_MEM_DRAW, H5P_DEFAULT, 0, buf_size, \ + a_list_s + half_size) < 0) + { + TEST_ERROR; + } + + /* Verify logical file contents. */ + buf = (char *)HDmalloc(sizeof(char) * buf_size); + if (NULL == buf) + TEST_ERROR; + if (H5FDread(vfile_rw, H5FD_MEM_DRAW, H5P_DEFAULT, 0, buf_size, buf) \ + < 0) + { + TEST_ERROR; + } + if (HDmemcmp(a_list_s + half_size, buf, buf_size) != 0) + TEST_ERROR; + HDfree(buf); + buf = NULL; + + /* Overwrite existing data with entire buffer at addr 0 */ + if (H5FDset_eoa(vfile_rw, H5FD_MEM_DRAW, a_list_size_s) < 0) + TEST_ERROR; + if (H5FDwrite(vfile_rw, H5FD_MEM_DRAW, H5P_DEFAULT, 0, a_list_size_s, \ + a_list_s) < 0) + { + TEST_ERROR; + } + + /* Verify logical file contents. */ + buf = (char *)HDmalloc(sizeof(char) * a_list_size_s); + if (NULL == buf) + TEST_ERROR; + if (H5FDread(vfile_rw, H5FD_MEM_DRAW, H5P_DEFAULT, 0, a_list_size_s, \ + buf) < 0) + { + TEST_ERROR; + } + if (HDmemcmp(a_list_s, buf, a_list_size_s) != 0) + TEST_ERROR; + HDfree(buf); + buf = NULL; + } /* end if writing data to logical file */ + + /* + * CLOSED + */ + + if (H5FDclose(vfile_rw) < 0) + TEST_ERROR; + vfile_rw = NULL; + + /* Look at h5 file: should be known-empty + */ + + if (compare_file_bytes_exactly(paths->canon, onion_info.backing_fapl_id, \ + 8, (const unsigned char *)"ONIONEOF") < 0) + { + TEST_ERROR; + } + + /* Look at recovery file: should be gone. + */ + + H5E_BEGIN_TRY { + vfile_raw = H5FDopen(paths->recovery, H5F_ACC_RDONLY, \ + onion_info.backing_fapl_id, HADDR_UNDEF); + } H5E_END_TRY; + if (NULL != vfile_raw) + TEST_ERROR; + + /* Inspect onion file. + */ + + vfile_raw = H5FDopen(paths->onion, H5F_ACC_RDONLY, \ + onion_info.backing_fapl_id, HADDR_UNDEF); + if (NULL == vfile_raw) + TEST_ERROR; + + filter.page_size = onion_info.page_size; + filter.n_revisions = 1; + filter.origin_eof = 0; + filter.revisions[0].comment = onion_info.comment; + filter.revisions[0].n_index_entries = (uint64_t)(-1); /* don't care */ + filter.revisions[0].revision_id = 0; + filter.revisions[0].parent_revision_id = 0; + filter.revisions[0].logi_eof = (TRUE == with_initial_data) \ + ? a_list_size_s : 0; + + if (verify_history_as_expected_onion(vfile_raw, &filter) < 0) + TEST_ERROR; + + if (H5FDclose(vfile_raw) < 0) + TEST_ERROR; + vfile_raw = NULL; + + /* R/O open the file with Onion VFD; inspect logial file. + */ + + vfile_ro = H5FDopen(paths->canon, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF); + if (NULL == vfile_ro) + TEST_ERROR; + + if (TRUE == with_initial_data) { + if (H5FDget_eof(vfile_ro, H5FD_MEM_DRAW) != a_list_size_s) + TEST_ERROR; + if (H5FDget_eoa(vfile_ro, H5FD_MEM_DRAW) != 0) + TEST_ERROR; + if (H5FDset_eoa(vfile_ro, H5FD_MEM_DRAW, a_list_size_s) < 0) + TEST_ERROR; + buf = (char *)HDmalloc(sizeof(char) * a_list_size_s * 64); + if (NULL == buf) + TEST_ERROR; + if (H5FDread(vfile_ro, H5FD_MEM_DRAW, H5P_DEFAULT, 0, a_list_size_s, \ + buf) < 0) + { + TEST_ERROR; + } + if (HDmemcmp(a_list_s, buf, a_list_size_s) != 0) + TEST_ERROR; + HDfree(buf); + buf = NULL; + } /* end if data was written to initial revision */ + else { + if (H5FDget_eoa(vfile_ro, H5FD_MEM_DRAW) != 0) + TEST_ERROR; + if (H5FDget_eof(vfile_ro, H5FD_MEM_DRAW) != 0) + TEST_ERROR; + } /* end if initial revision has no data */ + + if (H5FDclose(vfile_ro) < 0) + TEST_ERROR; + vfile_ro = NULL; + + /* + * CLEANUP + */ + + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + fapl_id = H5I_INVALID_HID; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + onion_filepaths_destroy(paths); + + PASSED(); + return 0; + +error: + + if (paths != NULL) { + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + onion_filepaths_destroy(paths); + } + + if (buf != NULL) + HDfree(buf); + + if (vfile_raw != NULL) + (void)H5FDclose(vfile_raw); + if (vfile_rw != NULL) + (void)H5FDclose(vfile_rw); + if (vfile_ro != NULL) + (void)H5FDclose(vfile_ro); + + if (fapl_id != H5I_INVALID_HID) + (void)H5Pclose(fapl_id); + + return -1; +} /* end test_create_oniontarget() */ + +/*----------------------------------------------------------------------------- + * + * Function: test_several_revisions_with_logical_gaps() + * + * Purpose: Test the ability of the Onion VFD to create a valid + * 'onionized' file. + * + * When `truncate_canonical` is FALSE, the canonical file is + * nonexistent on the backing store on onion-creation. + * When `truncate_canonical` is TRUE, a canonical file is created + * on the backing store with known contents, which are to be + * truncated on onion-creation. + * + * Return: PASSED : 0 + * FAILED : -1 + * + *----------------------------------------------------------------------------- + */ +static int +test_several_revisions_with_logical_gaps(void) +{ + const char *basename = "somesuch"; + hid_t fapl_id = H5I_INVALID_HID; + struct onion_filepaths *paths = NULL; + H5FD_onion_fapl_info_t onion_info = { + H5FD_ONION_FAPL_INFO_MAGIC, + H5FD_ONION_FAPL_INFO_VERSION_CURR, + H5I_INVALID_HID, /* backing_fapl_id */ + ONION_TEST_PAGE_SIZE_5, /* page_size */ + H5FD_ONION_STORE_TARGET_ONION, /* store_target */ + H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST, + 0, /* force_write_open */ + 0, /* flags */ + "first" /* comment */ + }; + H5FD_t *file = NULL; /* Onion virtual file for read/write */ + struct expected_history filter; + unsigned char *buf = NULL; + struct revise_revision about[4]; +#if 0 + struct H5FD__onion_history_header hdr_out; + struct H5FD__onion_revision_record rev_out; +#endif + struct H5FD__onion_whole_history whs_out; + size_t i = 0; + haddr_t size = 0; + uint64_t a_off = ONION_TEST_PAGE_SIZE_5 + 7; /* 39 */ + uint64_t b_off = (((a_off + a_list_size_s + ONION_TEST_PAGE_SIZE_5 - 1) \ + >> 5) << 5) + ONION_TEST_PAGE_SIZE_5 + 7; /* full page between */ + + TESTING("multiple revisions with gaps and overlap"); + + /********* + * SETUP * + *********/ + +#if 0 + hdr_out.magic = H5FD__ONION_HEADER_MAGIC; + hdr_out.version = H5FD__ONION_HEADER_VERSION_CURR; + rev_out.magic = H5FD__ONION_REVISION_RECORD_MAGIC; + rev_out.version = H5FD__ONION_REVISION_RECORD_VERSION_CURR; +#endif + whs_out.magic = H5FD__ONION_WHOLE_HISTORY_MAGIC; + whs_out.version = H5FD__ONION_WHOLE_HISTORY_VERSION_CURR; + whs_out.n_revisions = 0; + whs_out.record_pointer_list = NULL; + + onion_info.backing_fapl_id = h5_fileaccess(); + + paths = onion_filepaths_init(basename, &onion_info); + if (NULL == paths) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + + /* Empty first revision */ + about[0].magic = ONION_TEST_REV_REV_MAGIC; + about[0].truncate = TRUE; + about[0].revision_id = H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST; + about[0].comment = "first"; + about[0].n_writes = 0; + + about[1].magic = ONION_TEST_REV_REV_MAGIC; + about[1].truncate = FALSE; + about[1].revision_id = H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST; + about[1].comment = "second"; + about[1].n_writes = 1; + about[1].writes[0].offset = a_off; + about[1].writes[0].size = a_list_size_s; + about[1].writes[0].buf = a_list_s; + + about[2].magic = ONION_TEST_REV_REV_MAGIC; + about[2].truncate = FALSE; + about[2].revision_id = H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST; + about[2].comment = "third"; + about[2].n_writes = 1; /* TODO: several writes */ + about[2].writes[0].offset = b_off; + about[2].writes[0].size = b_list_size_s; + about[2].writes[0].buf = b_list_s; + + about[3].magic = ONION_TEST_REV_REV_MAGIC; + about[3].truncate = FALSE; + about[3].revision_id = H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST; + about[3].comment = "fourth"; + about[3].n_writes = 1; + about[3].writes[0].offset = 0; + about[3].writes[0].size = a_list_size_s; + about[3].writes[0].buf = a_list_s; + + if (do_onion_open_and_writes(paths->canon, &onion_info, 4, about) < 0) + TEST_ERROR; + + /* Inspect logical file */ + + /* Empty first revision */ + onion_info.revision_id = 0; + fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == fapl_id) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + file = H5FDopen(paths->canon, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF); + if (NULL == file) + TEST_ERROR; + if (0 != H5FDget_eof(file, H5FD_MEM_DRAW)) + TEST_ERROR; + if (H5FDclose(file) < 0) + TEST_ERROR; + file = NULL; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + fapl_id = H5I_INVALID_HID; + + /* One offset block in second revision */ + onion_info.revision_id = 1; + fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == fapl_id) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + file = H5FDopen(paths->canon, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF); + if (NULL == file) + TEST_ERROR; + size = a_off + a_list_size_s; + if (size != H5FDget_eof(file, H5FD_MEM_DRAW)) + TEST_ERROR; + buf = (unsigned char *)HDmalloc(sizeof(unsigned char) * size); + if (NULL == buf) + TEST_ERROR; + if (H5FDset_eoa(file, H5FD_MEM_DRAW, size) < 0) + TEST_ERROR; + if (H5FDread(file, H5FD_MEM_DRAW, H5P_DEFAULT, 0, size, buf) < 0) + TEST_ERROR; + for (i = 0; i < a_off; i++) { + if (0 != buf[i]) + TEST_ERROR; + } + if (HDmemcmp(buf + a_off, a_list_s, a_list_size_s) != 0) + TEST_ERROR; + HDfree(buf); + buf = NULL; + /* Repeat read at page offset; test possible read offset error */ + buf = (unsigned char *)HDmalloc(sizeof(unsigned char) \ + * ONION_TEST_PAGE_SIZE_5); + if (NULL == buf) + TEST_ERROR; + if (H5FDread(file, H5FD_MEM_DRAW, H5P_DEFAULT, ONION_TEST_PAGE_SIZE_5, \ + ONION_TEST_PAGE_SIZE_5, buf) < 0) + TEST_ERROR; + size = a_off - ONION_TEST_PAGE_SIZE_5; + for (i = 0; i < size; i++) { + if (0 != buf[i]) + TEST_ERROR; + } + if (HDmemcmp(buf + size, a_list_s, ONION_TEST_PAGE_SIZE_5 - size) != 0) + TEST_ERROR; + HDfree(buf); + buf = NULL; + if (H5FDclose(file) < 0) + TEST_ERROR; + file = NULL; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + fapl_id = H5I_INVALID_HID; + + /* Two offset blocks in third revision */ + onion_info.revision_id = 2; + fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == fapl_id) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + file = H5FDopen(paths->canon, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF); + if (NULL == file) + TEST_ERROR; + size = b_off + b_list_size_s; + if (size != H5FDget_eof(file, H5FD_MEM_DRAW)) + TEST_ERROR; + buf = (unsigned char *)HDmalloc(sizeof(unsigned char) * size); + if (NULL == buf) + TEST_ERROR; + if (H5FDset_eoa(file, H5FD_MEM_DRAW, size) < 0) + TEST_ERROR; + if (H5FDread(file, H5FD_MEM_DRAW, H5P_DEFAULT, 0, size, buf) < 0) + TEST_ERROR; + for (i = 0; i < a_off; i++) { + if (0 != buf[i]) + TEST_ERROR; + } + if (HDmemcmp(buf + a_off, a_list_s, a_list_size_s) != 0) + TEST_ERROR; + for (i = a_off + a_list_size_s; i < b_off; i++) { + if (0 != buf[i]) + TEST_ERROR; + } + if (HDmemcmp(buf + b_off, b_list_s, b_list_size_s) != 0) + TEST_ERROR; + HDfree(buf); + buf = NULL; + if (H5FDclose(file) < 0) + TEST_ERROR; + file = NULL; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + fapl_id = H5I_INVALID_HID; + + /* From start and partial overwrite in fourth revision */ + onion_info.revision_id = 3; + fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == fapl_id) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + file = H5FDopen(paths->canon, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF); + if (NULL == file) + TEST_ERROR; + size = b_off + b_list_size_s; + if (size != H5FDget_eof(file, H5FD_MEM_DRAW)) + TEST_ERROR; + buf = (unsigned char *)HDmalloc(sizeof(unsigned char) * size); + if (NULL == buf) + TEST_ERROR; + if (H5FDset_eoa(file, H5FD_MEM_DRAW, size) < 0) + TEST_ERROR; + if (H5FDread(file, H5FD_MEM_DRAW, H5P_DEFAULT, 0, size, buf) < 0) + TEST_ERROR; + if (HDmemcmp(buf, a_list_s, a_list_size_s) != 0) + TEST_ERROR; + if (HDmemcmp(buf + a_list_size_s, \ + a_list_s + a_list_size_s - a_off, \ + a_off) != 0) + TEST_ERROR; + for (i = a_off + a_list_size_s; i < b_off; i++) { + if (0 != buf[i]) + TEST_ERROR; + } + if (HDmemcmp(buf + b_off, b_list_s, b_list_size_s) != 0) + TEST_ERROR; + HDfree(buf); + buf = NULL; + if (H5FDclose(file) < 0) + TEST_ERROR; + file = NULL; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + fapl_id = H5I_INVALID_HID; + + /* No fifth revision */ + onion_info.revision_id = 4; + fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == fapl_id) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + H5E_BEGIN_TRY { + file = H5FDopen(paths->canon, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF); + } H5E_END_TRY; + if (NULL != file) + TEST_ERROR; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + fapl_id = H5I_INVALID_HID; + + /* Inspect history construction */ + + file = H5FDopen(paths->onion, H5F_ACC_RDONLY, \ + onion_info.backing_fapl_id, HADDR_UNDEF); + if (NULL == file) + TEST_ERROR; + + filter.page_size = onion_info.page_size; + filter.n_revisions = 4; + filter.origin_eof = 0; + + filter.revisions[0].comment = "first"; + filter.revisions[0].n_index_entries = 0; + filter.revisions[0].revision_id = 0; + filter.revisions[0].parent_revision_id = 0; + filter.revisions[0].logi_eof = 0; + + filter.revisions[1].comment = "second"; + filter.revisions[1].n_index_entries = \ + (a_list_size_s + ONION_TEST_PAGE_SIZE_5 - 1) >> 5; + filter.revisions[1].revision_id = 1; + filter.revisions[1].parent_revision_id = 0; + filter.revisions[1].logi_eof = a_off + a_list_size_s; + + filter.revisions[2].comment = "third"; + filter.revisions[2].n_index_entries = filter.revisions[1].n_index_entries \ + + ((b_list_size_s + ONION_TEST_PAGE_SIZE_5 - 1) >> 5); + filter.revisions[2].revision_id = 2; + filter.revisions[2].parent_revision_id = 1; + filter.revisions[2].logi_eof = b_off + b_list_size_s; + + filter.revisions[3].comment = "fourth"; + filter.revisions[3].n_index_entries = filter.revisions[2].n_index_entries \ + + 1; + filter.revisions[3].revision_id = 3; + filter.revisions[3].parent_revision_id = 2; + filter.revisions[3].logi_eof = b_off + b_list_size_s; + + if (verify_history_as_expected_onion(file, &filter) < 0) + TEST_ERROR; + + if (H5FDclose(file) < 0) + TEST_ERROR; + file = NULL; + + /* + * CLEANUP + */ + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + onion_filepaths_destroy(paths); + + PASSED(); + return 0; + +error: + + if (paths != NULL) { + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + onion_filepaths_destroy(paths); + } + + if (whs_out.record_pointer_list != NULL) + HDfree(whs_out.record_pointer_list); + if (buf != NULL) + HDfree(buf); + if (file != NULL) + (void)H5FDclose(file); + if (fapl_id != H5I_INVALID_HID) + (void)H5Pclose(fapl_id); + + return -1; +} /* end test_several_revisions_with_logical_gaps() */ + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * + * Function: do_onion_open_and_writes + * + * Purpose: Automate the process of creating/opening a file and performing + * a series of writes. + * + * Return: Success : 0 + * Failure : -1 + * + *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + */ +static int +do_onion_open_and_writes(const char *filename, \ + H5FD_onion_fapl_info_t *onion_info_p, size_t n_ops, \ + struct revise_revision *about) +{ + hid_t fapl_id = H5I_INVALID_HID; + H5FD_t *file = NULL; /* Onion virtual file for read/write */ + unsigned char *buf_vfy = NULL; + size_t i = 0; + + for (i = 0; i < n_ops; i++) { + size_t j = 0; + unsigned int flags = H5F_ACC_RDWR; + + if (about[i].magic != ONION_TEST_REV_REV_MAGIC) + goto error; + if (i != 0 && about[i].truncate == TRUE) + goto error; + + if (TRUE == about[i].truncate) + flags |= H5F_ACC_CREAT | H5F_ACC_TRUNC; + + onion_info_p->revision_id = about[i].revision_id; + if (about[i].comment != NULL) { + j = MIN(strlen(about[i].comment), H5FD_ONION_FAPL_INFO_COMMENT_MAX_LEN); + HDmemcpy(onion_info_p->comment, about[i].comment, j); + } + onion_info_p->comment[j] = '\0'; + fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == fapl_id) + goto error; + if (H5Pset_fapl_onion(fapl_id, onion_info_p) < 0) + goto error; + file = H5FDopen(filename, flags, fapl_id, HADDR_UNDEF); + if (NULL == file) + goto error; + + for (j = 0; j < about[i].n_writes; j++) { + struct write_info *wi = &about[i].writes[j]; + + /* Write to file */ + if (H5FDget_eoa(file, H5FD_MEM_DRAW) < wi->offset + wi->size \ + && H5FDset_eoa(file, H5FD_MEM_DRAW, wi->offset + wi->size) < 0) + TEST_ERROR; + if (H5FDwrite(file, H5FD_MEM_DRAW, H5P_DEFAULT, wi->offset, \ + wi->size, wi->buf) < 0) + TEST_ERROR; + /* Verify write as expected */ + buf_vfy = (unsigned char *)HDmalloc( + sizeof(unsigned char) * wi->size); + if (NULL == buf_vfy) + TEST_ERROR; + if (H5FDread(file, H5FD_MEM_DRAW, H5P_DEFAULT, wi->offset, \ + wi->size, buf_vfy) < 0) + TEST_ERROR; + if (HDmemcmp(buf_vfy, wi->buf, wi->size) != 0) { + const unsigned char *_buf = wi->buf; + size_t z = 0; + HDputs("i exp act"); + for (z = 0; z < wi->size; z++) + HDprintf("%02x %c %c\n", z, _buf[z], buf_vfy[z]); + fflush(stdout); + TEST_ERROR; + } + HDfree(buf_vfy); + buf_vfy = NULL; + } /* end for each write */ + + if (H5FDclose(file) < 0) + goto error; + file = NULL; + if (H5Pclose(fapl_id) < 0) + goto error; + fapl_id = H5I_INVALID_HID; + } /* end for each open-close cycle */ + + return 0; + +error: + if (file != NULL) + (void)H5FDclose(file); + if (fapl_id != H5I_INVALID_HID) + (void)H5Pclose(fapl_id); + if (buf_vfy != NULL) + HDfree(buf_vfy); + + return -1; +} /* end do_onion_open_and_writes() */ + +/*----------------------------------------------------------------------------- + * + * Function: test_page_aligned_history_create() + * + * Purpose: Verify that, when specified in FAPL on onionization/creation, + * All history writes are aligned to page-size boundaries. + * + * Return: PASSED : 0 + * FAILED : -1 + * + *----------------------------------------------------------------------------- + */ +static int +test_page_aligned_history_create(void) +{ + const char *basename = "somesuch"; + hid_t fapl_id = H5I_INVALID_HID; + struct onion_filepaths *paths = NULL; + H5FD_onion_fapl_info_t onion_info = { + H5FD_ONION_FAPL_INFO_MAGIC, + H5FD_ONION_FAPL_INFO_VERSION_CURR, + H5I_INVALID_HID, /* backing_fapl_id */ + ONION_TEST_PAGE_SIZE_5, /* page_size */ + H5FD_ONION_STORE_TARGET_ONION, /* store_target */ + H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST, + 0, /* force_write_open */ + H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT, + "initial commit" /* comment */ + }; + H5FD_t *file = NULL; /* Onion virtual file for read/write */ + unsigned char *buf = NULL; + struct revise_revision about[2]; + struct H5FD__onion_history_header hdr_out; +#if 0 + struct H5FD__onion_revision_record rev_out; +#endif + struct H5FD__onion_whole_history whs_out; + size_t i = 0; + uint64_t a_off = b_list_size_s - a_list_size_s; + + TESTING("page-aligned history on onion-created file"); + + /********* + * SETUP * + *********/ + + hdr_out.magic = H5FD__ONION_HEADER_MAGIC; + hdr_out.version = H5FD__ONION_HEADER_VERSION_CURR; +#if 0 + rev_out.magic = H5FD__ONION_REVISION_RECORD_MAGIC; + rev_out.version = H5FD__ONION_REVISION_RECORD_VERSION_CURR; +#endif + whs_out.magic = H5FD__ONION_WHOLE_HISTORY_MAGIC; + whs_out.version = H5FD__ONION_WHOLE_HISTORY_VERSION_CURR; + whs_out.n_revisions = 0; + whs_out.record_pointer_list = NULL; + + onion_info.backing_fapl_id = h5_fileaccess(); + fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == fapl_id) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + paths = onion_filepaths_init(basename, &onion_info); + if (NULL == paths) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + + about[0].magic = ONION_TEST_REV_REV_MAGIC; + about[0].truncate = TRUE; + about[0].revision_id = H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST; + about[0].comment = "initial_commit"; + about[0].n_writes = 1; + about[0].writes[0].offset = 0; + about[0].writes[0].size = b_list_size_s; + about[0].writes[0].buf = b_list_s; + + about[1].magic = ONION_TEST_REV_REV_MAGIC; + about[1].truncate = FALSE; + about[1].revision_id = H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST; + about[1].comment = "second"; + about[1].n_writes = 1; + about[1].writes[0].offset = a_off; + about[1].writes[0].size = a_list_size_s; + about[1].writes[0].buf = a_list_s; + + if (do_onion_open_and_writes(paths->canon, &onion_info, 2, about) < 0) + TEST_ERROR; + + /* Inspect logical file */ + buf = (unsigned char *)HDmalloc(sizeof(unsigned char) * b_list_size_s); + if (NULL == buf) + TEST_ERROR; + file = H5FDopen(paths->canon, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF); + if (NULL == file) + TEST_ERROR; + if (b_list_size_s != H5FDget_eof(file, H5FD_MEM_DRAW)) + TEST_ERROR; + if (H5FDset_eoa(file, H5FD_MEM_DRAW, b_list_size_s) < 0) + TEST_ERROR; + if (H5FDread(file, H5FD_MEM_DRAW, H5P_DEFAULT, 0, b_list_size_s, buf) < 0) + TEST_ERROR; + if (HDmemcmp(a_list_s, buf + a_off, a_list_size_s) != 0) { + size_t k; + HDprintf("aoff: %llu\n", a_off); + HDputs("i exp act"); + for (k = 0; k < b_list_size_s; k++) { + HDprintf("%3llu:: %c : %c\n", + k, (k < a_off) ? ' ' : a_list_s[k - a_off], buf[k]); + } + fflush(stdout); + TEST_ERROR; + } + if (HDmemcmp(b_list_s, buf, a_off) != 0){ + size_t k; + HDprintf("aoff: %llu\n", a_off); + HDputs("i exp act"); + for (k = 0; k < b_list_size_s; k++) { + HDprintf("%3llu:: %c : %c\n", + k, (k < a_off) ? b_list_s[k] : ' ', buf[k]); + } + fflush(stdout); + TEST_ERROR; + } + if (H5FDclose(file) < 0) + TEST_ERROR; + file = NULL; + HDfree(buf); + buf = NULL; + + /* Inspect history construction */ + + file = H5FDopen(paths->onion, H5F_ACC_RDONLY, onion_info.backing_fapl_id, \ + HADDR_UNDEF); + if (NULL == file) + TEST_ERROR; + if (H5FDset_eoa(file, H5FD_MEM_DRAW, H5FDget_eof(file, H5FD_MEM_DRAW)) < 0) + TEST_ERROR; + + buf = (unsigned char *)HDmalloc(H5FD__ONION_ENCODED_SIZE_HEADER); + if (NULL == buf) + TEST_ERROR; + if (H5FDread(file, H5FD_MEM_DRAW, H5P_DEFAULT, 0, \ + H5FD__ONION_ENCODED_SIZE_HEADER, buf) < 0) + TEST_ERROR; + if (H5FD_onion_history_header_decode(buf, &hdr_out) \ + != H5FD__ONION_ENCODED_SIZE_HEADER) + TEST_ERROR; + if (hdr_out.whole_history_addr & ((1 << 5) - 1)) /* 5::PAGE_SIZE_5 */ + TEST_ERROR; + HDfree(buf); + buf = NULL; + + buf = (unsigned char *)HDmalloc(hdr_out.whole_history_size); + if (H5FDread(file, H5FD_MEM_DRAW, H5P_DEFAULT, hdr_out.whole_history_addr,\ + hdr_out.whole_history_size, buf) < 0) + TEST_ERROR; + if (H5FD_onion_whole_history_decode(buf, &whs_out) \ + != hdr_out.whole_history_size) + TEST_ERROR; + if (whs_out.n_revisions != 2) + TEST_ERROR; + whs_out.record_pointer_list = (struct H5FD__onion_record_pointer *) \ + HDcalloc(whs_out.n_revisions, \ + sizeof(struct H5FD__onion_record_pointer)); + if (NULL == whs_out.record_pointer_list) + TEST_ERROR; + if (H5FD_onion_whole_history_decode(buf, &whs_out) \ + != hdr_out.whole_history_size) + TEST_ERROR; + HDfree(buf); + buf = NULL; + + for (i = 0; i < whs_out.n_revisions; i++) { + struct H5FD__onion_record_pointer *rr_p = \ + &whs_out.record_pointer_list[i]; + if (rr_p->phys_addr & ((1 << 5) - 1)) /* 5::PAGE_SIZE_5 */ + TEST_ERROR; + /* TODO: check phys_addr of each page entry? */ + } + + + HDfree(whs_out.record_pointer_list); + whs_out.record_pointer_list = NULL; + + if (H5FDclose(file) < 0) + TEST_ERROR; + file = NULL; + HDfree(buf); + buf = NULL; + + /* + * CLEANUP + */ + + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + fapl_id = H5I_INVALID_HID; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + onion_filepaths_destroy(paths); + + PASSED(); + return 0; + +error: + + if (paths != NULL) { + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + onion_filepaths_destroy(paths); + } + + if (whs_out.record_pointer_list != NULL) + HDfree(whs_out.record_pointer_list); + if (buf != NULL) + HDfree(buf); + if (file != NULL) + (void)H5FDclose(file); + if (fapl_id != H5I_INVALID_HID) + (void)H5Pclose(fapl_id); + + return -1; +} /* end test_page_aligned_history_create() */ + +/*----------------------------------------------------------------------------- + * + * Function: test_integration_create() + * + * Purpose: Create and make multiple revisions in an HDF5 file. + * + * Return: PASSED : 0 + * FAILED : -1 + * + *----------------------------------------------------------------------------- + */ +static int +test_integration_create(void) +{ + const char *basename = "example.h5"; + //const char *basename = "somesuch.h5"; + hid_t fapl_id = H5I_INVALID_HID; + struct onion_filepaths *paths = NULL; + H5FD_onion_fapl_info_t onion_info = { + H5FD_ONION_FAPL_INFO_MAGIC, + H5FD_ONION_FAPL_INFO_VERSION_CURR, + H5I_INVALID_HID, /* backing_fapl_id */ + ONION_TEST_PAGE_SIZE_5, /* page_size */ + H5FD_ONION_STORE_TARGET_ONION, /* store_target */ + H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST, + 0, /* force_write_open */ + H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT, + "initial commit" /* comment */ + }; + hid_t file_id = H5I_INVALID_HID; + + TESTING("onion-created HDF5 file with revisions"); + + /********* + * SETUP * + *********/ + + onion_info.backing_fapl_id = h5_fileaccess(); + fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == fapl_id) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + paths = onion_filepaths_init(basename, &onion_info); + if (NULL == paths) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + + /* Create skeleton file */ +/* +HDputs("."); fflush(stdout); + file_id = H5Fcreate(paths->canon, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id); + if (H5I_INVALID_HID == file_id) + TEST_ERROR; +HDputs("."); fflush(stdout); + + +/// + hsize_t dims[2] = {6, 8}; + + hsize_t maxdims[2] = {H5S_UNLIMITED, H5S_UNLIMITED}; + hid_t space = H5Screate_simple(2, dims, maxdims); + + //hid_t space = H5Screate_simple(2, dims, NULL); + + hid_t dcpl = H5Pcreate(H5P_DATASET_CREATE); + hsize_t chunk[2] = {4, 4}; + herr_t status = H5Pset_chunk(dcpl, 2, chunk); + + int fillval = 99; + status = H5Pset_fill_value(dcpl, H5T_NATIVE_INT, &fillval); + status = H5Pset_alloc_time(dcpl, H5D_ALLOC_TIME_EARLY); + hid_t dset = H5Dcreate(file_id, "/dset", H5T_STD_I32LE, space, H5P_DEFAULT, dcpl, H5P_DEFAULT); + if (dset < 0) + TEST_ERROR + + if (H5Dclose(dset) < 0) + TEST_ERROR +/// + + + if (H5Fclose(file_id) < 0) + TEST_ERROR; + file_id = H5I_INVALID_HID; +*/ + + + // CREATE FILE WITHOUT ONION + + + hid_t file, space, dset, dcpl; /* Handles */ + herr_t status; + hsize_t dims[2] = {4, 7}, + maxdims[2] = {H5S_UNLIMITED, H5S_UNLIMITED}, + chunk[2] = {4, 4}; + int wdata[4][7], /* Write buffer */ + fillval, + i, j; + + /* + * Initialize data. + */ + for (i=0; i<4; i++) + for (j=0; j<7; j++) + wdata[i][j] = i * j - j; + + /* + * Create a new file using the default properties. + */ + //file = H5Fcreate ("example.h5", H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); + file = H5Fcreate (paths->canon, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); + + /* + * Create dataspace with unlimited dimensions. + */ + space = H5Screate_simple (2, dims, maxdims); + + /* + * Create the dataset creation property list, and set the chunk + * size. + */ + dcpl = H5Pcreate (H5P_DATASET_CREATE); + status = H5Pset_chunk (dcpl, 2, chunk); + + /* + * Set the fill value for the dataset. + */ + fillval = 99; + status = H5Pset_fill_value (dcpl, H5T_NATIVE_INT, &fillval); + + /* + * Set the allocation time to "early". This way we can be sure + * that reading from the dataset immediately after creation will + * return the fill value. + */ + status = H5Pset_alloc_time (dcpl, H5D_ALLOC_TIME_EARLY); + + /* + * Create the dataset using the dataset creation property list. + */ + dset = H5Dcreate (file, "DS1", H5T_STD_I32LE, space, H5P_DEFAULT, dcpl, + H5P_DEFAULT); + + /* + * Write the data to the dataset. + */ + status = H5Dwrite (dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, + wdata[0]); + + /* + * Close and release resources. + */ + status = H5Pclose (dcpl); + status = H5Dclose (dset); + status = H5Sclose (space); + status = H5Fclose (file); + + +//////////////////////////// + + + +HDputs("."); fflush(stdout); +HDputs("."); fflush(stdout); + //file_id = H5Fopen("example.h5", H5F_ACC_RDWR, fapl_id); + file_id = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id); +HDputs("."); fflush(stdout); + //file_id = H5Fopen(paths->canon, H5F_ACC_RDONLY, fapl_id); + //file_id = H5FDopen("example.h5", H5F_ACC_RDWR, fapl_id, HADDR_UNDEF); + if (H5I_INVALID_HID == file_id) { + printf("\n\n\n\nERROR OPENING\n\n\n\n"); + TEST_ERROR; + } +HDputs("."); fflush(stdout); + + +/// + dset = H5Dopen(file_id, "DS1", H5P_DEFAULT); + if (dset < 0) { + printf("\n\n\n\nERROR OPENING DSET\n\n\n\n"); + TEST_ERROR + } +HDputs("."); fflush(stdout); + int dset_data[2][2]; + for (i = 0; i < 2; i++) + for (j = 0; j < 2; j++) + dset_data[i][j] = i * 6 + j + 1; +HDputs("."); fflush(stdout); + status = H5Dwrite(dset, H5T_STD_I32LE, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset_data); +HDputs("."); fflush(stdout); +/// + + if (H5Fclose(file_id) < 0) + TEST_ERROR; + file_id = H5I_INVALID_HID; +HDputs("."); fflush(stdout); + + /* TODO */ + + /* + * CLEANUP + */ + + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + fapl_id = H5I_INVALID_HID; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + onion_filepaths_destroy(paths); + + PASSED(); + return 0; + +error: + + if (paths != NULL) { + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + onion_filepaths_destroy(paths); + } + + if (file_id != H5I_INVALID_HID) + (void)H5Fclose(file_id); + if (fapl_id != H5I_INVALID_HID) + (void)H5Pclose(fapl_id); + + return -1; +} /* end test_integration_create() */ + +/*----------------------------------------------------------------------------- + * + * Function: main() + * + * Purpose: Perform unit tests on for the Onion VFD. + * + *----------------------------------------------------------------------------- + */ +int +main(void) +{ + int nerrors = 0; + + HDprintf("Testing Onion VFD functionality.\n"); + + h5_reset(); + + /* initialize */ + flags_create_s = H5F_ACC_RDWR | H5F_ACC_CREAT | H5F_ACC_TRUNC; + + /* Run tests. Return values on error are negative. */ + nerrors -= test_archival_index(); + nerrors -= test_revision_index(); + nerrors -= test_revision_index_collisions(); + nerrors -= test_revision_index_resizing(); + nerrors -= test_revision_index_to_archival_index(); + nerrors -= test_fapl(); + nerrors -= test_header_encode_decode(); + nerrors -= test_whole_history_encode_decode_empty(); + nerrors -= test_whole_history_encode_decode(); + nerrors -= test_revision_record_encode_decode(); + nerrors -= test_create_oniontarget(FALSE, FALSE); + nerrors -= test_create_oniontarget(TRUE, FALSE); + nerrors -= test_create_oniontarget(FALSE, TRUE); + nerrors -= test_create_oniontarget(TRUE, TRUE); + nerrors -= test_several_revisions_with_logical_gaps(); + nerrors -= test_page_aligned_history_create(); +// nerrors -= test_integration_create(); + +#if H5FD_ONION_ENABLE_INDEX_STATS + nerrors -= test_working_index_stats(); /* TODO */ +#endif /* H5FD_ONION_ENABLE_INDEX_STATS */ + + if (nerrors > 0) { + HDprintf("***** %d Onion TEST%s FAILED! *****\n", + nerrors, + nerrors > 1 ? "S" : ""); + nerrors = 1; + } + else { + HDprintf("All Onion tests passed.\n"); + } + return nerrors; /* 0 if no errors, 1 if any errors */ + +} /* end main() */ + |