summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorDana Robinson <43805+derobins@users.noreply.github.com>2022-08-02 19:54:40 (GMT)
committerGitHub <noreply@github.com>2022-08-02 19:54:40 (GMT)
commitfcf41b3cd60df51af9be529e379a9dd6c488d088 (patch)
treee486d5f8254a33b978c34069b9810ce171ba7c2c /test
parentea13de1bb0aba8a97c75f10343dc4c792193b215 (diff)
downloadhdf5-fcf41b3cd60df51af9be529e379a9dd6c488d088.zip
hdf5-fcf41b3cd60df51af9be529e379a9dd6c488d088.tar.gz
hdf5-fcf41b3cd60df51af9be529e379a9dd6c488d088.tar.bz2
Onion VFD (#1953)
* Onion VFD feature * Fixes onion VFD errors with non-sec2 backing store VFDs * Disables the onion VFD tests w/ ph5diff * Disables non-sec2 VFDs as onion VFD backing stores * Committing clang-format changes * Formatted source * Typo * Adds onion VFD tools tests to CMake * Fixes for v16 API compatibility * Memset structs to avoid bad frees on errors * H5Dwrite() calls now use H5T_NATIVE_INT as the memory type vs LE * Properly decodes checksums on BE machines * Be more careful about uint64_t to haddr_t/hsize_t conversions * Another fix for BE data comparison * Removed double underscores from onion constants * Replace hard-coded onion header string w/ constant * Fixes cleanup paths in H5FD__onion_ingest_history() * Fixed use of size_t revision numbers * Fix h5dump revision count format string Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Diffstat (limited to 'test')
-rw-r--r--test/CMakeLists.txt1
-rw-r--r--test/Makefile.am3
-rw-r--r--test/onion.c4966
3 files changed, 4969 insertions, 1 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 5bccc81..b9d1208 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -381,6 +381,7 @@ set (H5_TESTS
timer
cmpd_dtransform
event_set
+ onion
)
if (HDF5_BUILD_UTILS)
set (H5_TESTS ${H5_TESTS} mirror_vfd)
diff --git a/test/Makefile.am b/test/Makefile.am
index fcfd34e..22510dc 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -70,7 +70,8 @@ TEST_PROG= testhdf5 \
flush1 flush2 app_ref enum set_extent ttsafe enc_dec_plist \
enc_dec_plist_cross_platform getname vfd ros3 s3comms hdfs ntypes \
dangle dtransform reserved cross_read freespace mf vds file_image \
- unregister cache_logging cork swmr thread_id vol timer event_set
+ unregister cache_logging cork swmr thread_id vol timer event_set \
+ onion
# List programs to be built when testing here
#
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() */