diff options
Diffstat (limited to 'test/onion.c')
-rw-r--r-- | test/onion.c | 4966 |
1 files changed, 4966 insertions, 0 deletions
diff --git a/test/onion.c b/test/onion.c new file mode 100644 index 0000000..96a4970 --- /dev/null +++ b/test/onion.c @@ -0,0 +1,4966 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 */ + +/* The Onion VFD uses H5MM calls internally, so any tests that allocate + * or free memory for said internal structures (e.g., the revision lists) + * will need to allocate memory using H5MM calls. + */ +#include "H5MMprivate.h" /* Memory management */ + +/* 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 4 +#define ONION_TEST_PAGE_SIZE_5 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 ONE_DIM_SIZE 1024 + +/* Structure to collect the onion filepaths in one place. */ +struct onion_filepaths { + char *canon; + char *onion; + char *recovery; +}; + +struct expected_revision { + uint64_t revision_num; + uint64_t parent_revision_num; + uint64_t logical_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 { + hbool_t truncate; /* onion-create, truncating any existing data */ + uint64_t revision_num; + 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 *filepath, hid_t fapl_id, size_t nbytes, + const unsigned char *exp); +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 *paths); +static struct onion_filepaths *onion_filepaths_init(const char *basename); + +/* 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) +{ + struct onion_filepaths *paths = NULL; + + if (NULL == (paths = HDmalloc(sizeof(struct onion_filepaths)))) + TEST_ERROR; + paths->canon = NULL; + paths->onion = NULL; + paths->recovery = NULL; + + if (NULL == (paths->canon = HDmalloc(sizeof(char) * ONION_TEST_FIXNAME_SIZE))) + TEST_ERROR; + HDsnprintf(paths->canon, ONION_TEST_FIXNAME_SIZE, "%s", basename); + + if (NULL == (paths->onion = HDmalloc(sizeof(char) * ONION_TEST_FIXNAME_SIZE))) + TEST_ERROR; + HDsnprintf(paths->onion, ONION_TEST_FIXNAME_SIZE, "%s.onion", paths->canon); + + if (NULL == (paths->recovery = HDmalloc(sizeof(char) * ONION_TEST_FIXNAME_SIZE))) + TEST_ERROR; + HDsnprintf(paths->recovery, ONION_TEST_FIXNAME_SIZE, "%s.onion.recovery", paths->canon); + + return paths; + +error: + if (paths != NULL) { + HDfree(paths->canon); + HDfree(paths->onion); + HDfree(paths->recovery); + } + HDfree(paths); + + return NULL; +} + +/* Free onion file paths */ +static void +onion_filepaths_destroy(struct onion_filepaths *paths) +{ + HDfree(paths->canon); + HDfree(paths->onion); + HDfree(paths->recovery); + HDfree(paths); +} + +/*----------------------------------------------------------------------------- + * 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 */ + H5FD_onion_index_entry_t e0 = {1, 474}; + H5FD_onion_index_entry_t e1 = {4, 558}; + H5FD_onion_index_entry_t e2 = {5, 306}; + H5FD_onion_index_entry_t e3 = {9, 515}; + H5FD_onion_index_entry_t e4 = {14, 386}; + H5FD_onion_index_entry_t e5 = {18, 90}; + H5FD_onion_index_entry_t e6 = {19, 94}; + H5FD_onion_index_entry_t e7 = {20, 509}; + H5FD_onion_index_entry_t sorted[8] = {e0, e1, e2, e3, e4, e5, e6, e7}; + H5FD_onion_index_entry_t sorted_duplicates[8] = {e0, e1, e2, e2, e4, e5, e6, e7}; + H5FD_onion_index_entry_t sorted_incomplete[8] = {e1, e3, e4, e5}; + /* Partially-sorted list also aligned to 2 * page-size */ + H5FD_onion_index_entry_t sorted_partial[8] = {e1, e4, e5, e7, e0, e6, e2, e3}; /* 0..3 sorted */ + H5FD_onion_index_entry_t unsorted[8] = {e3, e1, e4, e5, e0, e6, e2, e7}; + H5FD_onion_archival_index_t aix = { + H5FD_ONION_ARCHIVAL_INDEX_VERSION_CURR, 1, /* page_size_log2 */ + 8, /* list must be populated and sorted through 0 .. (count-1) */ + sorted, /* list */ + }; + const H5FD_onion_index_entry_t *entry_out_p = NULL; + + TESTING("archival index"); + + /* + * Failing validity checks + */ + + /* Invalid version should fail */ + 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; + aix.version = H5FD_ONION_ARCHIVAL_INDEX_VERSION_CURR; + + /* NULL list should fail */ + aix.list = NULL; + if (H5FD__onion_archival_index_is_valid(&aix)) + TEST_ERROR; + + /* List not full should fail */ + aix.list = sorted_incomplete; + if (H5FD__onion_archival_index_is_valid(&aix)) + TEST_ERROR; + + /* Unsorted list should fail */ + aix.list = unsorted; + if (H5FD__onion_archival_index_is_valid(&aix)) + TEST_ERROR; + + /* List with duplicates should fail */ + aix.list = sorted_duplicates; + if (H5FD__onion_archival_index_is_valid(&aix)) + TEST_ERROR; + + /* + * Passing validity checks + */ + + /* Sorted list should pass */ + aix.list = sorted; + if (!H5FD__onion_archival_index_is_valid(&aix)) + TEST_ERROR; + + /* Extra elements ignored (should pass) */ + aix.list = sorted_partial; + aix.n_entries = 4; + if (!H5FD__onion_archival_index_is_valid(&aix)) + TEST_ERROR; + + /* + * Archival index search routine + */ + + aix.list = sorted; + aix.n_entries = 8; + + /* Check that address not in array returns zero */ + if (H5FD__onion_archival_index_find(&aix, 3, &entry_out_p) != 0) + TEST_ERROR; + /* Pointer should remain unset */ + if (entry_out_p != NULL) + TEST_ERROR; + + /* Address found should return 1 */ + if (H5FD__onion_archival_index_find(&aix, 4, &entry_out_p) != 1) + TEST_ERROR; + /* Pointer should be set */ + if (NULL == entry_out_p) + TEST_ERROR; + /* Incorrect address recorded */ + if (558 != entry_out_p->phys_addr) + TEST_ERROR; + + /* + * Test search edge cases + */ + + aix.list = sorted_incomplete; + aix.n_entries = 4; + + /* Address not in array should return 0 */ + if (H5FD__onion_archival_index_find(&aix, 1, &entry_out_p) != 0) + TEST_ERROR; + + /* Address not in array should return 0 */ + if (H5FD__onion_archival_index_find(&aix, 101, &entry_out_p) != 0) + TEST_ERROR; + + /* + * Empty archival index + */ + + entry_out_p = NULL; + aix.n_entries = 0; /* actually populated list is irrelevant */ + /* Address not in array should return 0 */ + if (H5FD__onion_archival_index_find(&aix, 3, &entry_out_p) != 0) + TEST_ERROR; + /* Pointer should remain unset */ + if (entry_out_p != NULL) + TEST_ERROR; + + PASSED(); + return 0; + +error: + return -1; +} /* end test_archival_index() */ + +/*----------------------------------------------------------------------------- + * Function: test_revision_index() + * + * Purpose: Test revision index functionality + * + * Return: PASSED : 0 + * FAILED : -1 + *----------------------------------------------------------------------------- + */ +static int +test_revision_index(void) +{ + H5FD_onion_revision_index_t *rix_p = NULL; + H5FD_onion_index_entry_t entry = { + 42, /* logical_page */ + 111112, /* phys_addr */ + }; + const H5FD_onion_index_entry_t *entry_out_p = NULL; + + TESTING("revision index"); + + /* Test index creation */ + + if (NULL == (rix_p = H5FD__onion_revision_index_init(ONION_TEST_PAGE_SIZE_5))) + TEST_ERROR; + if (H5FD_ONION_REVISION_INDEX_VERSION_CURR != rix_p->version) + TEST_ERROR; + if (0 != rix_p->n_entries) + TEST_ERROR; + + /* Test missed search */ + + if (H5FD__onion_revision_index_find(rix_p, entry.logical_page, &entry_out_p) != 0) + TEST_ERROR; + + /* Test successful insertion and lookup */ + + /* Insertion failed */ + if (H5FD__onion_revision_index_insert(rix_p, &entry) < 0) + TEST_ERROR; + if (1 != rix_p->n_entries) + TEST_ERROR; + /* Lookup failed */ + if (H5FD__onion_revision_index_find(rix_p, entry.logical_page, &entry_out_p) < 0) + TEST_ERROR; + /* Failure to set output parameter */ + if (NULL == entry_out_p) + TEST_ERROR; + if (entry.logical_page != entry_out_p->logical_page) + TEST_ERROR; + /* Seeking missing page should miss */ + if (H5FD__onion_revision_index_find(rix_p, entry.logical_page + 1, &entry_out_p) != 0) + TEST_ERROR; + + /* Test / demonstrate stored entry independent of user object */ + + entry.logical_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.logical_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->logical_page || 101 != entry_out_p->phys_addr) + TEST_ERROR; + + /* Demonstrate updating an entry */ + + /* Error cases */ + + entry.logical_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.logical_page = 100; + entry.phys_addr = 101; + if (H5FD__onion_revision_index_insert(rix_p, &entry) < 0) + TEST_ERROR; + + /* Should still be two unique entries, not three */ + if (2 != rix_p->n_entries) + TEST_ERROR; + if (H5FD__onion_revision_index_find(rix_p, 100, &entry_out_p) < 0) + TEST_ERROR; + if (100 != entry_out_p->logical_page || 101 != entry_out_p->phys_addr) + TEST_ERROR; + + if (H5FD__onion_revision_index_destroy(rix_p) < 0) + TEST_ERROR; + + 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; + H5FD_onion_index_entry_t entry = { + 0, /* logical_page */ + 0, /* phys_addr */ + }; + const H5FD_onion_index_entry_t *entry_out_p = NULL; + const uint64_t n_insert = 40; + const uint64_t offset_from_power = 5; + + TESTING("revision index collisions"); + + if (NULL == (rix_p = H5FD__onion_revision_index_init(ONION_TEST_PAGE_SIZE_5))) + TEST_ERROR; + + for (uint64_t i = 0; i < n_insert; i++) { + entry.phys_addr = i; + entry.logical_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 (uint64_t 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; + + 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; + H5FD_onion_index_entry_t entry = { + 0, /* logical_page */ + 0, /* phys_addr */ + }; + const H5FD_onion_index_entry_t *entry_out_p = NULL; + const uint64_t n_insert = U64_EXP2((H5FD_ONION_REVISION_INDEX_STARTING_SIZE_LOG2 + 3)); + + TESTING("revision index resizing"); + + if (NULL == (rix_p = H5FD__onion_revision_index_init(ONION_TEST_PAGE_SIZE_5))) + TEST_ERROR; + + for (uint64_t i = 0; i < n_insert; i++) { + entry.logical_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 (uint64_t 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; + + 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; + H5FD_onion_index_entry_t rix_entry = { + 0, /* logical_page */ + 0, /* phys_addr */ + }; + H5FD_onion_archival_index_t aix = { + H5FD_ONION_ARCHIVAL_INDEX_VERSION_CURR, + 5, /* page_size_log2 */ + 0, /* n_entries to be set */ + NULL, + }; + const uint64_t n_insert = 10; + + TESTING("revision index to archival index"); + + /* + * SETUP + */ + + if (NULL == (rix_p = H5FD__onion_revision_index_init(ONION_TEST_PAGE_SIZE_5))) + TEST_ERROR; + + /* Add scattered entries in reverse order. */ + for (uint64_t i = 0; i < n_insert; i++) { + uint64_t n = 2003 * (n_insert - i) + 47; + + rix_entry.logical_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 = NULL; + aix.n_entries = 0; + + /* 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; + + if (n_insert != aix.n_entries) + TEST_ERROR; + for (uint64_t i = 0; i < n_insert; i++) { + const H5FD_onion_index_entry_t *aix_entry_p = NULL; + + uint64_t n = 2003 * (i + 1) + 47; + + aix_entry_p = &aix.list[i]; + + if (aix_entry_p->logical_page != n) + TEST_ERROR; + if (aix_entry_p->phys_addr != (n * 13)) + TEST_ERROR; + } + + /* Successful merge into populated archival index */ + + H5MM_xfree(aix.list); + aix.list = NULL; + if (NULL == (aix.list = H5MM_malloc(sizeof(H5FD_onion_index_entry_t) * 2))) + TEST_ERROR; + aix.list[0].logical_page = 47; + aix.list[0].phys_addr = 47 * 13; + aix.list[1].logical_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; + + 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; + + if (n_insert + 2 != aix.n_entries) + TEST_ERROR; + + for (uint64_t i = 0; i < (n_insert + 2); i++) { + const H5FD_onion_index_entry_t *aix_entry_p = NULL; + + uint64_t n = 2003 * i + 47; + + aix_entry_p = &aix.list[i]; + + if (aix_entry_p->logical_page != n) + TEST_ERROR; + if (aix_entry_p->phys_addr != (n * 13)) + TEST_ERROR; + } + + /* Merged enties from revision index replace existing entries */ + + H5MM_xfree(aix.list); + aix.list = NULL; + if (NULL == (aix.list = H5MM_malloc(sizeof(H5FD_onion_index_entry_t) * 2))) + TEST_ERROR; + aix.list[0].logical_page = 2003 * (n_insert / 2) + 47; + aix.list[0].phys_addr = 103; + aix.list[1].logical_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; + + 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; + + if (n_insert != aix.n_entries) + TEST_ERROR; + + for (uint64_t i = 0; i < n_insert; i++) { + const H5FD_onion_index_entry_t *aix_entry_p = NULL; + uint64_t n = 2003 * (i + 1) + 47; + + aix_entry_p = &aix.list[i]; + + if (aix_entry_p->logical_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; + H5MM_xfree(aix.list); + + PASSED(); + return 0; + +error: + if (rix_p) + (void)H5FD__onion_revision_index_destroy(rix_p); + H5MM_xfree(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_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"); + + if ((dxpl_id = H5Pcreate(H5P_DATASET_XFER)) < 0) + TEST_ERROR; + + if ((fapl_id_sec2 = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + if (H5Pset_fapl_sec2(fapl_id_sec2)) + TEST_ERROR; + + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + + /* Set FAPL */ + + /* Invalid fapl should fail */ + H5E_BEGIN_TRY + { + ret = H5Pset_fapl_onion(H5I_INVALID_HID, &info_in); + } + H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; + + /* NULL info pointer should fail */ + H5E_BEGIN_TRY + { + ret = H5Pset_fapl_onion(fapl_id, NULL); + } + H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; + + /* Invalid version should fail */ + info_in.version++; + H5E_BEGIN_TRY + { + ret = H5Pset_fapl_onion(fapl_id, &info_in); + } + H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; + info_in.version--; + + /* Page size not a power of 2 should fail */ + 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 <=0 should fail */ + info_in.page_size = 0; + H5E_BEGIN_TRY + { + ret = H5Pset_fapl_onion(fapl_id, &info_in); + } + H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; + info_in.page_size = ONION_TEST_PAGE_SIZE_1; + + /* Invalid backing fapl should fail */ + 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 not a fapl should fail */ + 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; + info_in.backing_fapl_id = H5P_DEFAULT; + + if (H5Pset_fapl_onion(fapl_id, &info_in) < 0) + TEST_ERROR; + + /* Get onion fapl info */ + + /* NULL info_out pointer should fail */ + H5E_BEGIN_TRY + { + ret = H5Pget_fapl_onion(fapl_id, NULL); + } + H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; + + /* Invalid fapl should fail */ + H5E_BEGIN_TRY + { + ret = H5Pget_fapl_onion(H5I_INVALID_HID, &info_out); + } + H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; + + /* Non-onion fapl ID should fail */ + H5E_BEGIN_TRY + { + ret = H5Pget_fapl_onion(fapl_id_sec2, &info_out); + } + H5E_END_TRY; + if (SUCCEED == ret) + TEST_ERROR; + + /* Normal case */ + if (H5Pget_fapl_onion(fapl_id, &info_out) < 0) + 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_num) + 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; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + if (H5Pclose(fapl_id_sec2) < 0) + TEST_ERROR; + + PASSED(); + return 0; + +error: + H5E_BEGIN_TRY + { + H5Pclose(dxpl_id); + H5Pclose(fapl_id); + H5Pclose(fapl_id_sec2); + } + H5E_END_TRY + + 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, /* history_addr */ + 88, 0, 0, 0, 0, 0, 0, 0, /* history_size */ + 0, 0, 0, 0 /* sum populated below */ + }; + unsigned char *ptr = NULL; + uint32_t checksum = 0; + uint32_t checksum_out = 0; + size_t i = 0; + uint64_t size_ret = 0; + H5FD_onion_header_t hdr; + H5FD_onion_header_t hdr_out; + + TESTING("encode/decode history header"); + + checksum = H5_checksum_fletcher32(exp, H5FD_ONION_ENCODED_SIZE_HEADER - 4); + ptr = exp + H5FD_ONION_ENCODED_SIZE_HEADER - 4; + UINT32ENCODE(ptr, checksum); + + hdr.version = H5FD_ONION_HEADER_VERSION_CURR; + hdr.flags = 12; + hdr.origin_eof = 8589934609ull, hdr.page_size = 4096; + hdr.history_addr = 123456; + hdr.history_size = 88; + + if (H5FD__onion_header_encode(&hdr, buf, &checksum_out) != H5FD_ONION_ENCODED_SIZE_HEADER) + TEST_ERROR; + + if (checksum != checksum_out) + TEST_ERROR; + + for (i = 0; i < H5FD_ONION_ENCODED_SIZE_HEADER; i++) { + if (exp[i] != buf[i]) { + HDprintf("first mismatched byte at %zu: %02x %02x\n", i, exp[i], buf[i]); + TEST_ERROR; + } + } + + hdr_out.version = H5FD_ONION_HEADER_VERSION_CURR; + hdr_out.flags = 0; + hdr_out.page_size = 0; + hdr_out.history_addr = 0; + hdr_out.history_size = 0; + + /* Invalid header signature prevents decoding. + */ + + exp[3] = 'X'; /* invalidate encoded signature */ + H5E_BEGIN_TRY + { + size_ret = H5FD__onion_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_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_header_decode(exp, &hdr_out); + } + H5E_END_TRY; + if (0 != size_ret) + TEST_ERROR; + + /* Reset */ + exp[4] = H5FD_ONION_HEADER_VERSION_CURR; + + /* Valid header can be decoded */ + + if (H5FD__onion_header_decode(buf, &hdr_out) != H5FD_ONION_ENCODED_SIZE_HEADER) + 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.history_addr != hdr_out.history_addr) + TEST_ERROR; + if (hdr.history_size != hdr_out.history_size) + TEST_ERROR; + + PASSED(); + return 0; + +error: + return -1; +} /* end test_header_encode_decode() */ + +/*----------------------------------------------------------------------------- + * Function: test_history_encode_decode_empty() + * + * Purpose: Verify onion history encoding and decoding behavior. + * Tests the case of the "empty" history. + * Verifies behavior in standard error cases. + * + * Return: PASSED : 0 + * FAILED : -1 + *----------------------------------------------------------------------------- + */ +static int +test_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 checksum = 0; + uint32_t checksum_out = 0; + size_t i = 0; + uint64_t size_ret = 0; + H5FD_onion_history_t history = { + H5FD_ONION_HISTORY_VERSION_CURR, 0, /* n_revisions */ + NULL, /* list */ + 0, /* checksum */ + }; + H5FD_onion_history_t history_out = { + H5FD_ONION_HISTORY_VERSION_CURR, 0, /* n_revisions */ + NULL, /* list */ + 0, /* checksum */ + }; + + TESTING("encode/decode history (empty and failures)"); + + /* Generage checksum but don't store it yet */ + checksum = H5_checksum_fletcher32(exp, H5FD_ONION_ENCODED_SIZE_HISTORY - 4); + ptr = exp + H5FD_ONION_ENCODED_SIZE_HISTORY - 4; + UINT32ENCODE(ptr, checksum); + + if (H5FD__onion_history_encode(&history, buf, &checksum_out) != H5FD_ONION_ENCODED_SIZE_HISTORY) + TEST_ERROR; + for (i = 0; i < 20; i++) { + if (exp[i] != buf[i]) { + HDprintf("first mismatched byte at %zu: %02x %02x\n", i, exp[i], buf[i]); + TEST_ERROR; + } + } + if (checksum != checksum_out) + TEST_ERROR; + history.checksum = checksum; /* set to compare later */ + + /* Invalid signature prevents decoding */ + + exp[3] = 'X'; /* invalidate encoded signature */ + H5E_BEGIN_TRY + { + size_ret = H5FD__onion_history_decode(exp, &history_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_history_decode(exp, &history_out); + } + H5E_END_TRY; + if (0 != size_ret) + TEST_ERROR; + + exp[4] = H5FD_ONION_HISTORY_VERSION_CURR + 1; + H5E_BEGIN_TRY + { + size_ret = H5FD__onion_history_decode(exp, &history_out); + } + H5E_END_TRY; + if (0 != size_ret) + TEST_ERROR; + + exp[4] = H5FD_ONION_HISTORY_VERSION_CURR; /* reset */ + + /* Valid summary can be decoded */ + + if (H5FD__onion_history_decode(buf, &history_out) != H5FD_ONION_ENCODED_SIZE_HISTORY) + TEST_ERROR; + if (H5FD_ONION_HISTORY_VERSION_CURR != history_out.version) + TEST_ERROR; + if (history.n_revisions != history_out.n_revisions) + TEST_ERROR; + if (history.checksum != history_out.checksum) + TEST_ERROR; + if (NULL != history_out.record_locs) + TEST_ERROR; + + PASSED(); + return 0; + +error: + return -1; +} /* end test_history_encode_decode_empty() */ + +/*----------------------------------------------------------------------------- + * Function: test_history_encode_decode() + * + * Purpose: Verify onion history encoding and decoding behavior. + * Encode/decode with some set of revision record pointers. + * + * Return: PASSED : 0 + * FAILED : -1 + *----------------------------------------------------------------------------- + */ +static int +test_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 checksum_out = 0; + size_t i = 0; + H5FD_onion_history_t history = { + H5FD_ONION_HISTORY_VERSION_CURR, 3, /* n_revisions */ + NULL, /* list set below */ + 0, /* checksum not set by us */ + }; + H5FD_onion_history_t history_out = { + H5FD_ONION_HISTORY_VERSION_CURR, 0, /* n_revisions must start as zero */ + NULL, /* list */ + 0, /* checksum */ + }; + size_t exp_size = + H5FD_ONION_ENCODED_SIZE_HISTORY + H5FD_ONION_ENCODED_SIZE_RECORD_POINTER * history.n_revisions; + + TESTING("encode/decode history"); + + if (80 != exp_size) + TEST_ERROR; + + history.record_locs = HDcalloc(history.n_revisions, sizeof(H5FD_onion_record_loc_t)); + if (NULL == history.record_locs) + TEST_ERROR; + + /* Must match values in exp */ + history.record_locs[0].phys_addr = 568ull; + history.record_locs[0].record_size = 238ull; + history.record_locs[1].phys_addr = 241017ull; + history.record_locs[1].record_size = 4555ull; + history.record_locs[2].phys_addr = 918153371232ull; + history.record_locs[2].record_size = 240ull; + + /* Populate revision pointer sums in exp */ + for (i = 0; i < history.n_revisions; i++) { + uint64_t history_pre = H5FD_ONION_ENCODED_SIZE_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 + history_pre + ptr_size * i; + history.record_locs[i].checksum = H5_checksum_fletcher32(buf_p, ptr_pre); + buf_p += ptr_pre; + UINT32ENCODE(buf_p, history.record_locs[i].checksum); + } + + /* Compute, populate, and store exp final sum */ + history.checksum = H5_checksum_fletcher32(exp, exp_size - 4); + buf_p = exp + exp_size - 4; + UINT32ENCODE(buf_p, history.checksum); + + if (NULL == (buf = HDmalloc(exp_size))) + TEST_ERROR; + + if (H5FD__onion_history_encode(&history, buf, &checksum_out) != exp_size) + TEST_ERROR; + for (i = 0; i < exp_size; i++) { + if (exp[i] != buf[i]) + TEST_ERROR; + } + if (history.checksum != checksum_out) + TEST_ERROR; + + /* Initial decode, gets always-present components */ + + history_out.n_revisions = 0; /* must be initialized to 0 */ + if (H5FD__onion_history_decode(exp, &history_out) != exp_size) + TEST_ERROR; + if (H5FD_ONION_HISTORY_VERSION_CURR != history_out.version) + TEST_ERROR; + if (history.n_revisions != history_out.n_revisions) + TEST_ERROR; + /* Must be created by us */ + if (NULL != history_out.record_locs) + TEST_ERROR; + + /* True decode requires allocating space for record pointers */ + + history_out.record_locs = HDcalloc(history_out.n_revisions, sizeof(H5FD_onion_record_loc_t)); + if (NULL == history_out.record_locs) + TEST_ERROR; + + if (H5FD__onion_history_decode(exp, &history_out) != exp_size) + TEST_ERROR; + if (H5FD_ONION_HISTORY_VERSION_CURR != history_out.version) + TEST_ERROR; + if (history.n_revisions != history_out.n_revisions) + TEST_ERROR; + if (history.checksum != history_out.checksum) + TEST_ERROR; + if (NULL == history_out.record_locs) + TEST_ERROR; + for (i = 0; i < history.n_revisions; i++) { + H5FD_onion_record_loc_t exp_rp = history.record_locs[i]; + H5FD_onion_record_loc_t act_rp = history_out.record_locs[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(history_out.record_locs); + HDfree(buf); + HDfree(history.record_locs); + + PASSED(); + return 0; + +error: + HDfree(history_out.record_locs); + HDfree(buf); + HDfree(history.record_locs); + + return -1; +} /* end test_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) +{ + /* clang-format off */ + /* Byte array of expected values (FRAGILE!) */ + unsigned char exp[173] = { + 'O', 'R', 'R', 'S', /* Bytes 000-003: signature */ + 1, 0, 0, 0, /* Bytes 004-007: version */ + 5, 0, 0, 0, 0, 0, 0, 0, /* Bytes 008-015: revision ID */ + 2, 0, 0, 0, 0, 0, 0, 0, /* Bytes 016-023: parent revision ID */ + '1', '9', '4', '1', '1', '2', '0', '7', /* Bytes 024-039: time of creation */ + 'T', '1', '9', '0', '6', '4', '3', 'Z', /* ... */ + 0x11, 0x00, 0, 0, 0x02, 0, 0, 0, /* Bytes 040-047: logical file size */ + 0, 16, 0, 0, /* Bytes 048-051: page size */ + 4, 0, 0, 0, 0, 0, 0, 0, /* Bytes 052-059: # entries */ + 25, 0, 0, 0, /* Bytes 060-063: comment size */ + /* ENTRY 0 */ + 0, 0xB0, 0x1E, 0, 0, 0, 0, 0, /* Bytes 064-071: entry 0: logical offset */ + 0x4B, 0x02, 0, 0, 0, 0, 0, 0, /* Bytes 072-079: entry 0: physical address */ + 0, 0, 0, 0, /* Bytes 080-083: checksum (populated below) */ + /* ENTRY 1 */ + 0, 0xF0, 0x2E, 0, 0, 0, 0, 0, /* Bytes 084-091: entry 1: logical offset */ + 0xA7, 0, 0, 0, 0, 0, 0, 0, /* Bytes 092-099: entry 1: physical address */ + 0, 0, 0, 0, /* Bytes 100-103: checksum (populated below) */ + /* ENTRY 2 */ + 0, 0x50, 0x15, 0, 0, 0x20, 0, 0, /* Bytes 104-111: entry 2: logical offset */ + 0x11, 0, 0, 0, 0x02, 0, 0, 0, /* Bytes 112-119: entry 2: physical address */ + 0, 0, 0, 0, /* Bytes 120-123: checksum (populated below) */ + /* ENTRY 3 */ + 0, 0xE0, 0x24, 0, 0, 0, 0, 0, /* Bytes 124-131: entry 3: logical offset */ + 0xB1, 0x01, 0, 0, 0, 0, 0, 0, /* Bytes 132-139: entry 3: physical address */ + 0, 0, 0, 0, /* Bytes 140-143: checksum (populated below) */ + 'E', 'x', 'a', 'm', 'p', 'l', 'e', ' ', /* Bytes 144-168: comment */ + 'c', 'o', 'm', 'm', 'e', 'n', 't', ' ', /* ... */ + 'm', 'e', 's', 's', 'a', 'g', 'e', '.', /* ... */ + '\0', /* ... */ + 0, 0, 0, 0 /* Bytes 169-172: final checksum (populated below) */ + }; + /* clang-format on */ + unsigned char *buf = NULL; + unsigned char *buf_p = NULL; + size_t i = 0; + uint64_t size_ret; + H5FD_onion_revision_record_t r_out; + uint32_t checksum = 0; + char comment[25] = "Example comment message."; + H5FD_onion_revision_record_t record = { + H5FD_ONION_REVISION_RECORD_VERSION_CURR, + 5, /* revision ID */ + 2, /* parent revision ID */ + {'\0'}, /* time of creation - populated below */ + 8589934609ull, /* logical file size */ + { + H5FD_ONION_ARCHIVAL_INDEX_VERSION_CURR, /* version */ + 12, /* page_size_log2 */ + 4, /* n_entries */ + NULL, /* list - populated below */ + }, /* archival index struct */ + 25, /* comment size */ + comment, /* comment */ + 0, /* checksum (computed later) */ + }; + size_t exp_size = H5FD_ONION_ENCODED_SIZE_REVISION_RECORD + + (H5FD_ONION_ENCODED_SIZE_INDEX_ENTRY * record.archival_index.n_entries) + + HDstrlen("Example comment message.") + 1; + + r_out.archival_index.list = NULL; + r_out.comment = NULL; + + TESTING("encode/decode revision record"); + + HDmemcpy(record.time_of_creation, "19411207T190643Z", 16); + record.archival_index.list = HDcalloc(record.archival_index.n_entries, sizeof(H5FD_onion_index_entry_t)); + if (NULL == record.archival_index.list) + TEST_ERROR; + + /* Convert logical_page and should match address in expected buffer */ + record.archival_index.list[0].logical_page = 491ull; + record.archival_index.list[0].phys_addr = 587ull; + record.archival_index.list[1].logical_page = 751ull; + record.archival_index.list[1].phys_addr = 167ull; + record.archival_index.list[2].logical_page = 8589934933ull; + record.archival_index.list[2].phys_addr = 8589934609ull; + record.archival_index.list[3].logical_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; + checksum = H5_checksum_fletcher32(buf_p, idx_pre); + buf_p += idx_pre; + UINT32ENCODE(buf_p, checksum); + } + + checksum = 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.version = H5FD_ONION_REVISION_RECORD_VERSION_CURR; + r_out.comment_size = 0; + r_out.comment = NULL; + r_out.archival_index.version = H5FD_ONION_ARCHIVAL_INDEX_VERSION_CURR; + r_out.archival_index.n_entries = 0; + r_out.archival_index.list = NULL; + + if (NULL == (buf = HDmalloc(sizeof(unsigned char) * exp_size))) + TEST_ERROR; + + /* Test encode */ + + if (H5FD__onion_revision_record_encode(&record, buf, &checksum) != exp_size) + TEST_ERROR; + + hbool_t badness = FALSE; + for (i = 0; i < exp_size; i++) { + if (exp[i] != buf[i]) { + badness = TRUE; + HDprintf("Bad encoded record - Index %zu: expected 0x%02X but got 0x%02X\n", i, + (unsigned int)exp[i], (unsigned int)buf[i]); + } + } + if (badness) { + /* If this fragile test breaks, this information is helpful... */ + HDprintf("INDEX\n"); + for (i = 0; i < exp_size; i++) + HDprintf("%4zu ", i); + HDprintf("\n"); + + HDprintf("EXPECTED\n"); + for (i = 0; i < exp_size; i++) + HDprintf("0x%02X ", (unsigned int)exp[i]); + HDprintf("\n"); + + HDprintf("ACTUAL\n"); + for (i = 0; i < exp_size; i++) + HDprintf("0x%02X ", (unsigned int)buf[i]); + HDprintf("\n"); + } + if (badness) + TEST_ERROR; + if (record.checksum != checksum) + TEST_ERROR; + + /* 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.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.comment = HDcalloc(r_out.comment_size, sizeof(char)); + if (NULL == r_out.comment) + TEST_ERROR; + r_out.archival_index.list = HDcalloc(r_out.archival_index.n_entries, sizeof(H5FD_onion_index_entry_t)); + 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_VERSION_CURR != r_out.version) + TEST_ERROR; + if (record.revision_num != r_out.revision_num) + TEST_ERROR; + if (record.parent_revision_num != r_out.parent_revision_num) + TEST_ERROR; + if (record.parent_revision_num != r_out.parent_revision_num) + 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.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_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++) { + H5FD_onion_index_entry_t *ep = &record.archival_index.list[i]; + H5FD_onion_index_entry_t *ap = &r_out.archival_index.list[i]; + + if (ep->phys_addr != ap->phys_addr) + TEST_ERROR; + if (ep->logical_page != ap->logical_page) + TEST_ERROR; + } + + /* Cleanup */ + + HDfree(r_out.archival_index.list); + HDfree(r_out.comment); + HDfree(buf); + HDfree(record.archival_index.list); + + PASSED(); + return 0; + +error: + HDfree(r_out.archival_index.list); + HDfree(r_out.comment); + HDfree(buf); + 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 */ + uint64_t filesize = 0; + + if (NULL == (raw_vfile = H5FDopen(filepath, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF))) + TEST_ERROR; + + /* filesize is wrong w/ stdio - it's zero instead of 40 or whatnot */ + filesize = (uint64_t)H5FDget_eof(raw_vfile, H5FD_MEM_DRAW); + if ((uint64_t)nbytes != filesize) { + HDfprintf(stderr, "\nSizes not the same - nbytes: %zu, filesize: %" PRIu64 "\n", nbytes, filesize); + TEST_ERROR; + } + + if (NULL == (act_buf = HDmalloc(nbytes))) + TEST_ERROR; + /* Fill buffer with bogus UCHAR_MAX values */ + for (size_t i = 0; i < nbytes; i++) + act_buf[i] = UCHAR_MAX; + 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 (size_t i = 0; i < nbytes; i++) { + if (exp[i] != act_buf[i]) { + HDprintf("first mismatched byte %zu: expected 0x%02X was 0x%02X\n", i, exp[i], act_buf[i]); + TEST_ERROR; + } + } + + if (H5FDclose(raw_vfile) < 0) + TEST_ERROR; + HDfree(act_buf); + + return 0; + +error: + if (raw_vfile != NULL) + H5FDclose(raw_vfile); + HDfree(act_buf); + + 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 */ + H5FD_onion_header_t hdr_out; + H5FD_onion_history_t history_out; + H5FD_onion_revision_record_t rev_out; + uint64_t filesize = 0; + uint64_t readsize = 0; + uint8_t *ui8p = NULL; + uint32_t buf_checksum = 0; + + /* memset to avoid bad frees on errors */ + HDmemset(&rev_out, 0, sizeof(H5FD_onion_revision_record_t)); + HDmemset(&history_out, 0, sizeof(H5FD_onion_history_t)); + + hdr_out.version = H5FD_ONION_HEADER_VERSION_CURR; + + history_out.version = H5FD_ONION_HISTORY_VERSION_CURR; + history_out.n_revisions = 0; + history_out.record_locs = NULL; + + rev_out.version = H5FD_ONION_REVISION_RECORD_VERSION_CURR; + 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; + + /* Ingest onion header */ + + readsize = MIN(filesize, H5FD_ONION_ENCODED_SIZE_HEADER); + if (NULL == (buf = HDmalloc(readsize * sizeof(unsigned char)))) + TEST_ERROR; + if (H5FDread(raw_file, H5FD_MEM_DRAW, H5P_DEFAULT, 0, readsize, buf) < 0) + TEST_ERROR; + + readsize = H5FD__onion_header_decode(buf, &hdr_out); + if (0 == readsize) + TEST_ERROR; + if (H5FD_ONION_HEADER_VERSION_CURR != hdr_out.version) + TEST_ERROR; + /* Decode from the buffer to we can compare on BE systems */ + ui8p = (uint8_t *)(&buf[readsize - 4]); + UINT32DECODE(ui8p, buf_checksum); + if (hdr_out.checksum != buf_checksum) + 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.history_addr + hdr_out.history_size != filesize) + TEST_ERROR; + if (filter->origin_eof != hdr_out.origin_eof) + TEST_ERROR; + + HDfree(buf); + buf = NULL; + + /* Ingest history */ + + readsize = hdr_out.history_size; + if (NULL == (buf = HDmalloc(readsize * sizeof(unsigned char)))) + TEST_ERROR; + if (H5FDread(raw_file, H5FD_MEM_DRAW, H5P_DEFAULT, hdr_out.history_addr, readsize, buf) < 0) + TEST_ERROR; + + /* Initial read, get count of revisions */ + readsize = H5FD__onion_history_decode(buf, &history_out); + if (0 == readsize) + TEST_ERROR; + if (H5FD_ONION_HISTORY_VERSION_CURR != history_out.version) + TEST_ERROR; + /* Decode from the buffer to we can compare on BE systems */ + ui8p = (uint8_t *)(&buf[readsize - 4]); + UINT32DECODE(ui8p, buf_checksum); + if (history_out.checksum != buf_checksum) + TEST_ERROR; + if (history_out.checksum != H5_checksum_fletcher32(buf, readsize - 4)) + TEST_ERROR; + if (filter->n_revisions != history_out.n_revisions) + TEST_ERROR; + + /* Final read, populate pointers to revision records */ + history_out.record_locs = HDcalloc(history_out.n_revisions, sizeof(H5FD_onion_record_loc_t)); + if (NULL == history_out.record_locs) + TEST_ERROR; + if (H5FD__onion_history_decode(buf, &history_out) != readsize) + TEST_ERROR; + + /* Re-use buffer space to sanity-check checksum for record pointer(s). */ + HDassert(readsize >= sizeof(H5FD_onion_record_loc_t)); + for (size_t i = 0; i < history_out.n_revisions; i++) { + + uint64_t phys_addr; + uint64_t record_size; + + /* Do a checked assignment from the struct value into appropriately + * sized types. We don't have access to the H5F_t struct for this + * file, so we can't use the offset/length macros in H5Fprivate.h. + * + * Have to do an encode to get these values so the test passes on BE + * systems. + */ + H5_CHECKED_ASSIGN(phys_addr, uint64_t, history_out.record_locs[i].phys_addr, haddr_t); + H5_CHECKED_ASSIGN(record_size, uint64_t, history_out.record_locs[i].record_size, hsize_t); + + ui8p = (uint8_t *)buf; + UINT64ENCODE(ui8p, phys_addr); + + ui8p = (uint8_t *)(buf + 8); + UINT64ENCODE(ui8p, record_size); + + if (history_out.record_locs[i].checksum != H5_checksum_fletcher32(buf, 16)) + TEST_ERROR; + } + + HDfree(buf); + buf = NULL; + + /* Ingest revision(s) */ + + for (size_t i = 0; i < history_out.n_revisions; i++) { + H5FD_onion_record_loc_t *rpp = &history_out.record_locs[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; + + readsize = rpp->record_size; + if (NULL == (buf = HDmalloc((size_t)rpp->record_size))) + 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_VERSION_CURR != rev_out.version) + TEST_ERROR; + /* Decode from the buffer to we can compare on BE systems */ + ui8p = (uint8_t *)(&buf[readsize - 4]); + UINT32DECODE(ui8p, buf_checksum); + if (rev_out.checksum != buf_checksum) + TEST_ERROR; + if (rev_out.checksum != H5_checksum_fletcher32(buf, readsize - 4)) + TEST_ERROR; + if (erp->revision_num != rev_out.revision_num) + TEST_ERROR; + if (erp->parent_revision_num != rev_out.parent_revision_num) + TEST_ERROR; + if (erp->logical_eof != rev_out.logical_eof) + TEST_ERROR; + + /* Final read, get variable-length data */ + if (NULL == (rev_out.comment = HDmalloc((size_t)rev_out.comment_size))) + TEST_ERROR; + rev_out.archival_index.list = + HDcalloc(rev_out.archival_index.n_entries, sizeof(H5FD_onion_index_entry_t)); + if (NULL == rev_out.archival_index.list) + 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); + HDfree(rev_out.comment); + HDfree(rev_out.archival_index.list); + } + + HDfree(history_out.record_locs); + history_out.record_locs = NULL; + + return 0; + +error: + HDfree(buf); + HDfree(rev_out.comment); + HDfree(rev_out.archival_index.list); + HDfree(history_out.record_locs); + + 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" 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; + herr_t err_ret = FAIL; + 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 history_exp_bytes_size = 20; + unsigned char history_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 checksum = 0; + + /* Finish populating expected header bytes */ + ptr = hdr_exp_bytes + 8; /* WARNING: must match format */ + UINT32ENCODE(ptr, onion_info->page_size); + checksum = H5_checksum_fletcher32(hdr_exp_bytes, H5FD_ONION_ENCODED_SIZE_HEADER - 4); + ptr = hdr_exp_bytes + H5FD_ONION_ENCODED_SIZE_HEADER - 4; + UINT32ENCODE(ptr, checksum); + ptr = NULL; + + /* Finish populating expected history bytes */ + checksum = H5_checksum_fletcher32(history_exp_bytes, H5FD_ONION_ENCODED_SIZE_HISTORY - 4); + ptr = history_exp_bytes + H5FD_ONION_ENCODED_SIZE_HISTORY - 4; + UINT32ENCODE(ptr, checksum); + ptr = NULL; + + /* Look at h5 file: should have zero bytes */ + + file = H5FDopen(paths->canon, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF); + if (NULL == file) + TEST_ERROR; + + /* Size here is arbitrary */ + if (NULL == (act_buf = HDcalloc(1, 8))) + TEST_ERROR; + + /* Should fail when reading from an empty file */ + 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; + + HDfree(act_buf); + act_buf = NULL; + + if (H5FDclose(file) < 0) + TEST_ERROR; + file = NULL; + + /* 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 history */ + if (compare_file_bytes_exactly(paths->recovery, fapl_id, history_exp_bytes_size, history_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; + + return 0; + +error: + if (file != NULL) + (void)H5FDclose(file); + HDfree(act_buf); + + 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_VERSION_CURR, + H5P_DEFAULT, /* 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 * + *********/ + + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + if (NULL == (paths = onion_filepaths_init(basename))) + 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; + + /* Check if onion history for onion-open created file */ + if (NULL != vfile_raw) + TEST_ERROR; + + /* 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; + /* check if onionization (creation) not complete; nothing to open */ + if (vfile_ro != NULL) + TEST_ERROR; + + /* + * 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. */ + if (NULL == (buf = HDmalloc(4 * sizeof(char)))) + 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. */ + if (NULL == (buf = HDmalloc(buf_size * sizeof(char)))) + 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. */ + if (NULL == (buf = HDmalloc(a_list_size_s * sizeof(char)))) + 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_num = 0; + filter.revisions[0].parent_revision_num = 0; + filter.revisions[0].logical_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) { + /* Initial revision contains 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; + if (NULL == (buf = HDmalloc(a_list_size_s * 64 * sizeof(char)))) + 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; + } + else { + /* Initial revision has no data */ + if (H5FDget_eoa(vfile_ro, H5FD_MEM_DRAW) != 0) + TEST_ERROR; + if (H5FDget_eof(vfile_ro, H5FD_MEM_DRAW) != 0) + TEST_ERROR; + } + + if (H5FDclose(vfile_ro) < 0) + TEST_ERROR; + vfile_ro = NULL; + + /* + * CLEANUP + */ + + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + + 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); + } + + 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); + + H5E_BEGIN_TRY + { + H5Pclose(fapl_id); + } + H5E_END_TRY; + + 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_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]; + H5FD_onion_history_t history_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 * + *********/ + + history_out.version = H5FD_ONION_HISTORY_VERSION_CURR; + history_out.n_revisions = 0; + history_out.record_locs = NULL; + + if (NULL == (paths = onion_filepaths_init(basename))) + TEST_ERROR; + + if ((onion_info.backing_fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + + /* Empty first revision */ + about[0].truncate = TRUE; + about[0].revision_num = H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST; + about[0].comment = "first"; + about[0].n_writes = 0; + + about[1].truncate = FALSE; + about[1].revision_num = 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].truncate = FALSE; + about[2].revision_num = 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].truncate = FALSE; + about[3].revision_num = 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 */ + + /* THIS IS THE INITIAL FILE, SHOULD ONLY HAVE 8 BYTES */ + onion_info.revision_num = 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 (8 != H5FDget_eof(file, H5FD_MEM_DRAW)) { + HDprintf("\nEOF is not zero, it is: %" PRIuHADDR "\n", 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; + + /* Empty first revision */ + onion_info.revision_num = 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; + if (0 != H5FDget_eof(file, H5FD_MEM_DRAW)) { + HDprintf("\nEOF is not zero, it is: %" PRIuHADDR "\n", 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_num = 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 = a_off + a_list_size_s; + if (size != H5FDget_eof(file, H5FD_MEM_DRAW)) { + HDprintf("\nEOF is not %" PRIuHADDR ", it is: %" PRIuHADDR "\n", size, + H5FDget_eof(file, H5FD_MEM_DRAW)); + TEST_ERROR; + } + if (NULL == (buf = HDmalloc(size * sizeof(unsigned char)))) + 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 */ + if (NULL == (buf = HDmalloc(ONION_TEST_PAGE_SIZE_5 * sizeof(unsigned char)))) + 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_num = 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; + if (NULL == (buf = HDmalloc(size * sizeof(unsigned char)))) + 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_num = 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; + 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 */ + + /* 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_num = 0; + filter.revisions[0].parent_revision_num = 0; + filter.revisions[0].logical_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_num = 1; + filter.revisions[1].parent_revision_num = 0; + filter.revisions[1].logical_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_num = 2; + filter.revisions[2].parent_revision_num = 1; + filter.revisions[2].logical_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_num = 3; + filter.revisions[3].parent_revision_num = 2; + filter.revisions[3].logical_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 */ + + if (H5Pclose(onion_info.backing_fapl_id) < 0) + TEST_ERROR; + + 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); + } + + HDfree(history_out.record_locs); + HDfree(buf); + + if (file != NULL) + (void)H5FDclose(file); + + H5E_BEGIN_TRY + { + H5Pclose(fapl_id); + H5Pclose(onion_info.backing_fapl_id); + } + H5E_END_TRY; + + 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 + * + *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + */ +// TODO: Modify to create initial file without onion +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 (i != 0 && about[i].truncate == TRUE) + goto error; + + if (TRUE == about[i].truncate) + flags |= H5F_ACC_CREAT | H5F_ACC_TRUNC; + + onion_info_p->revision_num = about[i].revision_num; + if (about[i].comment != NULL) { + j = MIN(HDstrlen(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 */ + if (NULL == (buf_vfy = HDmalloc(wi->size * sizeof(unsigned char)))) + 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("%02zx %c %c\n", z, _buf[z], buf_vfy[z]); + HDfflush(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); + + H5E_BEGIN_TRY + { + H5Pclose(fapl_id); + } + H5E_END_TRY; + + 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_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]; + H5FD_onion_header_t hdr_out; + H5FD_onion_history_t history_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.version = H5FD_ONION_HEADER_VERSION_CURR; + history_out.version = H5FD_ONION_HISTORY_VERSION_CURR; + history_out.n_revisions = 0; + history_out.record_locs = NULL; + + if ((onion_info.backing_fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + if (NULL == (paths = onion_filepaths_init(basename))) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + + about[0].truncate = TRUE; + about[0].revision_num = 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].truncate = FALSE; + about[1].revision_num = 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 */ + if (NULL == (buf = HDmalloc(b_list_size_s * sizeof(unsigned char)))) + 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: %" PRIu64 "\n", a_off); + HDputs("i exp act"); + for (k = 0; k < b_list_size_s; k++) { + HDprintf("%3zu:: %c : %c\n", k, (k < a_off) ? ' ' : a_list_s[k - a_off], buf[k]); + } + HDfflush(stdout); + TEST_ERROR; + } + if (HDmemcmp(b_list_s, buf, a_off) != 0) { + size_t k; + HDprintf("aoff: %" PRIu64 "\n", a_off); + HDputs("i exp act"); + for (k = 0; k < b_list_size_s; k++) { + HDprintf("%3zu:: %c : %c\n", k, (k < a_off) ? b_list_s[k] : ' ', buf[k]); + } + HDfflush(stdout); + TEST_ERROR; + } + if (H5FDclose(file) < 0) + TEST_ERROR; + file = NULL; + HDfree(buf); + buf = NULL; + + /* Inspect history construction */ + + if (NULL == (file = H5FDopen(paths->onion, H5F_ACC_RDONLY, onion_info.backing_fapl_id, HADDR_UNDEF))) + TEST_ERROR; + if (H5FDset_eoa(file, H5FD_MEM_DRAW, H5FDget_eof(file, H5FD_MEM_DRAW)) < 0) + TEST_ERROR; + + if (NULL == (buf = HDmalloc(H5FD_ONION_ENCODED_SIZE_HEADER))) + TEST_ERROR; + if (H5FDread(file, H5FD_MEM_DRAW, H5P_DEFAULT, 0, H5FD_ONION_ENCODED_SIZE_HEADER, buf) < 0) + TEST_ERROR; + if (H5FD__onion_header_decode(buf, &hdr_out) != H5FD_ONION_ENCODED_SIZE_HEADER) + TEST_ERROR; + if (hdr_out.history_addr & ((1 << 5) - 1)) /* 5::PAGE_SIZE_5 */ + TEST_ERROR; + HDfree(buf); + buf = NULL; + + if (NULL == (buf = HDmalloc(hdr_out.history_size))) + TEST_ERROR; + if (H5FDread(file, H5FD_MEM_DRAW, H5P_DEFAULT, hdr_out.history_addr, hdr_out.history_size, buf) < 0) + TEST_ERROR; + if (H5FD__onion_history_decode(buf, &history_out) != hdr_out.history_size) + TEST_ERROR; + if (history_out.n_revisions != 2) + TEST_ERROR; + history_out.record_locs = HDcalloc(history_out.n_revisions, sizeof(H5FD_onion_record_loc_t)); + if (NULL == history_out.record_locs) + TEST_ERROR; + if (H5FD__onion_history_decode(buf, &history_out) != hdr_out.history_size) + TEST_ERROR; + HDfree(buf); + buf = NULL; + + for (i = 0; i < history_out.n_revisions; i++) { + H5FD_onion_record_loc_t *rloc = &history_out.record_locs[i]; + if (rloc->phys_addr & ((1 << 5) - 1)) /* 5::PAGE_SIZE_5 */ + TEST_ERROR; + /* TODO: check phys_addr of each page entry? */ + } + + HDfree(history_out.record_locs); + history_out.record_locs = NULL; + + if (H5FDclose(file) < 0) + TEST_ERROR; + file = NULL; + + /* CLEANUP */ + + if (H5Pclose(onion_info.backing_fapl_id) < 0) + TEST_ERROR; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + onion_filepaths_destroy(paths); + + HDfree(buf); + + PASSED(); + return 0; + +error: + + if (paths != NULL) { + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + onion_filepaths_destroy(paths); + } + + HDfree(history_out.record_locs); + HDfree(buf); + + if (file != NULL) + (void)H5FDclose(file); + + H5E_BEGIN_TRY + { + H5Pclose(fapl_id); + H5Pclose(onion_info.backing_fapl_id); + } + H5E_END_TRY; + + 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 = "integration_2d.h5"; + hid_t fapl_id = H5I_INVALID_HID; + hid_t file_id = H5I_INVALID_HID; + hid_t file = H5I_INVALID_HID; + hid_t space = H5I_INVALID_HID; + hid_t dset = H5I_INVALID_HID; + hid_t dcpl = H5I_INVALID_HID; + struct onion_filepaths *paths = NULL; + H5FD_onion_fapl_info_t onion_info = { + 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, was H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT */ + "initial commit" /* comment */ + }; + hsize_t dims[2] = {128, 256}; + hsize_t maxdims[2] = {H5S_UNLIMITED, H5S_UNLIMITED}; + hsize_t chunk[2] = {4, 4}; + int fillval; + struct { + int arr[128][256]; + } *wdata = NULL; + struct { + int arr[128][256]; + } *rdata = NULL; + struct { + int arr[128][256]; + } *dset_data = NULL; + + TESTING("onion-created two dimensional HDF5 file with revisions"); + + /* SETUP */ + + if (NULL == (wdata = HDcalloc(1, sizeof(*wdata)))) + TEST_ERROR; + if (NULL == (rdata = HDcalloc(1, sizeof(*rdata)))) + TEST_ERROR; + if (NULL == (dset_data = HDcalloc(1, sizeof(*dset_data)))) + TEST_ERROR; + + if ((onion_info.backing_fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + if (NULL == (paths = onion_filepaths_init(basename))) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + + /*---------------------------------------------------------------------- + * Create the skeleton file (create the file without Onion VFD) + *---------------------------------------------------------------------- + */ + + /* Initialize data */ + for (int i = 0; i < 128; i++) + for (int j = 0; j < 256; j++) + wdata->arr[i][j] = i * j - j; + + /* Create a new file using the default properties */ + if ((file = H5Fcreate(paths->canon, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Create dataspace with unlimited dimensions */ + if ((space = H5Screate_simple(2, dims, maxdims)) < 0) + TEST_ERROR; + + /* Create the dataset creation property list, and set the chunk + * size + */ + if ((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) + TEST_ERROR; + if (H5Pset_chunk(dcpl, 2, chunk) < 0) + TEST_ERROR; + + /* Set the fill value for the dataset */ + fillval = 99; + if (H5Pset_fill_value(dcpl, H5T_NATIVE_INT, &fillval) < 0) + TEST_ERROR; + + /* 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. + */ + if (H5Pset_alloc_time(dcpl, H5D_ALLOC_TIME_EARLY) < 0) + TEST_ERROR; + + /* Create the dataset using the dataset creation property list */ + if ((dset = H5Dcreate2(file, "DS1", H5T_STD_I32LE, space, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Write the data to the dataset */ + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, wdata) < 0) + TEST_ERROR; + + /* Close and release resources */ + if (H5Pclose(dcpl) < 0) + TEST_ERROR; + if (H5Dclose(dset) < 0) + TEST_ERROR; + if (H5Sclose(space) < 0) + TEST_ERROR; + if (H5Fclose(file) < 0) + TEST_ERROR; + + /*---------------------------------------------------------------------- + * First revision: open the file with Onion VFD and change the data + *---------------------------------------------------------------------- + */ + if ((file_id = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + if ((dset = H5Dopen2(file_id, "DS1", H5P_DEFAULT)) < 0) + TEST_ERROR; + + for (int i = 0; i < 128; i++) + for (int j = 0; j < 256; j++) + dset_data->arr[i][j] = i * 6 + j + 1; + + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset_data) < 0) + TEST_ERROR; + + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + if (H5Fclose(file_id) < 0) + TEST_ERROR; + file_id = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Second revision: open the file with Onion VFD and change the data + *---------------------------------------------------------------------- + */ + if ((file_id = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + if ((dset = H5Dopen2(file_id, "DS1", H5P_DEFAULT)) < 0) + TEST_ERROR; + + for (int i = 0; i < 128; i++) + for (int j = 0; j < 256; j++) + dset_data->arr[i][j] = i * 3 + j + 5; + + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset_data) < 0) + TEST_ERROR; + + /* CLEANUP */ + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + if (H5Fclose(file_id) < 0) + TEST_ERROR; + file_id = H5I_INVALID_HID; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + fapl_id = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Start to verify the revision + *---------------------------------------------------------------------- + */ + /*---------------------------------------------------------------------- + * Verify the original file + *---------------------------------------------------------------------- + */ + onion_info.revision_num = 0; + + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + if ((file_id = H5Fopen(paths->canon, H5F_ACC_RDONLY, fapl_id)) < 0) + TEST_ERROR; + + if ((dset = H5Dopen2(file_id, "DS1", H5P_DEFAULT)) < 0) + TEST_ERROR; + + if (H5Dread(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rdata) < 0) + TEST_ERROR; + + for (int i = 0; i < 128; i++) { + for (int j = 0; j < 256; j++) { + int expected = i * j - j; + if (rdata->arr[i][j] != expected) { + HDprintf("ERROR!!! Expected: %d, Got: %d\n", expected, rdata->arr[i][j]); + HDfflush(stdout); + TEST_ERROR; + } + } + } + + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + if (H5Fclose(file_id) < 0) + TEST_ERROR; + file_id = H5I_INVALID_HID; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + fapl_id = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Verify the first revision + *---------------------------------------------------------------------- + */ + onion_info.revision_num = 1; + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + if ((file_id = H5Fopen(paths->canon, H5F_ACC_RDONLY, fapl_id)) < 0) + TEST_ERROR; + if ((dset = H5Dopen2(file_id, "DS1", H5P_DEFAULT)) < 0) + TEST_ERROR; + if (H5Dread(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rdata) < 0) + TEST_ERROR; + + for (int i = 0; i < 128; i++) { + for (int j = 0; j < 256; j++) { + int expected = i * 6 + j + 1; + if (rdata->arr[i][j] != expected) { + HDprintf("ERROR!!! Expected: %d, Got: %d\n", expected, rdata->arr[i][j]); + HDfflush(stdout); + TEST_ERROR; + } + } + } + + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + if (H5Fclose(file_id) < 0) + TEST_ERROR; + file_id = H5I_INVALID_HID; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + fapl_id = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Verify the second revision + *---------------------------------------------------------------------- + */ + onion_info.revision_num = 2; + + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + if ((file_id = H5Fopen(paths->canon, H5F_ACC_RDONLY, fapl_id)) < 0) + TEST_ERROR; + if ((dset = H5Dopen2(file_id, "DS1", H5P_DEFAULT)) < 0) + TEST_ERROR; + if (H5Dread(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rdata) < 0) + TEST_ERROR; + + for (int i = 0; i < 128; i++) { + for (int j = 0; j < 256; j++) { + int expected = i * 3 + j + 5; + if (rdata->arr[i][j] != expected) { + HDprintf("ERROR!!! Expected: %d, Got: %d\n", expected, rdata->arr[i][j]); + HDfflush(stdout); + TEST_ERROR; + } + else { + HDfflush(stdout); + } + } + } + + if (H5Dclose(dset) < 0) + TEST_ERROR; + if (H5Fclose(file_id) < 0) + TEST_ERROR; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + + if (H5Pclose(onion_info.backing_fapl_id) < 0) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + onion_filepaths_destroy(paths); + + HDfree(wdata); + HDfree(rdata); + HDfree(dset_data); + + PASSED(); + return 0; + +error: + + if (paths != NULL) { + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + onion_filepaths_destroy(paths); + } + + H5E_BEGIN_TRY + { + H5Dclose(dset); + H5Fclose(file_id); + H5Pclose(fapl_id); + H5Pclose(onion_info.backing_fapl_id); + } + H5E_END_TRY; + + HDfree(wdata); + HDfree(rdata); + HDfree(dset_data); + + return -1; +} /* end test_integration_create() */ + +static int +test_integration_create_simple(void) +{ + const char *basename = "integration_1d.h5"; + hid_t fapl_id = H5I_INVALID_HID; + struct onion_filepaths *paths = NULL; + H5FD_onion_fapl_info_t onion_info = { + 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, was H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT */ + "initial commit" /* comment */ + }; + hid_t file_id = H5I_INVALID_HID; + hid_t file = H5I_INVALID_HID; + hid_t space = H5I_INVALID_HID; + hid_t dset = H5I_INVALID_HID; + hid_t dcpl = H5I_INVALID_HID; + hsize_t dims[2] = {1, ONE_DIM_SIZE}; + hsize_t maxdims[2] = {1, ONE_DIM_SIZE}; + int fillval; + struct { + int arr[ONE_DIM_SIZE]; + } *wdata = NULL; + struct { + int arr[ONE_DIM_SIZE]; + } *rdata = NULL; + struct { + int arr[ONE_DIM_SIZE]; + } *dset_data = NULL; + + TESTING("onion-created one-dimensional HDF5 file with revisions"); + + /* Setup */ + if (NULL == (wdata = HDcalloc(1, sizeof(*wdata)))) + TEST_ERROR; + if (NULL == (rdata = HDcalloc(1, sizeof(*rdata)))) + TEST_ERROR; + if (NULL == (dset_data = HDcalloc(1, sizeof(*dset_data)))) + TEST_ERROR; + + if ((onion_info.backing_fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + if (NULL == (paths = onion_filepaths_init(basename))) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + + /*---------------------------------------------------------------------- + * Create the skeleton file (create the file without Onion VFD) + *---------------------------------------------------------------------- + */ + /* Initialize data */ + for (int i = 0; i < ONE_DIM_SIZE; i++) + wdata->arr[i] = i; + + /* Create a new file using the default properties */ + if ((file = H5Fcreate(paths->canon, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Create dataspace with unlimited dimensions*/ + if ((space = H5Screate_simple(2, dims, maxdims)) < 0) + TEST_ERROR; + + /* Create the dataset creation property list */ + if ((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) + TEST_ERROR; + + /* Set the fill value for the dataset */ + fillval = 99; + if (H5Pset_fill_value(dcpl, H5T_NATIVE_INT, &fillval) < 0) + TEST_ERROR; + + /* 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. + */ + if (H5Pset_alloc_time(dcpl, H5D_ALLOC_TIME_EARLY) < 0) + TEST_ERROR; + + /* Create the dataset using the dataset creation property list */ + if ((dset = H5Dcreate2(file, "DS1", H5T_STD_I32LE, space, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Write the data to the dataset */ + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, wdata) < 0) + TEST_ERROR; + + /* Close everything */ + if (H5Pclose(dcpl) < 0) + TEST_ERROR; + if (H5Dclose(dset) < 0) + TEST_ERROR; + if (H5Sclose(space) < 0) + TEST_ERROR; + if (H5Fclose(file) < 0) + TEST_ERROR; + + /*---------------------------------------------------------------------- + * First revision: open the file with Onion VFD and change the data + *---------------------------------------------------------------------- + */ + if ((file_id = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + if ((dset = H5Dopen2(file_id, "DS1", H5P_DEFAULT)) < 0) + TEST_ERROR; + + for (int i = 0; i < ONE_DIM_SIZE; i++) + dset_data->arr[i] = i + ONE_DIM_SIZE; + + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset_data) < 0) + TEST_ERROR; + + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + if (H5Fclose(file_id) < 0) + TEST_ERROR; + file_id = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Second revision: open the file with Onion VFD and change the data + *---------------------------------------------------------------------- + */ + if ((file_id = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + if ((dset = H5Dopen2(file_id, "DS1", H5P_DEFAULT)) < 0) + TEST_ERROR; + + for (int i = 0; i < ONE_DIM_SIZE; i++) + dset_data->arr[i] = i + 2048; + + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset_data) < 0) + TEST_ERROR; + + /* CLEANUP */ + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + if (H5Fclose(file_id) < 0) + TEST_ERROR; + file_id = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Third revision: open the file with Onion VFD and change the data + *---------------------------------------------------------------------- + */ + if ((file_id = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + if ((dset = H5Dopen2(file_id, "DS1", H5P_DEFAULT)) < 0) + TEST_ERROR; + + for (int i = 0; i < ONE_DIM_SIZE; i += 20) + dset_data->arr[i] = i + 3072; + + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset_data) < 0) + TEST_ERROR; + + /* CLEANUP */ + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + if (H5Fclose(file_id) < 0) + TEST_ERROR; + file_id = H5I_INVALID_HID; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + fapl_id = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Start to verify the revision + *---------------------------------------------------------------------- + */ + /*---------------------------------------------------------------------- + * Verify the second revision + *---------------------------------------------------------------------- + */ + onion_info.revision_num = 2; + + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + if ((file_id = H5Fopen(paths->canon, H5F_ACC_RDONLY, fapl_id)) < 0) + TEST_ERROR; + if ((dset = H5Dopen2(file_id, "DS1", H5P_DEFAULT)) < 0) + TEST_ERROR; + + if (H5Dread(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rdata) < 0) + TEST_ERROR; + + for (int i = 0; i < ONE_DIM_SIZE; i += 20) { + int expected = i + 2048; + if (rdata->arr[i] != expected) { + HDprintf("ERROR!!! Expected: %d, Got: %d\n", expected, rdata->arr[i]); + TEST_ERROR; + } + } + + /* Close everything */ + if (H5Dclose(dset) < 0) + TEST_ERROR; + if (H5Fclose(file_id) < 0) + TEST_ERROR; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + if (H5Pclose(onion_info.backing_fapl_id) < 0) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + onion_filepaths_destroy(paths); + + HDfree(wdata); + HDfree(rdata); + HDfree(dset_data); + + PASSED(); + return 0; + +error: + + if (paths != NULL) { + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + onion_filepaths_destroy(paths); + } + + H5E_BEGIN_TRY + { + H5Dclose(dset); + H5Fclose(file_id); + H5Pclose(fapl_id); + H5Pclose(onion_info.backing_fapl_id); + } + H5E_END_TRY; + + HDfree(wdata); + HDfree(rdata); + HDfree(dset_data); + + return -1; +} /* end test_integration_create_simple() */ + +static int +test_integration_create_delete_objects(void) +{ + const char *basename = "integration_objs.h5"; + hid_t fapl_id = H5I_INVALID_HID; + struct onion_filepaths *paths = NULL; + H5FD_onion_fapl_info_t onion_info = { + 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, was H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT */ + "initial commit" /* comment */ + }; + hid_t group_id = H5I_INVALID_HID; + hid_t attr_space_id = H5I_INVALID_HID; + hid_t attr_id = H5I_INVALID_HID; + hsize_t attr_dim[1] = {4}; + + hid_t file = H5I_INVALID_HID; + hid_t space = H5I_INVALID_HID; + hid_t dset = H5I_INVALID_HID; + hid_t dcpl = H5I_INVALID_HID; + hsize_t dims[2] = {4, 4}, maxdims[2] = {H5S_UNLIMITED, H5S_UNLIMITED}, chunk[2] = {4, 4}; + int wdata[4][4], /* Write buffer */ + fillval, i, j; + + TESTING("onion-created HDF5 file with revisions testing addition and deletion of objects"); + + /* Set up */ + if ((onion_info.backing_fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + if (NULL == (paths = onion_filepaths_init(basename))) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + + /*---------------------------------------------------------------------- + * Create the skeleton file (create the file without Onion VFD) + *---------------------------------------------------------------------- + */ + + /* Initialize data */ + for (i = 0; i < 4; i++) + for (j = 0; j < 4; j++) + wdata[i][j] = i + j; + + /* Create a new file using the default properties */ + if ((file = H5Fcreate(paths->canon, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Create dataspace with unlimited dimensions */ + if ((space = H5Screate_simple(2, dims, maxdims)) < 0) + TEST_ERROR; + + /* Create the dataset creation property list, and set the chunk + * size + */ + if ((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) + TEST_ERROR; + if (H5Pset_chunk(dcpl, 2, chunk) < 0) + TEST_ERROR; + + /* Set the fill value for the dataset */ + fillval = 99; + if (H5Pset_fill_value(dcpl, H5T_NATIVE_INT, &fillval) < 0) + TEST_ERROR; + + /* 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. + */ + if (H5Pset_alloc_time(dcpl, H5D_ALLOC_TIME_EARLY) < 0) + TEST_ERROR; + + /* Create the dataset using the dataset creation property list */ + if ((dset = H5Dcreate2(file, "DS1", H5T_STD_I32LE, space, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Write the data to the dataset */ + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, wdata) < 0) + TEST_ERROR; + + if (H5Dclose(dset) < 0) + TEST_ERROR; + if (H5Fclose(file) < 0) + TEST_ERROR; + + /*---------------------------------------------------------------------- + * First revision: open the file with Onion VFD and add a dataset (DS2) to the file + *---------------------------------------------------------------------- + */ + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + /* Create the dataset using the dataset creation property list */ + if ((dset = H5Dcreate2(file, "DS2", H5T_STD_I32LE, space, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Write the data to the dataset */ + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, wdata) < 0) + TEST_ERROR; + + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Second revision: open the file with Onion VFD and remove the dataset (DS2), + * which was added during the first revision. + *---------------------------------------------------------------------- + */ + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + if (H5Ldelete(file, "DS2", H5P_DEFAULT) < 0) + TEST_ERROR; + + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Third revision: open the file with Onion VFD and add an attribute to the file + *---------------------------------------------------------------------- + */ + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + /* Create dataspace for attribute */ + if ((attr_space_id = H5Screate_simple(1, attr_dim, NULL)) < 0) + TEST_ERROR; + + if ((attr_id = + H5Acreate2(file, "file_attribute", H5T_STD_I32LE, attr_space_id, H5P_DEFAULT, H5P_DEFAULT)) < 0) + TEST_ERROR; + + if (H5Sclose(attr_space_id) < 0) + TEST_ERROR; + if (H5Aclose(attr_id) < 0) + TEST_ERROR; + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Fourth revision: open the file with Onion VFD and delete the attribute + *---------------------------------------------------------------------- + */ + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + if (H5Adelete(file, "file_attribute") < 0) + TEST_ERROR; + + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Fifth revision: open the file with Onion VFD and add a group to the file + *---------------------------------------------------------------------- + */ + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + if ((group_id = H5Gcreate2(file, "new_group", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) + TEST_ERROR; + + if (H5Gclose(group_id) < 0) + TEST_ERROR; + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Sixth revision: open the file with Onion VFD and delete the newly added group + *---------------------------------------------------------------------- + */ + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + if (H5Ldelete(file, "new_group", H5P_DEFAULT) < 0) + TEST_ERROR; + + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Start to verify the revision + *---------------------------------------------------------------------- + */ + /*---------------------------------------------------------------------- + * Verify the first revision: it should have the second dataset (DS2) + *---------------------------------------------------------------------- + */ + onion_info.revision_num = 1; + + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + /* The second dataset (DS2) should exist */ + if (H5Lexists(file, "DS2", H5P_DEFAULT) <= 0) + TEST_ERROR; + + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /*------------------------------------------------------------------------ + * Verify the second revision: the second dataset (DS2) should be removed + *------------------------------------------------------------------------ + */ + onion_info.revision_num = 2; + + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + /* The second dataset (DS2) shouldn't exist */ + if (H5Lexists(file, "DS2", H5P_DEFAULT) > 0) + TEST_ERROR; + + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /*------------------------------------------------------------------------- + * Verify the third revision: the file attribute (file_attribute) should exist + *------------------------------------------------------------------------- + */ + onion_info.revision_num = 3; + + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + /* The file attribute should exist */ + if (H5Aexists(file, "file_attribute") <= 0) + TEST_ERROR; + + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /*------------------------------------------------------------------------- + * Verify the fourth revision: the file attribute (file_attribute) should be removed + *------------------------------------------------------------------------- + */ + onion_info.revision_num = 4; + + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + /* The file attribute should be removed */ + if (H5Aexists(file, "file_attribute") > 0) + TEST_ERROR; + + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /*------------------------------------------------------------------------- + * Verify the fifth revision: the group (new_group) should exist + *------------------------------------------------------------------------- + */ + onion_info.revision_num = 5; + + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + /* The new group should exist */ + if (H5Lexists(file, "new_group", H5P_DEFAULT) <= 0) + TEST_ERROR; + + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /*------------------------------------------------------------------------- + * Verify the sixth revision: the group (new_group) should be removed + *------------------------------------------------------------------------- + */ + onion_info.revision_num = 6; + + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + /* The new group should exist */ + if (H5Lexists(file, "new_group", H5P_DEFAULT) > 0) + TEST_ERROR; + + /* Close everything */ + if (H5Fclose(file) < 0) + TEST_ERROR; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + if (H5Pclose(dcpl) < 0) + TEST_ERROR; + if (H5Sclose(space) < 0) + TEST_ERROR; + if (H5Pclose(onion_info.backing_fapl_id) < 0) + TEST_ERROR; + + 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); + } + + H5E_BEGIN_TRY + { + H5Dclose(dset); + H5Fclose(file); + H5Pclose(fapl_id); + H5Sclose(space); + H5Pclose(onion_info.backing_fapl_id); + } + H5E_END_TRY; + + return -1; +} /* end test_integration_create_delete_objects */ + +static int +test_integration_dset_extension(void) +{ + const char *basename = "integration_dset_ext.h5"; + hid_t fapl_id = H5I_INVALID_HID; + struct onion_filepaths *paths = NULL; + H5FD_onion_fapl_info_t onion_info = { + 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, was H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT */ + "initial commit" /* comment */ + }; + hid_t file = H5I_INVALID_HID; + hid_t space = H5I_INVALID_HID; + hid_t dset_space = H5I_INVALID_HID; + hid_t dset = H5I_INVALID_HID; + hid_t dcpl = H5I_INVALID_HID; + hsize_t dims[2] = {4, 4}; + hsize_t maxdims[2] = {H5S_UNLIMITED, H5S_UNLIMITED}; + hsize_t chunk[2] = {4, 4}; + hsize_t size[2]; + hsize_t offset[2]; + int wdata[4][4]; /* Write buffer */ + int fillval; + int rdata[4][4]; /* Read buffer */ + + TESTING("onion-created HDF5 file with revisions testing dataset extension"); + + /* Setup */ + if ((onion_info.backing_fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + if (NULL == (paths = onion_filepaths_init(basename))) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + + /*---------------------------------------------------------------------- + * Create the skeleton file (create the file without Onion VFD) + *---------------------------------------------------------------------- + */ + + /* Initialize data */ + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + wdata[i][j] = i + j; + + /* Create a new file using the default properties */ + if ((file = H5Fcreate(paths->canon, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Create dataspace with unlimited dimensions*/ + if ((space = H5Screate_simple(2, dims, maxdims)) < 0) + TEST_ERROR; + + /* Create the dataset creation property list, and set the chunk + * size + */ + if ((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) + TEST_ERROR; + if (H5Pset_chunk(dcpl, 2, chunk) < 0) + TEST_ERROR; + + /* Set the fill value for the dataset */ + fillval = 99; + if (H5Pset_fill_value(dcpl, H5T_NATIVE_INT, &fillval) < 0) + TEST_ERROR; + + /* 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. + */ + if (H5Pset_alloc_time(dcpl, H5D_ALLOC_TIME_EARLY) < 0) + TEST_ERROR; + + /* Create the dataset using the dataset creation property list */ + if ((dset = H5Dcreate2(file, "DS1", H5T_STD_I32LE, space, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Write the data to the dataset */ + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, wdata) < 0) + TEST_ERROR; + + if (H5Dclose(dset) < 0) + TEST_ERROR; + if (H5Fclose(file) < 0) + TEST_ERROR; + + /*---------------------------------------------------------------------- + * First revision: open the file with Onion VFD and extend the dataset + *---------------------------------------------------------------------- + */ + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + /* Open the dataset */ + if ((dset = H5Dopen2(file, "DS1", H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Extend the dataset and double the rows */ + size[0] = 2 * dims[0]; + size[1] = dims[1]; + if (H5Dset_extent(dset, size) < 0) + TEST_ERROR; + + if ((dset_space = H5Dget_space(dset)) < 0) + TEST_ERROR; + + offset[0] = dims[0]; + offset[1] = 0; + if (H5Sselect_hyperslab(dset_space, H5S_SELECT_SET, offset, NULL, dims, NULL) < 0) + TEST_ERROR; + + /* Write the data to the dataset. */ + if (H5Dwrite(dset, H5T_NATIVE_INT, space, dset_space, H5P_DEFAULT, wdata) < 0) + TEST_ERROR; + + if (H5Sclose(dset_space) < 0) + TEST_ERROR; + + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Second revision: open the file with Onion VFD and shrink the dataset + *---------------------------------------------------------------------- + */ + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + /* Open the dataset */ + if ((dset = H5Dopen2(file, "DS1", H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Extend the dataset and shrink back the size */ + if (H5Dset_extent(dset, dims) < 0) + TEST_ERROR; + + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Start to verify the revision + *---------------------------------------------------------------------- + */ + /*---------------------------------------------------------------------- + * Verify the first revision: it should have the extended data + *---------------------------------------------------------------------- + */ + onion_info.revision_num = 1; + + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + /* Open the dataset */ + if ((dset = H5Dopen2(file, "DS1", H5P_DEFAULT)) < 0) + TEST_ERROR; + + if ((dset_space = H5Dget_space(dset)) < 0) + TEST_ERROR; + + offset[0] = dims[0]; + offset[1] = 0; + if (H5Sselect_hyperslab(dset_space, H5S_SELECT_SET, offset, NULL, dims, NULL) < 0) + TEST_ERROR; + + if (H5Dread(dset, H5T_NATIVE_INT, space, dset_space, H5P_DEFAULT, rdata) < 0) + TEST_ERROR; + + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + if (rdata[i][j] != wdata[i][j]) + TEST_ERROR; + + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Verify the second revision: it should have the original data + *---------------------------------------------------------------------- + */ + onion_info.revision_num = 2; + + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + /* Open the dataset */ + dset = H5Dopen2(file, "DS1", H5P_DEFAULT); + + if ((dset_space = H5Dget_space(dset)) < 0) + TEST_ERROR; + + if (H5Dread(dset, H5T_NATIVE_INT, space, dset_space, H5P_DEFAULT, rdata) < 0) + TEST_ERROR; + + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + if (rdata[i][j] != wdata[i][j]) + TEST_ERROR; + + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /* Close and release resources. */ + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + if (H5Pclose(dcpl) < 0) + TEST_ERROR; + if (H5Sclose(space) < 0) + TEST_ERROR; + if (H5Pclose(onion_info.backing_fapl_id) < 0) + TEST_ERROR; + + 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); + } + + H5E_BEGIN_TRY + { + H5Dclose(dset); + H5Fclose(file); + H5Pclose(fapl_id); + H5Pclose(onion_info.backing_fapl_id); + } + H5E_END_TRY; + + return -1; +} /* end test_integration_dset_extension */ + +static int +test_integration_ctl(void) +{ + const char *basename = "integration_ctl.h5"; + hid_t file = H5I_INVALID_HID; + hid_t space = H5I_INVALID_HID; + hid_t dset = H5I_INVALID_HID; + hid_t dcpl = H5I_INVALID_HID; + hid_t fapl_id = H5I_INVALID_HID; + hsize_t dims[2] = {4, 4}; + hsize_t maxdims[2] = {H5S_UNLIMITED, H5S_UNLIMITED}; + hsize_t chunk[2] = {4, 4}; + int wdata[4][4]; /* Write buffer */ + int fillval; + struct onion_filepaths *paths = NULL; + H5FD_onion_fapl_info_t onion_info = { + 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, was H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT */ + "initial commit" /* comment */ + }; + uint64_t revision_count; + + TESTING("onion-created HDF5 file with revisions testing H5FDctl"); + + /* Set up */ + if ((onion_info.backing_fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + if (NULL == (paths = onion_filepaths_init(basename))) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + + /*---------------------------------------------------------------------- + * Create the skeleton file (create the file without Onion VFD) + *---------------------------------------------------------------------- + */ + + /* Initialize data */ + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + wdata[i][j] = i + j; + + /* Create a new file using the default properties */ + if ((file = H5Fcreate(paths->canon, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Create dataspace with unlimited dimensions */ + if ((space = H5Screate_simple(2, dims, maxdims)) < 0) + TEST_ERROR; + + /* Create the dataset creation property list, and set the chunk size */ + if ((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) + TEST_ERROR; + if (H5Pset_chunk(dcpl, 2, chunk) < 0) + TEST_ERROR; + + /* Set the fill value for the dataset */ + fillval = 99; + if (H5Pset_fill_value(dcpl, H5T_NATIVE_INT, &fillval) < 0) + TEST_ERROR; + + /* 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. + */ + if (H5Pset_alloc_time(dcpl, H5D_ALLOC_TIME_EARLY) < 0) + TEST_ERROR; + + /* Create the dataset using the dataset creation property list */ + if ((dset = H5Dcreate2(file, "DS1", H5T_STD_I32LE, space, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Write the data to the dataset */ + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, wdata) < 0) + TEST_ERROR; + + if (H5Dclose(dset) < 0) + TEST_ERROR; + if (H5Fclose(file) < 0) + TEST_ERROR; + + /*---------------------------------------------------------------------- + * First revision: open the file with Onion VFD and add a dataset (DS2) to the file + *---------------------------------------------------------------------- + */ + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + /* Create the dataset using the dataset creation property list */ + if ((dset = H5Dcreate2(file, "DS2", H5T_STD_I32LE, space, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Write the data to the dataset */ + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, wdata) < 0) + TEST_ERROR; + + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Second revision: open the file with Onion VFD and remove the dataset (DS2), + * which was added during the first revision. + *---------------------------------------------------------------------- + */ + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + if (H5Ldelete(file, "DS2", H5P_DEFAULT) < 0) + TEST_ERROR; + + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Start to verify the number of revisions + *---------------------------------------------------------------------- + */ + if (H5FDonion_get_revision_count(basename, fapl_id, &revision_count) < 0) + TEST_ERROR; + + if (2 != revision_count) + TEST_ERROR; + + /* Close and release resources */ + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + if (H5Pclose(dcpl) < 0) + TEST_ERROR; + if (H5Sclose(space) < 0) + TEST_ERROR; + if (H5Pclose(onion_info.backing_fapl_id) < 0) + TEST_ERROR; + + 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); + } + + H5E_BEGIN_TRY + { + H5Dclose(dset); + H5Fclose(file); + H5Pclose(fapl_id); + H5Pclose(onion_info.backing_fapl_id); + } + H5E_END_TRY; + + return -1; +} + +static int +test_integration_reference(void) +{ + const char *basename = "integration_refer.h5"; + hid_t file = H5I_INVALID_HID; + hid_t group = H5I_INVALID_HID; + hid_t space = H5I_INVALID_HID; + hid_t space2 = H5I_INVALID_HID; + hid_t space_ref = H5I_INVALID_HID; + hid_t dset = H5I_INVALID_HID; + hid_t dset2 = H5I_INVALID_HID; + hid_t fapl_id = H5I_INVALID_HID; + hsize_t dims[2] = {4, 4}; + hsize_t dim_ref[1] = {2}; + int wdata[4][4]; /* Write buffer */ + int rdata[4][4]; /* Read buffer */ + H5R_ref_t wbuf[2]; + H5R_ref_t rbuf[2]; + H5O_type_t obj_type; + hsize_t start[2]; + hsize_t stride[2]; + hsize_t count[2]; + hsize_t block[2]; + hsize_t coord1[4][2]; /* Coordinates for point selection */ + hssize_t nelmts; + + struct onion_filepaths *paths = NULL; + H5FD_onion_fapl_info_t onion_info = { + 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, was H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT */ + "initial commit" /* comment */ + }; + + TESTING("onion-created HDF5 file with revisions testing references"); + + /* Set up */ + if ((onion_info.backing_fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + if (NULL == (paths = onion_filepaths_init(basename))) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + + /*---------------------------------------------------------------------- + * Create the skeleton file (create the file without Onion VFD) + *---------------------------------------------------------------------- + */ + + /* Initialize data */ + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + wdata[i][j] = i + j; + + /* Create a new file using the default properties */ + if ((file = H5Fcreate(paths->canon, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Create dataspace */ + if ((space = H5Screate_simple(2, dims, NULL)) < 0) + TEST_ERROR; + + /* Create the dataset using the dataset creation property list */ + if ((dset = H5Dcreate2(file, "DS1", H5T_STD_I32LE, space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Write the data to the dataset */ + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, wdata) < 0) + TEST_ERROR; + + if (H5Dclose(dset) < 0) + TEST_ERROR; + if (H5Fclose(file) < 0) + TEST_ERROR; + + /*---------------------------------------------------------------------- + * First revision: open the file with Onion VFD and add a dataset (DS2) + * of object references + *---------------------------------------------------------------------- + */ + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + /* Create dataspace with unlimited dimensions */ + if ((space_ref = H5Screate_simple(1, dim_ref, NULL)) < 0) + TEST_ERROR; + + /* Create the dataset of object references */ + if ((dset = H5Dcreate2(file, "DS2", H5T_STD_REF, space_ref, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Create reference to dataset */ + if (H5Rcreate_object(file, "DS1", H5P_DEFAULT, &wbuf[0]) < 0) + TEST_ERROR; + + if (H5Rget_obj_type3(&wbuf[0], H5P_DEFAULT, &obj_type) < 0) + TEST_ERROR; + + if (obj_type != H5O_TYPE_DATASET) + TEST_ERROR; + + /* Create reference to the root group */ + if (H5Rcreate_object(file, "/", H5P_DEFAULT, &wbuf[1]) < 0) + TEST_ERROR; + + if (H5Rget_obj_type3(&wbuf[1], H5P_DEFAULT, &obj_type) < 0) + TEST_ERROR; + + if (obj_type != H5O_TYPE_GROUP) + TEST_ERROR; + + /* Write the object reference data to the dataset */ + if (H5Dwrite(dset, H5T_STD_REF, H5S_ALL, H5S_ALL, H5P_DEFAULT, wbuf) < 0) + TEST_ERROR; + + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + for (int i = 0; i < 2; i++) + if (H5Rdestroy(&wbuf[i]) < 0) + TEST_ERROR; + + /*---------------------------------------------------------------------- + * Second revision: open the file with Onion VFD and add a dataset (DS3) + * of region references + *---------------------------------------------------------------------- + */ + if ((file = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + /* Create the dataset of region references */ + if ((dset = H5Dcreate2(file, "DS3", H5T_STD_REF, space_ref, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Select 2x4 hyperslab for first reference */ + start[0] = 0; + start[1] = 0; + stride[0] = 1; + stride[1] = 1; + count[0] = 1; + count[1] = 1; + block[0] = 2; + block[1] = 4; + + /* Make a hyperslab selection of 2x4 elements */ + if (H5Sselect_hyperslab(space, H5S_SELECT_SET, start, stride, count, block) < 0) + TEST_ERROR; + + /* Verify the number of selection */ + if ((nelmts = H5Sget_select_npoints(space)) != 8) { + HDprintf("Number of selected elements is supposed to be 8, but got %" PRIuHSIZE "\n", nelmts); + TEST_ERROR; + } + + /* Store first data region */ + if (H5Rcreate_region(file, "/DS1", space, H5P_DEFAULT, &wbuf[0]) < 0) + TEST_ERROR; + + if (H5Rget_obj_type3(&wbuf[0], H5P_DEFAULT, &obj_type) < 0) + TEST_ERROR; + + if (obj_type != H5O_TYPE_DATASET) + TEST_ERROR; + + /* Select the sequence of four points for the second reference */ + coord1[0][0] = 0; + coord1[0][1] = 0; + coord1[1][0] = 1; + coord1[1][1] = 1; + coord1[2][0] = 2; + coord1[2][1] = 2; + coord1[3][0] = 3; + coord1[3][1] = 3; + + if (H5Sselect_elements(space, H5S_SELECT_SET, 4, (const hsize_t *)coord1) < 0) + TEST_ERROR; + + /* Store the second data region */ + if (H5Rcreate_region(file, "/DS1", space, H5P_DEFAULT, &wbuf[1]) < 0) + TEST_ERROR; + + if (H5Rget_obj_type3(&wbuf[1], H5P_DEFAULT, &obj_type) < 0) + TEST_ERROR; + + if (obj_type != H5O_TYPE_DATASET) + TEST_ERROR; + + /* Write the region reference data to the dataset */ + if (H5Dwrite(dset, H5T_STD_REF, H5S_ALL, H5S_ALL, H5P_DEFAULT, wbuf) < 0) + TEST_ERROR; + + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + for (int i = 0; i < 2; i++) + if (H5Rdestroy(&wbuf[i]) < 0) + TEST_ERROR; + + /*---------------------------------------------------------------------- + * Start to verify the revisions + *---------------------------------------------------------------------- + */ + /*---------------------------------------------------------------------- + * Verify the first revision: it should have the object references + *---------------------------------------------------------------------- + */ + onion_info.revision_num = 1; + + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + if ((file = H5Fopen(paths->canon, H5F_ACC_RDONLY, fapl_id)) < 0) + TEST_ERROR; + + /* Open the dataset of the object references */ + if ((dset = H5Dopen2(file, "DS2", H5P_DEFAULT)) < 0) + TEST_ERROR; + + if (H5Dread(dset, H5T_STD_REF, H5S_ALL, H5S_ALL, H5P_DEFAULT, rbuf) < 0) + TEST_ERROR; + + /* Open the referenced dataset and check the data */ + if ((dset2 = H5Ropen_object(&rbuf[0], H5P_DEFAULT, H5P_DEFAULT)) < 0) + TEST_ERROR; + + if (H5Dread(dset2, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rdata) < 0) + TEST_ERROR; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + int expected = i + j; + if (rdata[i][j] != expected) { + HDprintf("ERROR!!! Expected: %d, Got: %d\n", expected, rdata[i][j]); + TEST_ERROR; + } + } + } + + /* Open the referenced group and make sure it's a group object */ + if ((group = H5Ropen_object(&rbuf[1], H5P_DEFAULT, H5P_DEFAULT)) < 0) + TEST_ERROR; + + if (H5I_GROUP != H5Iget_type(group)) + TEST_ERROR; + + if (H5Gclose(group) < 0) + TEST_ERROR; + + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + + if (H5Dclose(dset2) < 0) + TEST_ERROR; + dset2 = H5I_INVALID_HID; + + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + for (int i = 0; i < 2; i++) + if (H5Rdestroy(&rbuf[i]) < 0) + TEST_ERROR; + + /*---------------------------------------------------------------------- + * Verify the second revision: it should have the region references + *---------------------------------------------------------------------- + */ + onion_info.revision_num = 2; + + if (H5Pset_fapl_onion(fapl_id, &onion_info) < 0) + TEST_ERROR; + + if ((file = H5Fopen(paths->canon, H5F_ACC_RDONLY, fapl_id)) < 0) + TEST_ERROR; + + /* Open the dataset of the region reference */ + if ((dset = H5Dopen2(file, "DS3", H5P_DEFAULT)) < 0) + TEST_ERROR; + + if (H5Dread(dset, H5T_STD_REF, H5S_ALL, H5S_ALL, H5P_DEFAULT, rbuf) < 0) + TEST_ERROR; + + /* Get the hyperslab selection and check the referenced region of the dataset */ + if ((space2 = H5Ropen_region(&rbuf[0], H5P_DEFAULT, H5P_DEFAULT)) < 0) + TEST_ERROR; + + if ((nelmts = H5Sget_select_npoints(space2)) != 8) { + HDprintf("Number of selected elements is supposed to be 8, but got %" PRIuHSIZE "\n", nelmts); + TEST_ERROR; + } + + if (H5Sclose(space2) < 0) + TEST_ERROR; + space2 = H5I_INVALID_HID; + + /* Get the element selection and check the referenced region of the dataset */ + if ((space2 = H5Ropen_region(&rbuf[1], H5P_DEFAULT, H5P_DEFAULT)) < 0) + TEST_ERROR; + + if ((nelmts = H5Sget_select_npoints(space2)) != 4) { + HDprintf("Number of selected elements is supposed to be 4, but got %" PRIuHSIZE "\n", nelmts); + TEST_ERROR; + } + + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + + if (H5Sclose(space2) < 0) + TEST_ERROR; + space2 = H5I_INVALID_HID; + + if (H5Fclose(file) < 0) + TEST_ERROR; + file = H5I_INVALID_HID; + + for (int i = 0; i < 2; i++) + if (H5Rdestroy(&rbuf[i]) < 0) + TEST_ERROR; + + /* Close and release resources */ + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + if (H5Sclose(space) < 0) + TEST_ERROR; + if (H5Sclose(space_ref) < 0) + TEST_ERROR; + if (H5Pclose(onion_info.backing_fapl_id) < 0) + TEST_ERROR; + + 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); + } + + H5E_BEGIN_TRY + { + H5Dclose(dset); + H5Fclose(file); + H5Pclose(fapl_id); + H5Pclose(onion_info.backing_fapl_id); + } + H5E_END_TRY; + + return -1; +} + +static int +test_integration_create_by_name(void) +{ + const char *basename = "integration_by_name.h5"; + hid_t fapl_id = H5I_INVALID_HID; + struct onion_filepaths *paths = NULL; + hid_t file_id = H5I_INVALID_HID; + hid_t file = H5I_INVALID_HID; + hid_t space = H5I_INVALID_HID; + hid_t dset = H5I_INVALID_HID; + hid_t dcpl = H5I_INVALID_HID; + hsize_t dims[2] = {1, ONE_DIM_SIZE}; + hsize_t maxdims[2] = {1, ONE_DIM_SIZE}; + int fillval; + struct { + int arr[ONE_DIM_SIZE]; + } *wdata = NULL; + struct { + int arr[ONE_DIM_SIZE]; + } *rdata = NULL; + struct { + int arr[ONE_DIM_SIZE]; + } *dset_data = NULL; + + TESTING("H5Pset_driver_by_name"); + + /* Setup */ + if (NULL == (wdata = HDcalloc(1, sizeof(*wdata)))) + TEST_ERROR; + if (NULL == (rdata = HDcalloc(1, sizeof(*rdata)))) + TEST_ERROR; + if (NULL == (dset_data = HDcalloc(1, sizeof(*dset_data)))) + TEST_ERROR; + + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + + /* Use H5Pset_driver_by_name to enable the Onion VFD */ + if (H5Pset_driver_by_name(fapl_id, "onion", "{revision_num: H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST}") < + 0) + TEST_ERROR; + + if (NULL == (paths = onion_filepaths_init(basename))) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + + /*---------------------------------------------------------------------- + * Create the skeleton file (create the file without Onion VFD) + *---------------------------------------------------------------------- + */ + /* Initialize data */ + for (int i = 0; i < ONE_DIM_SIZE; i++) + wdata->arr[i] = i; + + /* Create a new file using the default properties */ + if ((file = H5Fcreate(paths->canon, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Create dataspace with unlimited dimensions*/ + if ((space = H5Screate_simple(2, dims, maxdims)) < 0) + TEST_ERROR; + + /* Create the dataset creation property list */ + if ((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) + TEST_ERROR; + + /* Set the fill value for the dataset */ + fillval = 99; + if (H5Pset_fill_value(dcpl, H5T_NATIVE_INT, &fillval) < 0) + TEST_ERROR; + + /* 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. + */ + if (H5Pset_alloc_time(dcpl, H5D_ALLOC_TIME_EARLY) < 0) + TEST_ERROR; + + /* Create the dataset using the dataset creation property list */ + if ((dset = H5Dcreate2(file, "DS1", H5T_STD_I32LE, space, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) + TEST_ERROR; + + /* Write the data to the dataset */ + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, wdata) < 0) + TEST_ERROR; + + /* Close everything */ + if (H5Pclose(dcpl) < 0) + TEST_ERROR; + if (H5Dclose(dset) < 0) + TEST_ERROR; + if (H5Sclose(space) < 0) + TEST_ERROR; + if (H5Fclose(file) < 0) + TEST_ERROR; + + /*---------------------------------------------------------------------- + * First revision: open the file with Onion VFD and change the data + *---------------------------------------------------------------------- + */ + if ((file_id = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + + if ((dset = H5Dopen2(file_id, "DS1", H5P_DEFAULT)) < 0) + TEST_ERROR; + + for (int i = 0; i < ONE_DIM_SIZE; i++) + dset_data->arr[i] = i + ONE_DIM_SIZE; + + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset_data) < 0) + TEST_ERROR; + + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + if (H5Fclose(file_id) < 0) + TEST_ERROR; + file_id = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Second revision: open the file with Onion VFD and change the data + *---------------------------------------------------------------------- + */ + if ((file_id = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + if ((dset = H5Dopen2(file_id, "DS1", H5P_DEFAULT)) < 0) + TEST_ERROR; + + for (int i = 0; i < ONE_DIM_SIZE; i++) + dset_data->arr[i] = i + 2048; + + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset_data) < 0) + TEST_ERROR; + + /* CLEANUP */ + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + if (H5Fclose(file_id) < 0) + TEST_ERROR; + file_id = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Third revision: open the file with Onion VFD and change the data + *---------------------------------------------------------------------- + */ + if ((file_id = H5Fopen(paths->canon, H5F_ACC_RDWR, fapl_id)) < 0) + TEST_ERROR; + if ((dset = H5Dopen2(file_id, "DS1", H5P_DEFAULT)) < 0) + TEST_ERROR; + + for (int i = 0; i < ONE_DIM_SIZE; i += 20) + dset_data->arr[i] = i + 3072; + + if (H5Dwrite(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, dset_data) < 0) + TEST_ERROR; + + /* CLEANUP */ + if (H5Dclose(dset) < 0) + TEST_ERROR; + dset = H5I_INVALID_HID; + if (H5Fclose(file_id) < 0) + TEST_ERROR; + file_id = H5I_INVALID_HID; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + fapl_id = H5I_INVALID_HID; + + /*---------------------------------------------------------------------- + * Start to verify the revision with H5Pset_driver_by_name + *---------------------------------------------------------------------- + */ + /*---------------------------------------------------------------------- + * Verify the second revision + *---------------------------------------------------------------------- + */ + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + TEST_ERROR; + if (H5Pset_driver_by_name(fapl_id, "onion", "{revision_num: 2; page_size: 4; }") < 0) + TEST_ERROR; + + if ((file_id = H5Fopen(paths->canon, H5F_ACC_RDONLY, fapl_id)) < 0) + TEST_ERROR; + if ((dset = H5Dopen2(file_id, "DS1", H5P_DEFAULT)) < 0) + TEST_ERROR; + + if (H5Dread(dset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rdata) < 0) + TEST_ERROR; + + for (int i = 0; i < ONE_DIM_SIZE; i += 20) { + int expected = i + 2048; + if (rdata->arr[i] != expected) { + HDprintf("ERROR!!! Expected: %d, Got: %d\n", expected, rdata->arr[i]); + TEST_ERROR; + } + } + + /* Close everything */ + if (H5Dclose(dset) < 0) + TEST_ERROR; + if (H5Fclose(file_id) < 0) + TEST_ERROR; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + onion_filepaths_destroy(paths); + + HDfree(wdata); + HDfree(rdata); + HDfree(dset_data); + + PASSED(); + return 0; + +error: + + if (paths != NULL) { + HDremove(paths->canon); + HDremove(paths->onion); + HDremove(paths->recovery); + onion_filepaths_destroy(paths); + } + + H5E_BEGIN_TRY + { + H5Dclose(dset); + H5Fclose(file_id); + H5Pclose(fapl_id); + } + H5E_END_TRY; + + HDfree(wdata); + HDfree(rdata); + HDfree(dset_data); + + return -1; +} /* end test_integration_create_simple() */ + +/*----------------------------------------------------------------------------- + * + * Function: main() + * + * Purpose: Perform unit tests on for the Onion VFD. + * + *----------------------------------------------------------------------------- + */ +int +main(void) +{ + const char *env_h5_drvr = NULL; /* VFD value from environment */ + int nerrors = 0; + + HDprintf("Testing Onion VFD functionality.\n"); + + h5_reset(); + + /* The onion VFD only supports the sec2 VFD under the hood, so skip this + * test when the environment variable has been set to something else + */ + env_h5_drvr = HDgetenv(HDF5_DRIVER); + if (env_h5_drvr == NULL) + env_h5_drvr = "nomatch"; + if ((0 != HDstrcmp(env_h5_drvr, "nomatch")) && (0 != HDstrcmp(env_h5_drvr, "sec2"))) { + SKIPPED(); + HDputs("Onion VFD test skipped due to non-sec2 default VFD"); + HDexit(EXIT_SUCCESS); + } + + /* 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_history_encode_decode_empty(); + nerrors -= test_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(); + nerrors -= test_integration_create_simple(); + nerrors -= test_integration_create_delete_objects(); + nerrors -= test_integration_dset_extension(); + nerrors -= test_integration_ctl(); + nerrors -= test_integration_reference(); + nerrors -= test_integration_create_by_name(); + + if (nerrors > 0) { + HDprintf("***** %d Onion TEST%s FAILED! *****\n", nerrors, nerrors > 1 ? "S" : ""); + return EXIT_FAILURE; + } + + HDprintf("All Onion tests passed.\n"); + return EXIT_SUCCESS; + +} /* end main() */ |