hdf5/test/onion.c
2023-10-03 09:01:28 -07:00

4967 lines
158 KiB
C

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* 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://www.hdfgroup.org/licenses. *
* 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_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 {
bool 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 */
static 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 */
static const unsigned char *b_list_s =
(const unsigned char *)"badebailbaitbalebanebarebaskbeambeanbearbeenbeerbeltbentbestbide"
"bikebilebindbirdbiteblipblueboarboatbobsbodyboilboldbollboltbond"
"boneboobboorboosbootbradbragbratbraybrewbritbrowbuckbudsbunkbunt"
"buoyburnburybustbuys";
static 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 = malloc(sizeof(struct onion_filepaths))))
TEST_ERROR;
paths->canon = NULL;
paths->onion = NULL;
paths->recovery = NULL;
if (NULL == (paths->canon = malloc(sizeof(char) * ONION_TEST_FIXNAME_SIZE)))
TEST_ERROR;
snprintf(paths->canon, ONION_TEST_FIXNAME_SIZE, "%s", basename);
if (NULL == (paths->onion = malloc(sizeof(char) * ONION_TEST_FIXNAME_SIZE)))
TEST_ERROR;
snprintf(paths->onion, ONION_TEST_FIXNAME_SIZE, "%s.onion", paths->canon);
if (NULL == (paths->recovery = malloc(sizeof(char) * ONION_TEST_FIXNAME_SIZE)))
TEST_ERROR;
snprintf(paths->recovery, ONION_TEST_FIXNAME_SIZE, "%s.onion.recovery", paths->canon);
return paths;
error:
if (paths != NULL) {
free(paths->canon);
free(paths->onion);
free(paths->recovery);
}
free(paths);
return NULL;
}
/* Free onion file paths */
static void
onion_filepaths_destroy(struct onion_filepaths *paths)
{
free(paths->canon);
free(paths->onion);
free(paths->recovery);
free(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 (strcmp(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]) {
printf("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)");
/* Generate 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]) {
printf("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 = calloc(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 = malloc(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 = calloc(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;
}
free(history_out.record_locs);
free(buf);
free(history.record_locs);
PASSED();
return 0;
error:
free(history_out.record_locs);
free(buf);
free(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;
bool badness = false;
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) +
strlen("Example comment message.") + 1;
r_out.archival_index.list = NULL;
r_out.comment = NULL;
TESTING("encode/decode revision record");
memcpy(record.time_of_creation, "19411207T190643Z", 16);
record.archival_index.list = calloc(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 = malloc(sizeof(unsigned char) * exp_size)))
TEST_ERROR;
/* Test encode */
if (H5FD__onion_revision_record_encode(&record, buf, &checksum) != exp_size)
TEST_ERROR;
for (i = 0; i < exp_size; i++) {
if (exp[i] != buf[i]) {
badness = true;
printf("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... */
printf("INDEX\n");
for (i = 0; i < exp_size; i++)
printf("%4zu ", i);
printf("\n");
printf("EXPECTED\n");
for (i = 0; i < exp_size; i++)
printf("0x%02X ", (unsigned int)exp[i]);
printf("\n");
printf("ACTUAL\n");
for (i = 0; i < exp_size; i++)
printf("0x%02X ", (unsigned int)buf[i]);
printf("\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 = calloc(r_out.comment_size, sizeof(char));
if (NULL == r_out.comment)
TEST_ERROR;
r_out.archival_index.list = calloc(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 (strncmp(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 != strlen(r_out.comment) + 1)
TEST_ERROR;
if (strlen(record.comment) != strlen(r_out.comment))
TEST_ERROR;
if (strcmp(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 */
free(r_out.archival_index.list);
free(r_out.comment);
free(buf);
free(record.archival_index.list);
PASSED();
return 0;
error:
free(r_out.archival_index.list);
free(r_out.comment);
free(buf);
free(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) {
fprintf(stderr, "\nSizes not the same - nbytes: %zu, filesize: %" PRIu64 "\n", nbytes, filesize);
TEST_ERROR;
}
if (NULL == (act_buf = malloc(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]) {
printf("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;
free(act_buf);
return 0;
error:
if (raw_vfile != NULL)
H5FDclose(raw_vfile);
free(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 */
memset(&rev_out, 0, sizeof(H5FD_onion_revision_record_t));
memset(&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 = malloc(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;
free(buf);
buf = NULL;
/* Ingest history */
readsize = hdr_out.history_size;
if (NULL == (buf = malloc(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 = calloc(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;
/* Reuse buffer space to sanity-check checksum for record pointer(s). */
assert(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;
}
free(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 = malloc((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 = malloc((size_t)rev_out.comment_size)))
TEST_ERROR;
rev_out.archival_index.list =
calloc(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 (strlen(rev_out.comment) != strlen(erp->comment))
TEST_ERROR;
if (strcmp(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;
free(buf);
free(rev_out.comment);
free(rev_out.archival_index.list);
}
free(history_out.record_locs);
history_out.record_locs = NULL;
return 0;
error:
free(buf);
free(rev_out.comment);
free(rev_out.archival_index.list);
free(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 = calloc(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;
free(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);
free(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(bool truncate_canonical, bool 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 = malloc(4 * sizeof(char))))
TEST_ERROR;
if (H5FDread(vfile_rw, H5FD_MEM_DRAW, H5P_DEFAULT, 0, 4, buf) < 0)
TEST_ERROR;
if (memcmp(a_list_s, buf, 4) != 0)
TEST_ERROR;
free(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 = malloc(buf_size * sizeof(char))))
TEST_ERROR;
if (H5FDread(vfile_rw, H5FD_MEM_DRAW, H5P_DEFAULT, 0, buf_size, buf) < 0)
TEST_ERROR;
if (memcmp(a_list_s + half_size, buf, buf_size) != 0)
TEST_ERROR;
free(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 = malloc(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 (memcmp(a_list_s, buf, a_list_size_s) != 0)
TEST_ERROR;
free(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 logical 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 = malloc(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 (memcmp(a_list_s, buf, a_list_size_s) != 0)
TEST_ERROR;
free(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);
}
free(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)) {
printf("\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)) {
printf("\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)) {
printf("\nEOF is not %" PRIuHADDR ", it is: %" PRIuHADDR "\n", size,
H5FDget_eof(file, H5FD_MEM_DRAW));
TEST_ERROR;
}
if (NULL == (buf = malloc(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 (memcmp(buf + a_off, a_list_s, a_list_size_s) != 0)
TEST_ERROR;
free(buf);
buf = NULL;
/* Repeat read at page offset; test possible read offset error */
if (NULL == (buf = malloc(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 (memcmp(buf + size, a_list_s, ONION_TEST_PAGE_SIZE_5 - size) != 0)
TEST_ERROR;
free(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 = malloc(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 (memcmp(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 (memcmp(buf + b_off, b_list_s, b_list_size_s) != 0)
TEST_ERROR;
free(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 *)malloc(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 (memcmp(buf, a_list_s, a_list_size_s) != 0)
TEST_ERROR;
if (memcmp(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 (memcmp(buf + b_off, b_list_s, b_list_size_s) != 0)
TEST_ERROR;
free(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);
}
free(history_out.record_locs);
free(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(strlen(about[i].comment), H5FD_ONION_FAPL_INFO_COMMENT_MAX_LEN);
memcpy(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 = malloc(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 (memcmp(buf_vfy, wi->buf, wi->size) != 0) {
const unsigned char *_buf = wi->buf;
size_t z = 0;
puts("i exp act");
for (z = 0; z < wi->size; z++)
printf("%02zx %c %c\n", z, _buf[z], buf_vfy[z]);
fflush(stdout);
TEST_ERROR;
}
free(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
free(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 = malloc(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 (memcmp(a_list_s, buf + a_off, a_list_size_s) != 0) {
size_t k;
printf("aoff: %" PRIu64 "\n", a_off);
puts("i exp act");
for (k = 0; k < b_list_size_s; k++) {
printf("%3zu:: %c : %c\n", k, (k < a_off) ? ' ' : a_list_s[k - a_off], buf[k]);
}
fflush(stdout);
TEST_ERROR;
}
if (memcmp(b_list_s, buf, a_off) != 0) {
size_t k;
printf("aoff: %" PRIu64 "\n", a_off);
puts("i exp act");
for (k = 0; k < b_list_size_s; k++) {
printf("%3zu:: %c : %c\n", k, (k < a_off) ? b_list_s[k] : ' ', buf[k]);
}
fflush(stdout);
TEST_ERROR;
}
if (H5FDclose(file) < 0)
TEST_ERROR;
file = NULL;
free(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 = malloc(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;
free(buf);
buf = NULL;
if (NULL == (buf = malloc(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 = calloc(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;
free(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? */
}
free(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);
free(buf);
PASSED();
return 0;
error:
if (paths != NULL) {
HDremove(paths->canon);
HDremove(paths->onion);
HDremove(paths->recovery);
onion_filepaths_destroy(paths);
}
free(history_out.record_locs);
free(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 = calloc(1, sizeof(*wdata))))
TEST_ERROR;
if (NULL == (rdata = calloc(1, sizeof(*rdata))))
TEST_ERROR;
if (NULL == (dset_data = calloc(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) {
printf("ERROR!!! Expected: %d, Got: %d\n", expected, rdata->arr[i][j]);
fflush(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) {
printf("ERROR!!! Expected: %d, Got: %d\n", expected, rdata->arr[i][j]);
fflush(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) {
printf("ERROR!!! Expected: %d, Got: %d\n", expected, rdata->arr[i][j]);
fflush(stdout);
TEST_ERROR;
}
else {
fflush(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);
free(wdata);
free(rdata);
free(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
free(wdata);
free(rdata);
free(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 = calloc(1, sizeof(*wdata))))
TEST_ERROR;
if (NULL == (rdata = calloc(1, sizeof(*rdata))))
TEST_ERROR;
if (NULL == (dset_data = calloc(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) {
printf("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);
free(wdata);
free(rdata);
free(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
free(wdata);
free(rdata);
free(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) {
printf("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) {
printf("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) {
printf("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) {
printf("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 = calloc(1, sizeof(*wdata))))
TEST_ERROR;
if (NULL == (rdata = calloc(1, sizeof(*rdata))))
TEST_ERROR;
if (NULL == (dset_data = calloc(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) {
printf("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);
free(wdata);
free(rdata);
free(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
free(wdata);
free(rdata);
free(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;
printf("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 = getenv(HDF5_DRIVER);
if (env_h5_drvr == NULL)
env_h5_drvr = "nomatch";
if ((0 != strcmp(env_h5_drvr, "nomatch")) && (0 != strcmp(env_h5_drvr, "sec2"))) {
SKIPPED();
puts("Onion VFD test skipped due to non-sec2 default VFD");
exit(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) {
printf("***** %d Onion TEST%s FAILED! *****\n", nerrors, nerrors > 1 ? "S" : "");
return EXIT_FAILURE;
}
printf("All Onion tests passed.\n");
return EXIT_SUCCESS;
} /* end main() */