summaryrefslogtreecommitdiffstats
path: root/test/vfd.c
diff options
context:
space:
mode:
authorJacob Smith <jake.smith@hdfgroup.org>2020-03-13 22:13:17 (GMT)
committerJacob Smith <jake.smith@hdfgroup.org>2020-03-13 22:13:17 (GMT)
commitb65405439d88be6c09fe94360e7fea9856697926 (patch)
treef81fc8aa14e58a3b500cdf64a6a53b7150d8ac3e /test/vfd.c
parent7613f7e1aa89210bb625d59d79a6220c49a1f22c (diff)
downloadhdf5-b65405439d88be6c09fe94360e7fea9856697926.zip
hdf5-b65405439d88be6c09fe94360e7fea9856697926.tar.gz
hdf5-b65405439d88be6c09fe94360e7fea9856697926.tar.bz2
Add Splitter VFD to library.
* "Simultaneous and equivalent" Read-Write and Write-Only channels for file I/O. * Only supports drivers with the H5FD_FEAT_DEFAULT_VFD_COMPATIBLE flag for now, preventing issues with multi-file drivers. Add Mirror VFD to library. * Write-only operations over a network. * Uses TCP/IP sockets. * Server and auxiliary server-shutdown programs provided in a new directory, `utils/mirror_vfd`. * Automated testing via loopback ("remote" of localhost).
Diffstat (limited to 'test/vfd.c')
-rw-r--r--test/vfd.c1097
1 files changed, 1092 insertions, 5 deletions
diff --git a/test/vfd.c b/test/vfd.c
index e88d9bb..97995d2 100644
--- a/test/vfd.c
+++ b/test/vfd.c
@@ -25,6 +25,7 @@
#define FAMILY_SIZE (1*KB)
#define FAMILY_SIZE2 (5*KB)
#define MULTI_SIZE 128
+#define SPLITTER_SIZE 8 /* dimensions of a dataset */
#define CORE_INCREMENT (4*KB)
#define CORE_PAGE_SIZE (1024*KB)
@@ -59,6 +60,9 @@ const char *FILENAME[] = {
"windows_file", /*8*/
"new_multi_file_v16",/*9*/
"ro_s3_file", /*10*/
+ "splitter_rw_file", /*11*/
+ "splitter_wo_file", /*12*/
+ "splitter.log", /*13*/
NULL
};
@@ -66,8 +70,47 @@ const char *FILENAME[] = {
#define COMPAT_BASENAME "family_v16_"
#define MULTI_COMPAT_BASENAME "multi_file_v16"
+#define SPLITTER_DATASET_NAME "dataset"
+
+/* Macro: HEXPRINT()
+ * Helper macro to pretty-print hexadecimal output of a buffer of known size.
+ * Each line has the address of the first printed byte, and four columns of
+ * four-byte data.
+ */
+static int __k;
+#define HEXPRINT(size, buf) \
+for (__k = 0; __k < (size); __k++) { \
+ if (__k % 16 == 0) { \
+ HDprintf("\n%04x", __k); \
+ } \
+ HDprintf((__k%4 == 0) ? " %02X" : " %02X", (unsigned char)(buf)[__k]); \
+} /* end #define HEXPRINT() */
+/* Helper structure to pass around dataset information.
+ */
+struct splitter_dataset_def {
+ void *buf; /* contents of dataset */
+ const char *dset_name; /* dataset name, always added to root group */
+ hid_t mem_type_id; /* datatype */
+ const hsize_t *dims; /* dimensions */
+ int n_dims; /* rank */
+};
+
+static int splitter_prepare_file_paths(H5FD_splitter_vfd_config_t *vfd_config,
+ char *filename_rw_out);
+static int splitter_create_single_file_at(const char *filename, hid_t fapl_id,
+ const struct splitter_dataset_def *data);
+static int splitter_compare_expected_data(hid_t file_id,
+ const struct splitter_dataset_def *data);
+static int run_splitter_test(const struct splitter_dataset_def *data,
+ hbool_t ignore_wo_errors, hbool_t provide_logfile_path,
+ hid_t sub_fapl_ids[2]);
+static int splitter_RO_test(const struct splitter_dataset_def *data,
+ hid_t child_fapl_id);
+static int splitter_tentative_open_test(hid_t child_fapl_id);
+static int file_exists(const char *filename, hid_t fapl_id);
+
/*-------------------------------------------------------------------------
* Function: test_sec2
*
@@ -859,7 +902,7 @@ test_family(void)
hid_t driver_id = -1; /* ID for this VFD */
unsigned long driver_flags = 0; /* VFD feature flags */
char filename[1024];
- char dname[]="dataset";
+ char dname[] = "dataset";
unsigned int i, j;
int *fhandle=NULL, *fhandle2=NULL;
int **buf = NULL;
@@ -1263,7 +1306,7 @@ error:
return FAIL;
} /* end test_family_member_fapl() */
-
+
/*-------------------------------------------------------------------------
* Function: test_multi_opens
*
@@ -2179,6 +2222,1052 @@ error:
#endif /* H5_HAVE_ROS3_VFD */
} /* end test_ros3() */
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * Macro: SPLITTER_TEST_FAULT()
+ *
+ * utility macro, helps create stack-like backtrace on error.
+ * requires defined in the calling function:
+ * * variable `int ret_value` (return -1 on error)`
+ * * label `done` for exit on fault
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+#define SPLITTER_TEST_FAULT(mesg) { \
+ H5_FAILED(); \
+ AT(); \
+ HDfprintf(stderr, mesg); \
+ H5Eprint2(H5E_DEFAULT, stderr); \
+ fflush(stderr); \
+ ret_value = -1; \
+ goto done; \
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function: compare_splitter_config_info
+ *
+ * Purpose: Helper function to compare configuration info found in a
+ * FAPL against a canonical structure.
+ *
+ * Return: Success: 0, if config info in FAPL matches info structure.
+ * Failure: -1, if difference detected.
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+compare_splitter_config_info(hid_t fapl_id, H5FD_splitter_vfd_config_t *info)
+{
+ int ret_value = 0;
+ H5FD_splitter_vfd_config_t fetched_info;
+
+ fetched_info.magic = H5FD_SPLITTER_MAGIC;
+ fetched_info.version = H5FD_CURR_SPLITTER_VFD_CONFIG_VERSION;
+ fetched_info.rw_fapl_id = H5I_INVALID_HID;
+ fetched_info.wo_fapl_id = H5I_INVALID_HID;
+
+ if (H5Pget_fapl_splitter(fapl_id, &fetched_info) < 0) {
+ SPLITTER_TEST_FAULT("can't get splitter info\n");
+ }
+ if (info->rw_fapl_id == H5P_DEFAULT) {
+ if (H5Pget_driver(fetched_info.rw_fapl_id) != H5Pget_driver(H5P_FILE_ACCESS_DEFAULT)) {
+ SPLITTER_TEST_FAULT("Read-Write driver mismatch (default)\n");
+ }
+ }
+ else {
+ if (H5Pget_driver(fetched_info.rw_fapl_id) != H5Pget_driver(info->rw_fapl_id)) {
+ SPLITTER_TEST_FAULT("Read-Write driver mismatch\n");
+ }
+ }
+ if (info->wo_fapl_id == H5P_DEFAULT) {
+ if (H5Pget_driver(fetched_info.wo_fapl_id) != H5Pget_driver(H5P_FILE_ACCESS_DEFAULT)) {
+ SPLITTER_TEST_FAULT("Write-Only driver mismatch (default)\n");
+ }
+ }
+ else {
+ if (H5Pget_driver(fetched_info.wo_fapl_id) != H5Pget_driver(info->wo_fapl_id)) {
+ SPLITTER_TEST_FAULT("Write-Only driver mismatch\n");
+ }
+ }
+ if ( (HDstrlen(info->wo_path) != HDstrlen(fetched_info.wo_path)) ||
+ HDstrncmp(info->wo_path, fetched_info.wo_path, H5FD_SPLITTER_PATH_MAX))
+ {
+ HDfprintf(stderr, "MISMATCH: '%s' :: '%s'\n", info->wo_path, fetched_info.wo_path);
+ HEXPRINT(H5FD_SPLITTER_PATH_MAX, info->wo_path);
+ HEXPRINT(H5FD_SPLITTER_PATH_MAX, fetched_info.wo_path);
+ SPLITTER_TEST_FAULT("Write-Only file path mismatch\n");
+ }
+
+done:
+ return ret_value;
+} /* end compare_splitter_config_info() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: run_splitter_test
+ *
+ * Purpose: Auxiliary function for test_splitter().
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ * Description:
+ * Perform basic open-write-close with the Splitter VFD.
+ * Prior to operations, removes files from a previous run,
+ * if they exist.
+ * After writing, compares read-write and write-only files.
+ * Includes FAPL sanity testing.
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+run_splitter_test(const struct splitter_dataset_def *data,
+ hbool_t ignore_wo_errors,
+ hbool_t provide_logfile_path,
+ hid_t sub_fapl_ids[2])
+{
+ hid_t file_id = H5I_INVALID_HID;
+ hid_t fapl_id = H5I_INVALID_HID;
+ hid_t dset_id = H5I_INVALID_HID;
+ hid_t space_id = H5I_INVALID_HID;
+ hid_t fapl_id_out = H5I_INVALID_HID;
+ hid_t fapl_id_cpy = H5I_INVALID_HID;
+ H5FD_splitter_vfd_config_t vfd_config;
+ char filename_rw[H5FD_SPLITTER_PATH_MAX + 1];
+ FILE *logfile = NULL;
+ int ret_value = 0;
+
+ vfd_config.magic = H5FD_SPLITTER_MAGIC;
+ vfd_config.version = H5FD_CURR_SPLITTER_VFD_CONFIG_VERSION;
+ vfd_config.ignore_wo_errs = ignore_wo_errors;
+ vfd_config.rw_fapl_id = sub_fapl_ids[0];
+ vfd_config.wo_fapl_id = sub_fapl_ids[1];
+
+ if (splitter_prepare_file_paths(&vfd_config, filename_rw) < 0) {
+ SPLITTER_TEST_FAULT("can't prepare file paths\n");
+ }
+
+ if (provide_logfile_path == FALSE) {
+ *vfd_config.log_file_path = '\0'; /* reset as empty string */
+ }
+
+ /* Create a new fapl to use the SPLITTER file driver */
+ if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) == H5I_INVALID_HID) {
+ SPLITTER_TEST_FAULT("can't create FAPL ID\n");
+ }
+ if (H5Pset_fapl_splitter(fapl_id, &vfd_config) < 0) {
+ SPLITTER_TEST_FAULT("can't set splitter FAPL\n");
+ }
+ if (H5Pget_driver(fapl_id) != H5FD_SPLITTER) {
+ SPLITTER_TEST_FAULT("set FAPL not SPLITTER\n");
+ }
+
+ if (compare_splitter_config_info(fapl_id, &vfd_config) < 0) {
+ SPLITTER_TEST_FAULT("information mismatch\n");
+ }
+
+ /*
+ * Copy property list, light compare, and close the copy.
+ * Helps test driver-implemented FAPL-copying and library ID management.
+ */
+
+ fapl_id_cpy = H5Pcopy(fapl_id);
+ if (H5I_INVALID_HID == fapl_id_cpy) {
+ SPLITTER_TEST_FAULT("can't copy FAPL\n");
+ }
+ if (compare_splitter_config_info(fapl_id_cpy, &vfd_config) < 0) {
+ SPLITTER_TEST_FAULT("information mismatch\n");
+ }
+ if (H5Pclose(fapl_id_cpy) < 0) {
+ SPLITTER_TEST_FAULT("can't close fapl copy\n");
+ }
+
+ /*
+ * Proceed with test. Create file.
+ */
+ file_id = H5Fcreate(filename_rw, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id);
+ if (file_id < 0) {
+ SPLITTER_TEST_FAULT("can't create file\n");
+ }
+
+ /*
+ * Check driver from file
+ */
+
+ fapl_id_out = H5Fget_access_plist(file_id);
+ if (H5I_INVALID_HID == fapl_id_out) {
+ SPLITTER_TEST_FAULT("can't get file's FAPL\n");
+
+ }
+ if (H5Pget_driver(fapl_id_out) != H5FD_SPLITTER) {
+ SPLITTER_TEST_FAULT("wrong file FAPL driver\n");
+ }
+ if (compare_splitter_config_info(fapl_id_out, &vfd_config) < 0) {
+ SPLITTER_TEST_FAULT("information mismatch\n");
+ }
+ if (H5Pclose(fapl_id_out) < 0) {
+ SPLITTER_TEST_FAULT("can't close file's FAPL\n");
+ }
+
+ /*
+ * Create and write the dataset
+ */
+
+ space_id = H5Screate_simple(data->n_dims, data->dims, NULL);
+ if (space_id < 0) {
+ SPLITTER_TEST_FAULT("can't create dataspace\n");
+ }
+ dset_id = H5Dcreate2(file_id, data->dset_name, data->mem_type_id, space_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+ if (dset_id < 0) {
+ SPLITTER_TEST_FAULT("can't create dataset\n");
+ }
+ if (H5Dwrite(dset_id, data->mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, data->buf) < 0) {
+ SPLITTER_TEST_FAULT("can't write data to dataset\n");
+ }
+
+ /* Close everything */
+ if (H5Dclose(dset_id) < 0) {
+ SPLITTER_TEST_FAULT("can't close dset\n");
+ }
+ if (H5Sclose(space_id) < 0) {
+ SPLITTER_TEST_FAULT("can't close space\n");
+ }
+ if (H5Pclose(fapl_id) < 0) {
+ SPLITTER_TEST_FAULT("can't close fapl\n");
+ }
+ if (H5Fclose(file_id) < 0) {
+ SPLITTER_TEST_FAULT("can't close file\n");
+ }
+
+ /* Verify that the R/W and W/O files are identical */
+ if (h5_compare_file_bytes(filename_rw, vfd_config.wo_path) < 0) {
+ SPLITTER_TEST_FAULT("files are not byte-for-byte equivalent\n");
+ }
+
+ /* Verify existence of logfile iff appropriate */
+ logfile = fopen(vfd_config.log_file_path, "r");
+ if ( (TRUE == provide_logfile_path && NULL == logfile) ||
+ (FALSE == provide_logfile_path && NULL != logfile) )
+ {
+ SPLITTER_TEST_FAULT("no logfile when one was expected\n");
+ }
+
+done:
+ if (ret_value < 0) {
+ H5E_BEGIN_TRY {
+ (void)H5Dclose(dset_id);
+ (void)H5Sclose(space_id);
+ (void)H5Pclose(fapl_id_out);
+ (void)H5Pclose(fapl_id_cpy);
+ (void)H5Pclose(fapl_id);
+ (void)H5Fclose(file_id);
+ } H5E_END_TRY;
+ }
+ if (logfile != NULL) {
+ fclose(logfile);
+ }
+ return ret_value;
+
+} /* end run_splitter_test() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: driver_is_splitter_compatible
+ *
+ * Purpose: Determine whether the driver set in the FAPL ID is compatible
+ * with the Splitter VFD -- specificially, Write-Only channel.
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ * Description: Attempts to put the given FAPL ID as the W/O channel.
+ * Uses driver's own mechanisms to generate error, and catches
+ * error.
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+driver_is_splitter_compatible(hid_t fapl_id)
+{
+ H5FD_splitter_vfd_config_t vfd_config;
+ hid_t split_fapl_id = H5I_INVALID_HID;
+ herr_t ret;
+ int ret_value = 0;
+
+ split_fapl_id = H5Pcreate(H5P_FILE_ACCESS);
+ if (H5I_INVALID_HID == split_fapl_id) {
+ FAIL_PUTS_ERROR("Can't create contained FAPL");
+ }
+ vfd_config.magic = H5FD_SPLITTER_MAGIC;
+ vfd_config.version = H5FD_CURR_SPLITTER_VFD_CONFIG_VERSION;
+ vfd_config.ignore_wo_errs = FALSE;
+ vfd_config.rw_fapl_id = H5P_DEFAULT;
+ vfd_config.wo_fapl_id = fapl_id;
+ HDstrncpy(vfd_config.wo_path, "nonesuch", H5FD_SPLITTER_PATH_MAX);
+
+ H5E_BEGIN_TRY {
+ ret = H5Pset_fapl_splitter(split_fapl_id, &vfd_config);
+ } H5E_END_TRY;
+ if (SUCCEED == ret) {
+ ret_value = -1;
+ }
+
+ if (H5Pclose(split_fapl_id) < 0) {
+ FAIL_PUTS_ERROR("Can't close contained FAPL")
+ }
+ split_fapl_id = H5I_INVALID_HID;
+
+ return ret_value;
+
+error:
+ H5E_BEGIN_TRY {
+ (void)H5Pclose(split_fapl_id);
+ } H5E_END_TRY;
+ return -1;
+} /* end driver_is_splitter_compatible() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: splitter_RO_test
+ *
+ * Purpose: Verify Splitter VFD with the Read-Only access flag.
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ * Description: Attempt read-only opening of files with different
+ * permutations of files already existing on-disk.
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+splitter_RO_test(
+ const struct splitter_dataset_def *data,
+ hid_t child_fapl_id)
+{
+ char filename_rw[H5FD_SPLITTER_PATH_MAX + 1];
+ H5FD_splitter_vfd_config_t vfd_config;
+ hid_t fapl_id = H5I_INVALID_HID;
+ int ret_value = 0;
+ hid_t file_id = H5I_INVALID_HID;
+
+ vfd_config.magic = H5FD_SPLITTER_MAGIC;
+ vfd_config.version = H5FD_CURR_SPLITTER_VFD_CONFIG_VERSION;
+ vfd_config.ignore_wo_errs = FALSE;
+ vfd_config.rw_fapl_id = child_fapl_id;
+ vfd_config.wo_fapl_id = child_fapl_id;
+
+ if (splitter_prepare_file_paths(&vfd_config, filename_rw) < 0) {
+ SPLITTER_TEST_FAULT("can't prepare splitter file paths\n");
+ }
+
+ /* Create a new fapl to use the SPLITTER file driver */
+ fapl_id = H5Pcreate(H5P_FILE_ACCESS);
+ if (H5I_INVALID_HID == fapl_id) {
+ SPLITTER_TEST_FAULT("can't create FAPL ID\n");
+ }
+ if (H5Pset_fapl_splitter(fapl_id, &vfd_config) < 0) {
+ SPLITTER_TEST_FAULT("can't set splitter FAPL\n");
+ }
+ if (H5Pget_driver(fapl_id) != H5FD_SPLITTER) {
+ SPLITTER_TEST_FAULT("set FAPL not SPLITTER\n");
+ }
+
+ /* Attempt R/O open when both files are nonexistent
+ * Should fail.
+ */
+
+ H5E_BEGIN_TRY {
+ file_id = H5Fopen(filename_rw, H5F_ACC_RDONLY, fapl_id);
+ } H5E_END_TRY;
+ if (file_id >= 0) {
+ SPLITTER_TEST_FAULT("R/O open on nonexistent files unexpectedly successful\n");
+ }
+
+ /* Attempt R/O open when only W/O file exists
+ * Should fail.
+ */
+
+ if (splitter_create_single_file_at(vfd_config.wo_path, vfd_config.wo_fapl_id, data) < 0) {
+ SPLITTER_TEST_FAULT("can't write W/O file\n");
+ }
+ H5E_BEGIN_TRY {
+ file_id = H5Fopen(filename_rw, H5F_ACC_RDONLY, fapl_id);
+ } H5E_END_TRY;
+ if (file_id >= 0) {
+ SPLITTER_TEST_FAULT("R/O open with extant W/O file unexpectedly successful\n");
+ }
+ HDremove(vfd_config.wo_path);
+
+ /* Attempt R/O open when only R/W file exists
+ * Should fail.
+ */
+
+ if (splitter_create_single_file_at(filename_rw, vfd_config.rw_fapl_id, data) < 0) {
+ SPLITTER_TEST_FAULT("can't create R/W file\n");
+ }
+ H5E_BEGIN_TRY {
+ file_id = H5Fopen(filename_rw, H5F_ACC_RDONLY, fapl_id);
+ } H5E_END_TRY;
+ if (file_id >= 0) {
+ SPLITTER_TEST_FAULT("R/O open with extant R/W file unexpectedly successful\n");
+ }
+
+ /* Attempt R/O open when both R/W and W/O files exist
+ */
+
+ if (splitter_create_single_file_at(vfd_config.wo_path, vfd_config.wo_fapl_id, data) < 0) {
+ SPLITTER_TEST_FAULT("can't create W/O file\n");
+ }
+ file_id = H5Fopen(filename_rw, H5F_ACC_RDONLY, fapl_id);
+ if (file_id < 0) {
+ SPLITTER_TEST_FAULT("R/O open on two extant files failed\n");
+ }
+ if (splitter_compare_expected_data(file_id, data) < 0) {
+ SPLITTER_TEST_FAULT("data mismatch in R/W file\n");
+ }
+ if (H5Fclose(file_id) < 0) {
+ SPLITTER_TEST_FAULT("can't close file(s)\n");
+ }
+ file_id = H5I_INVALID_HID;
+
+ /* Cleanup
+ */
+
+ if (H5Pclose(fapl_id) < 0) {
+ SPLITTER_TEST_FAULT("can't close FAPL ID\n");
+ }
+ fapl_id = H5I_INVALID_HID;
+
+done:
+ if (ret_value < 0) {
+ H5E_BEGIN_TRY {
+ (void)H5Pclose(fapl_id);
+ (void)H5Fclose(file_id);
+ } H5E_END_TRY;
+ } /* end if error */
+ return ret_value;
+} /* end splitter_RO_test() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: splitter_prepare_file_paths
+ *
+ * Purpose: Get file paths ready for use by the Splitter VFD tests.
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ * Description:
+ * Use h5_fixname to adjust the splitter-relevant file paths
+ * from those given in FILENAMES.
+ *
+ * REMOVES EXISTING FILES AT THE PATH LOCATIONS PRIOR TO RETURN.
+ *
+ * The relevant file paths will be set in filename_rw_out and
+ * inside the config structure (wo_path, log_file_path).
+ *
+ * `filename_rw_out` must be at least H5FD_SPLITTER_PATH_MAX+1
+ * characters long.
+ *
+ * `vfd_config` must have its child FAPL IDs preset.
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+splitter_prepare_file_paths(H5FD_splitter_vfd_config_t *vfd_config, char *filename_rw_out)
+{
+ int ret_value = 0;
+
+ if (vfd_config == NULL || vfd_config->magic != H5FD_SPLITTER_MAGIC) {
+ SPLITTER_TEST_FAULT("invalid splitter config pointer\n");
+ }
+ if (filename_rw_out == NULL) {
+ SPLITTER_TEST_FAULT("NULL filename_rw pointer\n");
+ }
+
+ /* TODO: sanity-check fapl IDs? */
+
+ /* Build the r/w file, w/o file, and the log file paths.
+ * Output is stored in the associated string pointers.
+ */
+ h5_fixname(FILENAME[11], vfd_config->rw_fapl_id, filename_rw_out, H5FD_SPLITTER_PATH_MAX);
+ h5_fixname(FILENAME[12], vfd_config->wo_fapl_id, vfd_config->wo_path, H5FD_SPLITTER_PATH_MAX);
+ h5_fixname_no_suffix(FILENAME[13], vfd_config->wo_fapl_id, vfd_config->log_file_path, H5FD_SPLITTER_PATH_MAX);
+
+ /* Delete any existing files on disk.
+ */
+ HDremove(filename_rw_out);
+ HDremove(vfd_config->wo_path);
+ HDremove(vfd_config->log_file_path);
+
+done:
+ return ret_value;
+} /* end splitter_prepare_file_paths() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: splitter_crate_single_file_at
+ *
+ * Purpose: Create a file, optionally w/ dataset.
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ * Description:
+ * Create a file at the given location with the given FAPL,
+ * and write data as defined in `data` in a pre-determined location in the file.
+ *
+ * If the dataset definition pointer is NULL, no data is written
+ * to the file.
+ *
+ * Will always overwrite an existing file with the given name/path.
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+splitter_create_single_file_at(
+ const char *filename,
+ hid_t fapl_id,
+ const struct splitter_dataset_def *data)
+{
+ hid_t file_id = H5I_INVALID_HID;
+ hid_t space_id = H5I_INVALID_HID;
+ hid_t dset_id = H5I_INVALID_HID;
+ int ret_value = 0;
+
+ if (filename == NULL || *filename == '\0') {
+ SPLITTER_TEST_FAULT("filename is invalid\n");
+ }
+ /* TODO: sanity-check fapl id? */
+
+ file_id = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id);
+ if (file_id < 0) {
+ SPLITTER_TEST_FAULT("can't create file\n");
+ }
+
+ if (data) {
+ /* TODO: sanity-check data, if it exists? */
+ space_id = H5Screate_simple(data->n_dims, data->dims, NULL);
+ if (space_id < 0) {
+ SPLITTER_TEST_FAULT("can't create dataspace\n");
+ }
+
+ dset_id = H5Dcreate2(
+ file_id,
+ data->dset_name,
+ data->mem_type_id,
+ space_id,
+ H5P_DEFAULT,
+ H5P_DEFAULT,
+ H5P_DEFAULT);
+ if (dset_id < 0) {
+ SPLITTER_TEST_FAULT("can't create dataset\n");
+ }
+
+ if (H5Dwrite(dset_id, data->mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, data->buf) < 0) {
+ SPLITTER_TEST_FAULT("can't write data to dataset\n");
+ }
+
+ if (H5Dclose(dset_id) < 0) {
+ SPLITTER_TEST_FAULT("can't close dset\n");
+ }
+ if (H5Sclose(space_id) < 0) {
+ SPLITTER_TEST_FAULT("can't close space\n");
+ }
+ } /* end if data definition is provided */
+
+ if (H5Fclose(file_id) < 0) {
+ SPLITTER_TEST_FAULT("can't close file\n");
+ }
+
+done:
+ if (ret_value < 0) {
+ H5E_BEGIN_TRY {
+ (void)H5Dclose(dset_id);
+ (void)H5Sclose(space_id);
+ (void)H5Fclose(file_id);
+ } H5E_END_TRY;
+ } /* end if error */
+ return ret_value;
+} /* end splitter_create_single_file_at() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: splitter_compare_expected_data
+ *
+ * Purpose: Compare data within a predermined dataset.
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ * Description: Read data from the file at a predetermined location, and
+ * compare its contents byte-for-byte with that expected in
+ * the `data` definition structure.
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+splitter_compare_expected_data(hid_t file_id,
+ const struct splitter_dataset_def *data)
+{
+ hid_t dset_id = H5I_INVALID_HID;
+ int buf[SPLITTER_SIZE][SPLITTER_SIZE];
+ int expected[SPLITTER_SIZE][SPLITTER_SIZE];
+ size_t i = 0;
+ size_t j = 0;
+ int ret_value = 0;
+
+ if (sizeof((void *)buf) != sizeof(data->buf)) {
+ SPLITTER_TEST_FAULT("invariant size of expected data does not match that received!\n");
+ }
+ HDmemcpy(expected, data->buf, sizeof(expected));
+
+ dset_id = H5Dopen2(file_id, data->dset_name, H5P_DEFAULT);
+ if (dset_id < 0) {
+ SPLITTER_TEST_FAULT("can't open dataset\n");
+ }
+
+ if (H5Dread(dset_id, data->mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, (void *)buf) < 0) {
+ SPLITTER_TEST_FAULT("can't read dataset\n");
+ }
+
+ for (i=0; i < SPLITTER_SIZE; i++) {
+ for (j=0; j < SPLITTER_SIZE; j++) {
+ if (buf[i][j] != expected[i][j]) {
+ SPLITTER_TEST_FAULT("mismatch in expected data\n");
+ }
+ }
+ }
+
+ if (H5Dclose(dset_id) < 0) {
+ SPLITTER_TEST_FAULT("can't close dataset\n");
+ }
+
+done:
+ if (ret_value < 0) {
+ H5E_BEGIN_TRY {
+ (void)H5Dclose(dset_id);
+ } H5E_END_TRY;
+ }
+ return ret_value;
+} /* end splitter_compare_expected_data() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: splitter_tentative_open_test()
+ *
+ * Purpose: Verifies Splitter behavior with "tentative" H5F_open.
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ * Description:
+ * H5F_open() has a two-stage opening process when given a
+ * Read/Write access flag -- first it performs a "tentative
+ * open", where it checks to see whether files already exist
+ * on the system, done in such a way as to not "alter its state"
+ * (i.e., truncate).
+ * This can cause problems with the Splitter VFD, as the
+ * file on the R/W channel might exist already, but that on the
+ * W/O channel will not, and vice-versa.
+ *
+ * This test exists to verify that in any event, files will be
+ * created as required.
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+splitter_tentative_open_test(hid_t child_fapl_id)
+{
+ char filename_rw[H5FD_SPLITTER_PATH_MAX + 1];
+ H5FD_splitter_vfd_config_t vfd_config;
+ hid_t fapl_id = H5I_INVALID_HID;
+ hid_t file_id = H5I_INVALID_HID;
+ int buf[SPLITTER_SIZE][SPLITTER_SIZE]; /* for comparison */
+ hsize_t dims[2] = { SPLITTER_SIZE, SPLITTER_SIZE }; /* for comparison */
+ int i = 0; /* for comparison */
+ int j = 0; /* for comparison */
+ struct splitter_dataset_def data; /* for comparison */
+ int ret_value = 0;
+
+ /* pre-fill data buffer to write */
+ for (i=0; i < SPLITTER_SIZE; i++) {
+ for (j=0; j < SPLITTER_SIZE; j++) {
+ buf[i][j] = i*100+j;
+ }
+ }
+
+ /* Dataset info */
+ data.buf = (void *)buf;
+ data.mem_type_id = H5T_NATIVE_INT;
+ data.dims = dims;
+ data.n_dims = 2;
+ data.dset_name = SPLITTER_DATASET_NAME;
+
+ vfd_config.magic = H5FD_SPLITTER_MAGIC;
+ vfd_config.version = H5FD_CURR_SPLITTER_VFD_CONFIG_VERSION;
+ vfd_config.ignore_wo_errs = FALSE;
+ vfd_config.rw_fapl_id = child_fapl_id;
+ vfd_config.wo_fapl_id = child_fapl_id;
+
+ if (splitter_prepare_file_paths(&vfd_config, filename_rw) < 0) {
+ SPLITTER_TEST_FAULT("can't prepare splitter file paths\n");
+ }
+
+ /* Create a new fapl to use the SPLITTER file driver */
+ if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) == H5I_INVALID_HID) {
+ SPLITTER_TEST_FAULT("can't create FAPL ID\n");
+ }
+ if (H5Pset_fapl_splitter(fapl_id, &vfd_config) < 0) {
+ SPLITTER_TEST_FAULT("can't set splitter FAPL\n");
+ }
+ if (H5Pget_driver(fapl_id) != H5FD_SPLITTER) {
+ SPLITTER_TEST_FAULT("set FAPL not SPLITTER\n");
+ }
+
+ /* H5Fopen() with RDWR access.
+ * Neither file exist already
+ * Should fail.
+ */
+
+ H5E_BEGIN_TRY {
+ file_id = H5Fopen(filename_rw, H5F_ACC_RDWR, fapl_id);
+ } H5E_END_TRY;
+ if (file_id != H5I_INVALID_HID) {
+ SPLITTER_TEST_FAULT("open with both nonexistent files unexpectedly succeeded\n");
+ }
+ if (file_exists(filename_rw, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("R/W file unexpectedly created\n");
+ }
+ if (file_exists(vfd_config.wo_path, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("W/O file unexpectedly created\n");
+ }
+
+ /* H5Fopen() with RDWR access.
+ * W/O file exists already.
+ * Should fail.
+ */
+
+ if (splitter_create_single_file_at(vfd_config.wo_path, child_fapl_id, &data) < 0) {
+ SPLITTER_TEST_FAULT("can't write W/O file\n");
+ }
+ H5E_BEGIN_TRY {
+ file_id = H5Fopen(filename_rw, H5F_ACC_RDWR, fapl_id);
+ } H5E_END_TRY;
+ if (file_id != H5I_INVALID_HID) {
+ SPLITTER_TEST_FAULT("open with nonexistent R/W file unexpectedly succeeded\n");
+ }
+ if (file_exists(filename_rw, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("R/W file unexpectedly created\n");
+ }
+ if (!file_exists(vfd_config.wo_path, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("W/O file mysteriously disappeared\n");
+ }
+ HDremove(vfd_config.wo_path);
+ if (file_exists(vfd_config.wo_path, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("failed to remove W/O file\n");
+ }
+
+ /* H5Fopen() with RDWR access.
+ * R/W file exists already.
+ * Should fail.
+ */
+
+ if (splitter_create_single_file_at(filename_rw, child_fapl_id, &data) < 0) {
+ SPLITTER_TEST_FAULT("can't write R/W file\n");
+ }
+ H5E_BEGIN_TRY {
+ file_id = H5Fopen(filename_rw, H5F_ACC_RDWR, fapl_id);
+ } H5E_END_TRY;
+ if (file_id != H5I_INVALID_HID) {
+ SPLITTER_TEST_FAULT("open with nonexistent W/O unexpectedly succeeded\n");
+ }
+ if (!file_exists(filename_rw, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("R/W file mysteriously disappeared\n");
+ }
+ if (file_exists(vfd_config.wo_path, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("W/O file unexpectedly created\n");
+ }
+
+ /* H5Fopen() with RDWR access.
+ * Both files already exist.
+ */
+
+ if (splitter_create_single_file_at(vfd_config.wo_path, child_fapl_id, &data) < 0) {
+ SPLITTER_TEST_FAULT("can't write W/O file\n");
+ }
+ file_id = H5Fopen(filename_rw, H5F_ACC_RDWR, fapl_id);
+ if (file_id == H5I_INVALID_HID) {
+ SPLITTER_TEST_FAULT("file-open failed with both present\n");
+ }
+ /* Open successful; close file then inspect presence again */
+ if (H5Fclose(file_id) < 0) {
+ SPLITTER_TEST_FAULT("can't close file ID\n");
+ }
+ if (!file_exists(filename_rw, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("R/W file mysteriously disappared\n");
+ }
+ if (!file_exists(vfd_config.wo_path, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("W/O file mysteriously disappeared\n");
+ }
+ if (h5_compare_file_bytes(filename_rw, vfd_config.wo_path) < 0) {
+ SPLITTER_TEST_FAULT("files are not byte-for-byte equivalent\n");
+ }
+
+ /* H5Fcreate() with TRUNC access.
+ * Both files already exist.
+ */
+
+ file_id = H5Fcreate(filename_rw, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id);
+ if (file_id == H5I_INVALID_HID) {
+ SPLITTER_TEST_FAULT("file-open failed with both present\n");
+ }
+ /* Open successful; close file then inspect presence again */
+ if (H5Fclose(file_id) < 0) {
+ SPLITTER_TEST_FAULT("can't close file ID\n");
+ }
+ if (!file_exists(filename_rw, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("R/W file mysteriously disappared\n");
+ }
+ if (!file_exists(vfd_config.wo_path, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("W/O file mysteriously disappeared\n");
+ }
+ if (h5_compare_file_bytes(filename_rw, vfd_config.wo_path) < 0) {
+ SPLITTER_TEST_FAULT("files are not byte-for-byte equivalent\n");
+ }
+
+ /* H5Fcreate() with TRUNC access.
+ * R/W already exists.
+ */
+
+ HDremove(filename_rw);
+ HDremove(vfd_config.wo_path);
+ if (splitter_create_single_file_at(filename_rw, child_fapl_id, &data) < 0) {
+ SPLITTER_TEST_FAULT("can't write R/W file\n");
+ }
+
+ if (file_exists(vfd_config.wo_path, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("failed to remove W/O file\n");
+ }
+ file_id = H5Fcreate(filename_rw, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id);
+ if (file_id == H5I_INVALID_HID) {
+ SPLITTER_TEST_FAULT("file-open failed with both present\n");
+ }
+ /* Open successful; close file then inspect presence again */
+ if (H5Fclose(file_id) < 0) {
+ SPLITTER_TEST_FAULT("can't close file ID\n");
+ }
+ if (!file_exists(filename_rw, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("R/W file mysteriously disappared\n");
+ }
+ if (!file_exists(vfd_config.wo_path, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("W/O file mysteriously disappeared\n");
+ }
+ if (h5_compare_file_bytes(filename_rw, vfd_config.wo_path) < 0) {
+ SPLITTER_TEST_FAULT("files are not byte-for-byte equivalent\n");
+ }
+
+ /* H5Fcreate() with TRUNC access.
+ * W/O already exists.
+ */
+
+ HDremove(filename_rw);
+ HDremove(vfd_config.wo_path);
+ if (splitter_create_single_file_at(vfd_config.wo_path, child_fapl_id, &data) < 0) {
+ SPLITTER_TEST_FAULT("can't write R/W file\n");
+ }
+
+ if (file_exists(filename_rw, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("failed to remove R/W file\n");
+ }
+ file_id = H5Fcreate(filename_rw, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id);
+ if (file_id == H5I_INVALID_HID) {
+ SPLITTER_TEST_FAULT("file-open failed with both present\n");
+ }
+ /* Open successful; close file then inspect presence again */
+ if (H5Fclose(file_id) < 0) {
+ SPLITTER_TEST_FAULT("can't close file ID\n");
+ }
+ if (!file_exists(filename_rw, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("R/W file mysteriously disappared\n");
+ }
+ if (!file_exists(vfd_config.wo_path, child_fapl_id)) {
+ SPLITTER_TEST_FAULT("W/O file mysteriously disappeared\n");
+ }
+ if (h5_compare_file_bytes(filename_rw, vfd_config.wo_path) < 0) {
+ SPLITTER_TEST_FAULT("files are not byte-for-byte equivalent\n");
+ }
+
+ /* H5Fcreate with both files absent is tested elsewhere */
+
+ /* Cleanup
+ */
+
+ if (H5Pclose(fapl_id) < 0) {
+ SPLITTER_TEST_FAULT("can't close splitter FAPL ID\n");
+ }
+
+done:
+ if (ret_value < 0) {
+ H5E_BEGIN_TRY {
+ (void)H5Pclose(fapl_id);
+ (void)H5Fclose(file_id);
+ } H5E_END_TRY;
+ } /* end if error */
+ return ret_value;
+} /* end splitter_tentative_open_test() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: file_exists()
+ *
+ * Purpose: Determine whether a file exists on-system
+ *
+ * Return: Non-zero (1) if it exists (H5Fopen successful),
+ * zero (0) if absent (cannot be opened).
+ *
+ * Description: Attempt H5Fopen with the given FAPL ID and RDONLY access flag.
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+file_exists(const char *filename, hid_t fapl_id)
+{
+ hid_t file_id = H5I_INVALID_HID;
+ int ret_value = 0;
+
+ H5E_BEGIN_TRY {
+ file_id = H5Fopen(filename, H5F_ACC_RDONLY, fapl_id);
+ } H5E_END_TRY;
+ if (file_id != H5I_INVALID_HID) {
+ ret_value = 1;
+ if (H5Fclose(file_id) < 0) {
+ FAIL_PUTS_ERROR("can't close file ID\n");
+ }
+ }
+
+ return ret_value;
+
+error:
+ H5E_BEGIN_TRY {
+ (void)H5Fclose(file_id);
+ } H5E_END_TRY;
+ return ret_value;
+} /* end file_exists() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: test_splitter
+ *
+ * Purpose: Tests the Splitter VFD
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ * Description:
+ * This test function uses the Splitter VFD to produce a r/w
+ * file and a w/o file. It will verify that the two files
+ * are identical.
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+test_splitter(void)
+{
+ int buf[SPLITTER_SIZE][SPLITTER_SIZE];
+ hsize_t dims[2] = { SPLITTER_SIZE, SPLITTER_SIZE };
+ hid_t child_fapl_id = H5I_INVALID_HID;
+ int i = 0;
+ int j = 0;
+ struct splitter_dataset_def data;
+
+ TESTING("SPLITTER file driver");
+
+ /* pre-fill data buffer to write */
+ for (i=0; i < SPLITTER_SIZE; i++) {
+ for (j=0; j < SPLITTER_SIZE; j++) {
+ buf[i][j] = i*100+j;
+ }
+ }
+
+ /* Dataset info */
+ data.buf = (void *)buf;
+ data.mem_type_id = H5T_NATIVE_INT;
+ data.dims = dims;
+ data.n_dims = 2;
+ data.dset_name = SPLITTER_DATASET_NAME;
+
+ /* Stand-in for manual FAPL creation
+ * Enables verification with arbitrary VFDs via `make check-vfd`
+ */
+ child_fapl_id = h5_fileaccess();
+ if (child_fapl_id < 0) {
+ TEST_ERROR;
+ }
+
+ if (!driver_is_splitter_compatible(child_fapl_id)) {
+ SKIPPED();
+ HDprintf(" given driver is not Splitter W/O compatible.\n");
+ return 0;
+ }
+
+ /* Test Read-Only access, including when a file on the W/O channel
+ * does not exist.
+ */
+ if (splitter_RO_test(&data, child_fapl_id) < 0) {
+ TEST_ERROR;
+ }
+
+ /* Test opening of files when the W/O channel does not exist.
+ */
+ if (splitter_tentative_open_test(child_fapl_id) < 0) {
+ TEST_ERROR;
+ }
+
+
+ /* Test file creation, utilizing different child FAPLs (default vs.
+ * specified), logfile, and Write Channel error ignoring behavior.
+ */
+ for (i=0; i < 4; i++) {
+ hbool_t ignore_wo_errors = (i & 1) ? TRUE : FALSE;
+ hbool_t provide_logfile_path = (i & 2) ? TRUE : FALSE;
+ hid_t child_fapl_ids[2] = { H5P_DEFAULT, H5P_DEFAULT };
+
+ /* Test child driver definition/default combination */
+ for (j=0; j < 4; j++) {
+
+ child_fapl_ids[0] = (j & 1) ? child_fapl_id : H5P_DEFAULT;
+ child_fapl_ids[1] = (j & 2) ? child_fapl_id : H5P_DEFAULT;
+
+ if (run_splitter_test(&data, ignore_wo_errors, provide_logfile_path, child_fapl_ids) < 0) {
+ TEST_ERROR;
+ }
+
+ } /* end for child fapl definition/pairing */
+
+ } /* end for behavior-flag loops */
+
+/* TODO: SWMR open? */
+/* Concurrent opens with both drivers using the Splitter */
+
+
+ if (H5Pclose(child_fapl_id) == FAIL) {
+ TEST_ERROR;
+ }
+
+ PASSED();
+ return 0;
+
+error:
+ if (child_fapl_id != H5I_INVALID_HID) {
+ (void)H5Pclose(child_fapl_id);
+ }
+ return -1;
+} /* end test_splitter() */
+
+#undef SPLITTER_TEST_FAULT
+
+
/*-------------------------------------------------------------------------
* Function: main
*
@@ -2187,9 +3276,6 @@ error:
* Return: Success: 0
* Failure: 1
*
- * Programmer: Raymond Lu
- * Tuesday, Sept 24, 2002
- *
*-------------------------------------------------------------------------
*/
int
@@ -2213,6 +3299,7 @@ main(void)
nerrors += test_stdio() < 0 ? 1 : 0;
nerrors += test_windows() < 0 ? 1 : 0;
nerrors += test_ros3() < 0 ? 1 : 0;
+ nerrors += test_splitter() < 0 ? 1 : 0;
if(nerrors) {
HDprintf("***** %d Virtual File Driver TEST%s FAILED! *****\n",