From 7bca2f04a45de38dde4981c5359ca007c36ceba8 Mon Sep 17 00:00:00 2001 From: Dana Robinson Date: Fri, 14 Aug 2020 14:51:53 -0700 Subject: Brings splitter VFD from develop --- MANIFEST | 2 + bin/trace | 1 + release_docs/RELEASE.txt | 8 + src/CMakeLists.txt | 2 + src/H5FDpublic.h | 2 + src/H5FDsplitter.c | 1346 ++++++++++++++++++++++++++++++++++++++++++++++ src/H5FDsplitter.h | 99 ++++ src/Makefile.am | 5 +- src/hdf5.h | 1 + test/CMakeTests.cmake | 2 + test/Makefile.am | 3 +- test/h5test.c | 148 +++++ test/h5test.h | 2 + test/vfd.c | 1142 +++++++++++++++++++++++++++++++++++++++ 14 files changed, 2760 insertions(+), 3 deletions(-) create mode 100644 src/H5FDsplitter.c create mode 100644 src/H5FDsplitter.h diff --git a/MANIFEST b/MANIFEST index 025a153..9e89e57 100644 --- a/MANIFEST +++ b/MANIFEST @@ -699,6 +699,8 @@ ./src/H5FDsec2.c ./src/H5FDsec2.h ./src/H5FDspace.c +./src/H5FDsplitter.c +./src/H5FDsplitter.h ./src/H5FDstdio.c ./src/H5FDstdio.h ./src/H5FDtest.c diff --git a/bin/trace b/bin/trace index ab7a92c..6b5dd17 100755 --- a/bin/trace +++ b/bin/trace @@ -43,6 +43,7 @@ $Source = ""; "H5D_space_status_t" => "Ds", "H5D_vds_view_t" => "Dv", "H5FD_mpio_xfer_t" => "Dt", + "H5FD_splitter_vfd_config_t" => "Dr", "herr_t" => "e", "H5E_direction_t" => "Ed", "H5E_error_t" => "Ee", diff --git a/release_docs/RELEASE.txt b/release_docs/RELEASE.txt index 53467d5..909401e 100644 --- a/release_docs/RELEASE.txt +++ b/release_docs/RELEASE.txt @@ -303,6 +303,14 @@ New Features Library: -------- + + - Add Splitter VFD + + Maintain separate R/W and W/O channels for "concurrent" file writes + to two files using a single HDF5 file handle. + + (JOS - 2020/03/13, TBD) + - Add S3 and HDFS VFDs to HDF5 maintenance Fix windows requirements and java tests. Windows requires CMake 3.13. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9ff16c9..34afd42 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -230,6 +230,7 @@ set (H5FD_SOURCES ${HDF5_SRC_DIR}/H5FDs3comms.c ${HDF5_SRC_DIR}/H5FDsec2.c ${HDF5_SRC_DIR}/H5FDspace.c + ${HDF5_SRC_DIR}/H5FDsplitter.c ${HDF5_SRC_DIR}/H5FDstdio.c ${HDF5_SRC_DIR}/H5FDtest.c ${HDF5_SRC_DIR}/H5FDwindows.c @@ -248,6 +249,7 @@ set (H5FD_HDRS ${HDF5_SRC_DIR}/H5FDros3.h ${HDF5_SRC_DIR}/H5FDs3comms.h ${HDF5_SRC_DIR}/H5FDsec2.h + ${HDF5_SRC_DIR}/H5FDsplitter.h ${HDF5_SRC_DIR}/H5FDstdio.h ${HDF5_SRC_DIR}/H5FDwindows.h ) diff --git a/src/H5FDpublic.h b/src/H5FDpublic.h index 54ca8f7..61bf212 100644 --- a/src/H5FDpublic.h +++ b/src/H5FDpublic.h @@ -255,6 +255,8 @@ typedef enum H5F_mem_t H5FD_mem_t; * that creates a file which is compatible with the default VFD. * Generally, this means that the VFD creates a single file that follows * the canonical HDF5 file format. + * Regarding the Splitter VFD specifically, only drivers with this flag + * enabled may be used as the Write-Only (W/O) channel driver. */ #define H5FD_FEAT_DEFAULT_VFD_COMPATIBLE 0x00008000 diff --git a/src/H5FDsplitter.c b/src/H5FDsplitter.c new file mode 100644 index 0000000..d0ed250d --- /dev/null +++ b/src/H5FDsplitter.c @@ -0,0 +1,1346 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the COPYING file, which can be found at the root of the source code * + * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. * + * If you do not have access to either file, you may request a copy from * + * help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Purpose: The Splitter VFD implements a file driver which relays all the + * VFD calls to an underlying VFD, and send all the write calls to + * another underlying VFD. Maintains two files simultaneously. + */ + +/* This source code file is part of the H5FD driver module */ +#include "H5FDdrvr_module.h" + +#include "H5private.h" /* Generic Functions */ +#include "H5Eprivate.h" /* Error handling */ +#include "H5Fprivate.h" /* File access */ +#include "H5FDprivate.h" /* File drivers */ +#include "H5FDsplitter.h" /* Splitter file driver */ +#include "H5FLprivate.h" /* Free Lists */ +#include "H5Iprivate.h" /* IDs */ +#include "H5MMprivate.h" /* Memory management */ +#include "H5Pprivate.h" /* Property lists */ + +/* The driver identification number, initialized at runtime */ +static hid_t H5FD_SPLITTER_g = 0; + +/* Driver-specific file access properties */ +typedef struct H5FD_splitter_fapl_t { + hid_t rw_fapl_id; /* fapl for the R/W channel */ + hid_t wo_fapl_id; /* fapl for the W/O channel */ + char wo_path[H5FD_SPLITTER_PATH_MAX + 1]; /* file name for the W/O channel */ + char log_file_path[H5FD_SPLITTER_PATH_MAX + 1]; /* file to record errors reported by the W/O channel */ + hbool_t ignore_wo_errs; /* TRUE to ignore errors on the W/O channel */ +} H5FD_splitter_fapl_t; + +/* The information of this splitter */ +typedef struct H5FD_splitter_t { + H5FD_t pub; /* public stuff, must be first */ + unsigned version; /* version of the H5FD_splitter_vfd_config_t structure used */ + H5FD_splitter_fapl_t fa; /* driver-specific file access properties */ + H5FD_t *rw_file; /* pointer of R/W channel */ + H5FD_t *wo_file; /* pointer of W/O channel */ + FILE *logfp; /* Log file pointer */ +} H5FD_splitter_t; + +/* + * These macros check for overflow of various quantities. These macros + * assume that HDoff_t is signed and haddr_t and size_t are unsigned. + * + * ADDR_OVERFLOW: Checks whether a file address of type `haddr_t' + * is too large to be represented by the second argument + * of the file seek function. + * + * SIZE_OVERFLOW: Checks whether a buffer size of type `hsize_t' is too + * large to be represented by the `size_t' type. + * + * REGION_OVERFLOW: Checks whether an address and size pair describe data + * which can be addressed entirely by the second + * argument of the file seek function. + */ +#define MAXADDR (((haddr_t)1<<(8*sizeof(HDoff_t)-1))-1) +#define ADDR_OVERFLOW(A) (HADDR_UNDEF==(A) || ((A) & ~(haddr_t)MAXADDR)) +#define SIZE_OVERFLOW(Z) ((Z) & ~(hsize_t)MAXADDR) +#define REGION_OVERFLOW(A,Z) (ADDR_OVERFLOW(A) || SIZE_OVERFLOW(Z) || \ + HADDR_UNDEF==(A)+(Z) || \ + (HDoff_t)((A)+(Z))<(HDoff_t)(A)) + +/* This macro provides a wrapper for shared fail-log-ignore behavior + * for errors arising in the splitter's W/O channel. + * Logs an error entry in a log file, if the file exists. + * If not set to ignore errors, registers an error with the library. + */ +#define H5FD_SPLITTER_WO_ERROR(file, funcname, errmajor, errminor, ret, mesg) \ +{ \ + H5FD__splitter_log_error((file), (funcname), (mesg)); \ + if(FALSE == (file)->fa.ignore_wo_errs) \ + HGOTO_ERROR((errmajor), (errminor), (ret), (mesg)) \ +} + +#define H5FD_SPLITTER_DEBUG_OP_CALLS 0 /* debugging print toggle; 0 disables */ + +#if H5FD_SPLITTER_DEBUG_OP_CALLS +#define H5FD_SPLITTER_LOG_CALL(name) do { \ + HDprintf("called %s()\n", (name)); \ + HDfflush(stdout); \ +} while (0) +#else +#define H5FD_SPLITTER_LOG_CALL(name) /* no-op */ +#endif /* H5FD_SPLITTER_DEBUG_OP_CALLS */ + +/* Private functions */ + +/* Print error messages from W/O channel to log file */ +static herr_t H5FD__splitter_log_error(const H5FD_splitter_t *file, const char *atfunc, const char *msg); +static int H5FD__copy_plist(hid_t fapl_id, hid_t *id_out_ptr); + +/* Prototypes */ +static herr_t H5FD__splitter_term(void); +static hsize_t H5FD__splitter_sb_size(H5FD_t *_file); +static herr_t H5FD__splitter_sb_encode(H5FD_t *_file, char *name/*out*/, unsigned char *buf/*out*/); +static herr_t H5FD__splitter_sb_decode(H5FD_t *_file, const char *name, const unsigned char *buf); +static void *H5FD__splitter_fapl_get(H5FD_t *_file); +static void *H5FD__splitter_fapl_copy(const void *_old_fa); +static herr_t H5FD__splitter_fapl_free(void *_fapl); +static H5FD_t *H5FD__splitter_open(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr); +static herr_t H5FD__splitter_close(H5FD_t *_file); +static int H5FD__splitter_cmp(const H5FD_t *_f1, const H5FD_t *_f2); +static herr_t H5FD__splitter_query(const H5FD_t *_file, unsigned long *flags /* out */); +static herr_t H5FD__splitter_get_type_map(const H5FD_t *_file, H5FD_mem_t *type_map); +static haddr_t H5FD__splitter_alloc(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, hsize_t size); +static herr_t H5FD__splitter_free(H5FD_t *_file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, hsize_t size); +static haddr_t H5FD__splitter_get_eoa(const H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type); +static herr_t H5FD__splitter_set_eoa(H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type, haddr_t addr); +static haddr_t H5FD__splitter_get_eof(const H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type); +static herr_t H5FD__splitter_get_handle(H5FD_t *_file, hid_t H5_ATTR_UNUSED fapl, void** file_handle); +static herr_t H5FD__splitter_read(H5FD_t *_file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, size_t size, void *buf); +static herr_t H5FD__splitter_write(H5FD_t *_file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, size_t size, const void *buf); +static herr_t H5FD__splitter_flush(H5FD_t *_file, hid_t dxpl_id, hbool_t closing); +static herr_t H5FD__splitter_truncate(H5FD_t *_file, hid_t dxpl_id, hbool_t closing); +static herr_t H5FD__splitter_lock(H5FD_t *_file, hbool_t rw); +static herr_t H5FD__splitter_unlock(H5FD_t *_file); + +static const H5FD_class_t H5FD_splitter_g = { + "splitter", /* name */ + MAXADDR, /* maxaddr */ + H5F_CLOSE_WEAK, /* fc_degree */ + H5FD__splitter_term, /* terminate */ + H5FD__splitter_sb_size, /* sb_size */ + H5FD__splitter_sb_encode, /* sb_encode */ + H5FD__splitter_sb_decode, /* sb_decode */ + sizeof(H5FD_splitter_fapl_t), /* fapl_size */ + H5FD__splitter_fapl_get, /* fapl_get */ + H5FD__splitter_fapl_copy, /* fapl_copy */ + H5FD__splitter_fapl_free, /* fapl_free */ + 0, /* dxpl_size */ + NULL, /* dxpl_copy */ + NULL, /* dxpl_free */ + H5FD__splitter_open, /* open */ + H5FD__splitter_close, /* close */ + H5FD__splitter_cmp, /* cmp */ + H5FD__splitter_query, /* query */ + H5FD__splitter_get_type_map, /* get_type_map */ + H5FD__splitter_alloc, /* alloc */ + H5FD__splitter_free, /* free */ + H5FD__splitter_get_eoa, /* get_eoa */ + H5FD__splitter_set_eoa, /* set_eoa */ + H5FD__splitter_get_eof, /* get_eof */ + H5FD__splitter_get_handle, /* get_handle */ + H5FD__splitter_read, /* read */ + H5FD__splitter_write, /* write */ + H5FD__splitter_flush, /* flush */ + H5FD__splitter_truncate, /* truncate */ + H5FD__splitter_lock, /* lock */ + H5FD__splitter_unlock, /* unlock */ + H5FD_FLMAP_DICHOTOMY /* fl_map */ +}; + +/* Declare a free list to manage the H5FD_splitter_t struct */ +H5FL_DEFINE_STATIC(H5FD_splitter_t); + +/* Declare a free list to manage the H5FD_splitter_fapl_t struct */ +H5FL_DEFINE_STATIC(H5FD_splitter_fapl_t); + + +/*------------------------------------------------------------------------- + * Function: H5FD__init_package + * + * Purpose: Initializes any interface-specific data or routines. + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5FD__init_package(void) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + if(H5FD_splitter_init() < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "unable to initialize splitter VFD") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5FD__init_package() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_splitter_init + * + * Purpose: Initialize the splitter driver by registering it with the + * library. + * + * Return: Success: The driver ID for the splitter driver. + * Failure: Negative + *------------------------------------------------------------------------- + */ +hid_t +H5FD_splitter_init(void) +{ + hid_t ret_value = H5I_INVALID_HID; + + FUNC_ENTER_NOAPI(FAIL) + + H5FD_SPLITTER_LOG_CALL(FUNC); + + if(H5I_VFL != H5I_get_type(H5FD_SPLITTER_g)) + H5FD_SPLITTER_g = H5FDregister(&H5FD_splitter_g); + + ret_value = H5FD_SPLITTER_g; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD_splitter_init() */ + + +/*--------------------------------------------------------------------------- + * Function: H5FD__splitter_term + * + * Purpose: Shut down the splitter VFD. + * + * Returns: SUCCEED (Can't fail) + *--------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_term(void) +{ + FUNC_ENTER_STATIC_NOERR + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Reset VFL ID */ + H5FD_SPLITTER_g = 0; + + FUNC_LEAVE_NOAPI(SUCCEED) +} /* end H5FD__splitter_term() */ + + + /*------------------------------------------------------------------------- + * Function: H5FD__copy_plist + * + * Purpose: Sanity-wrapped H5P_copy_plist() for each channel. + * Utility function for operation in multiple locations. + * + * Return: 0 on success, -1 on error. + *------------------------------------------------------------------------- + */ +static int +H5FD__copy_plist(hid_t fapl_id, + hid_t *id_out_ptr) +{ + int ret_value = 0; + H5P_genplist_t *plist_ptr = NULL; + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + HDassert(id_out_ptr != NULL); + + if(FALSE == H5P_isa_class(fapl_id, H5P_FILE_ACCESS)) + HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, -1, "not a file access property list"); + + plist_ptr = (H5P_genplist_t *)H5I_object(fapl_id); + if(NULL == plist_ptr) + HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, -1, "unable to get property list"); + + *id_out_ptr = H5P_copy_plist(plist_ptr, FALSE); + if(H5I_INVALID_HID == *id_out_ptr) + HGOTO_ERROR(H5E_VFL, H5E_BADTYPE, -1, "unable to copy file access property list"); + +done: + FUNC_LEAVE_NOAPI(ret_value); +} /* end H5FD__copy_plist() */ + + +/*------------------------------------------------------------------------- + * Function: H5Pset_fapl_splitter + * + * Purpose: Sets the file access property list to use the + * splitter driver. + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +herr_t +H5Pset_fapl_splitter(hid_t fapl_id, H5FD_splitter_vfd_config_t *vfd_config) +{ + H5FD_splitter_fapl_t *info = NULL; + H5P_genplist_t *plist_ptr = NULL; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_API(FAIL) + H5TRACE2("e", "i*Dr", fapl_id, vfd_config); + + H5FD_SPLITTER_LOG_CALL(FUNC); + + if(H5FD_SPLITTER_MAGIC != vfd_config->magic) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid configuration (magic number mismatch)") + if(H5FD_CURR_SPLITTER_VFD_CONFIG_VERSION != vfd_config->version) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid config (version number mismatch)") + if(NULL == (plist_ptr = (H5P_genplist_t *)H5I_object(fapl_id))) + HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a valid property list") + + /* Make sure that the W/O channel supports write-only capability. + * Some drivers (e.g. family or multi) do revision of the superblock + * in-memory, causing problems in that channel. + * Uses the feature flag H5FD_FEAT_DEFAULT_VFD_COMPATIBLE as the + * determining attribute. + */ + if(H5P_DEFAULT != vfd_config->wo_fapl_id) { + H5FD_class_t *wo_driver = NULL; + H5FD_driver_prop_t wo_driver_prop; + H5P_genplist_t *wo_plist_ptr = NULL; + unsigned long wo_driver_flags = 0; + + wo_plist_ptr = (H5P_genplist_t *)H5I_object(vfd_config->wo_fapl_id); + if(NULL == wo_plist_ptr) + HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file access property list") + if(H5P_peek(wo_plist_ptr, H5F_ACS_FILE_DRV_NAME, &wo_driver_prop) < 0) + HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get driver ID & info") + wo_driver = (H5FD_class_t *)H5I_object(wo_driver_prop.driver_id); + if(NULL == wo_driver) + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid driver ID in file access property list") + if(H5FD_driver_query(wo_driver, &wo_driver_flags) < 0) + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "can't query VFD flags") + if(0 == (H5FD_FEAT_DEFAULT_VFD_COMPATIBLE & wo_driver_flags)) + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "unsuitable W/O driver") + } /* end if W/O VFD is non-default */ + + info = H5FL_CALLOC(H5FD_splitter_fapl_t); + if(NULL == info) + HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "unable to allocate file access property list struct") + + info->ignore_wo_errs = vfd_config->ignore_wo_errs; + HDstrncpy(info->wo_path, vfd_config->wo_path, H5FD_SPLITTER_PATH_MAX); + HDstrncpy(info->log_file_path, vfd_config->log_file_path, H5FD_SPLITTER_PATH_MAX); + info->rw_fapl_id = H5P_FILE_ACCESS_DEFAULT; /* pre-set value */ + info->wo_fapl_id = H5P_FILE_ACCESS_DEFAULT; /* pre-set value */ + + /* Set non-default channel FAPL IDs in splitter configuration info */ + if(H5P_DEFAULT != vfd_config->rw_fapl_id) { + if(FALSE == H5P_isa_class(vfd_config->rw_fapl_id, H5P_FILE_ACCESS)) + HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file access list") + info->rw_fapl_id = vfd_config->rw_fapl_id; + } + if(H5P_DEFAULT != vfd_config->wo_fapl_id) { + if(FALSE == H5P_isa_class(vfd_config->wo_fapl_id, H5P_FILE_ACCESS)) + HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file access list") + info->wo_fapl_id = vfd_config->wo_fapl_id; + } + + ret_value = H5P_set_driver(plist_ptr, H5FD_SPLITTER, info); + +done: + if(info) + info = H5FL_FREE(H5FD_splitter_fapl_t, info); + + FUNC_LEAVE_API(ret_value) +} /* end H5Pset_fapl_splitter() */ + + +/*------------------------------------------------------------------------- + * Function: H5Pget_fapl_splitter + * + * Purpose: Returns information about the splitter file access property + * list through the structure config_out. + * + * Will fail if config_out is received without pre-set valid + * magic and version information. + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +herr_t +H5Pget_fapl_splitter(hid_t fapl_id, H5FD_splitter_vfd_config_t *config_out) +{ + const H5FD_splitter_fapl_t *fapl_ptr = NULL; + H5P_genplist_t *plist_ptr = NULL; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_API(FAIL) + H5TRACE2("e", "i*Dr", fapl_id, config_out); + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Check arguments */ + if(TRUE != H5P_isa_class(fapl_id, H5P_FILE_ACCESS)) + HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file access property list") + if(config_out == NULL) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "config_out pointer is null") + if(H5FD_SPLITTER_MAGIC != config_out->magic) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "info-out pointer invalid (magic number mismatch)") + if(H5FD_CURR_SPLITTER_VFD_CONFIG_VERSION != config_out->version) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "info-out pointer invalid (version unsafe)") + + /* Pre-set out FAPL IDs with intent to replace these values */ + config_out->rw_fapl_id = H5I_INVALID_HID; + config_out->wo_fapl_id = H5I_INVALID_HID; + + /* Check and get the splitter fapl */ + if(NULL == (plist_ptr = H5P_object_verify(fapl_id, H5P_FILE_ACCESS))) + HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file access property list") + if(H5FD_SPLITTER != H5P_peek_driver(plist_ptr)) + HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "incorrect VFL driver") + if(NULL == (fapl_ptr = (const H5FD_splitter_fapl_t *)H5P_peek_driver_info(plist_ptr))) + HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "unable to get specific-driver info") + + HDstrncpy(config_out->wo_path, fapl_ptr->wo_path, H5FD_SPLITTER_PATH_MAX); + HDstrncpy(config_out->log_file_path, fapl_ptr->log_file_path, H5FD_SPLITTER_PATH_MAX); + config_out->ignore_wo_errs = fapl_ptr->ignore_wo_errs; + + /* Copy R/W and W/O FAPLs */ + if(H5FD__copy_plist(fapl_ptr->rw_fapl_id, &(config_out->rw_fapl_id)) < 0) + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "can't copy R/W FAPL"); + if(H5FD__copy_plist(fapl_ptr->wo_fapl_id, &(config_out->wo_fapl_id)) < 0) + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "can't copy W/O FAPL"); + +done: + FUNC_LEAVE_API(ret_value) +} /* end H5Pget_fapl_splitter() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_flush + * + * Purpose: Flushes all data to disk for both channels. + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_flush(H5FD_t *_file, hid_t H5_ATTR_UNUSED dxpl_id, hbool_t closing) +{ + H5FD_splitter_t *file = (H5FD_splitter_t *)_file; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Public API for dxpl "context" */ + if(H5FDflush(file->rw_file, dxpl_id, closing) < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTFLUSH, FAIL, "unable to flush R/W file") + if(H5FDflush(file->wo_file, dxpl_id, closing) < 0) + H5FD_SPLITTER_WO_ERROR(file, FUNC, H5E_VFL, H5E_CANTFLUSH, FAIL, "unable to flush W/O file") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_flush() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_read + * + * Purpose: Reads SIZE bytes of data from the R/W channel, beginning at + * address ADDR into buffer BUF according to data transfer + * properties in DXPL_ID. + * + * Return: Success: SUCCEED + * The read result is written into the BUF buffer + * which should be allocated by the caller. + * Failure: FAIL + * The contents of BUF are undefined. + *------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_read(H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type, + hid_t H5_ATTR_UNUSED dxpl_id, haddr_t addr, size_t size, void *buf) +{ + H5FD_splitter_t *file = (H5FD_splitter_t *)_file; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + HDassert(file && file->pub.cls); + HDassert(buf); + + /* Check for overflow conditions */ + if(!H5F_addr_defined(addr)) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "addr undefined, addr = %llu", (unsigned long long)addr) + if(REGION_OVERFLOW(addr, size)) + HGOTO_ERROR(H5E_ARGS, H5E_OVERFLOW, FAIL, "addr overflow, addr = %llu", (unsigned long long)addr) + + /* Only read from R/W channel */ + /* Public API for dxpl "context" */ + if(H5FDread(file->rw_file, type, dxpl_id, addr, size, buf) < 0) + HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "Reading from R/W channel failed") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_read() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_write + * + * Purpose: Writes SIZE bytes of data to R/W and W/O channels, beginning + * at address ADDR from buffer BUF according to data transfer + * properties in DXPL_ID. + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_write(H5FD_t *_file, H5FD_mem_t type, hid_t dxpl_id, + haddr_t addr, size_t size, const void *buf) +{ + H5FD_splitter_t *file = (H5FD_splitter_t *)_file; + H5P_genplist_t *plist_ptr = NULL; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + if(NULL == (plist_ptr = (H5P_genplist_t *)H5I_object(dxpl_id))) + HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a property list") + + /* Write to each file */ + /* Public API for dxpl "context" */ + if(H5FDwrite(file->rw_file, type, dxpl_id, addr, size, buf) < 0) + HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "R/W file write failed") + if(H5FDwrite(file->wo_file, type, dxpl_id, addr, size, buf) < 0) + H5FD_SPLITTER_WO_ERROR(file, FUNC, H5E_VFL, H5E_WRITEERROR, FAIL, "unable to write W/O file") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_write() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_fapl_get + * + * Purpose: Returns a file access property list which indicates how the + * specified file is being accessed. The return list could be + * used to access another file the same way. + * + * Return: Success: Ptr to new file access property list with all + * members copied from the file struct. + * Failure: NULL + *------------------------------------------------------------------------- + */ +static void * +H5FD__splitter_fapl_get(H5FD_t *_file) +{ + H5FD_splitter_t *file = (H5FD_splitter_t *)_file; + void *ret_value = NULL; + + FUNC_ENTER_STATIC_NOERR + + H5FD_SPLITTER_LOG_CALL(FUNC); + + ret_value = H5FD__splitter_fapl_copy(&(file->fa)); + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_fapl_get() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_fapl_copy + * + * Purpose: Copies the file access properties. + * + * Return: Success: Pointer to a new property list info structure. + * Failure: NULL + *------------------------------------------------------------------------- + */ +static void * +H5FD__splitter_fapl_copy(const void *_old_fa) +{ + const H5FD_splitter_fapl_t *old_fa_ptr = (const H5FD_splitter_fapl_t *)_old_fa; + H5FD_splitter_fapl_t *new_fa_ptr = NULL; + void *ret_value = NULL; + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + HDassert(old_fa_ptr); + + new_fa_ptr = H5FL_CALLOC(H5FD_splitter_fapl_t); + if(NULL == new_fa_ptr) + HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate log file FAPL") + + HDmemcpy(new_fa_ptr, old_fa_ptr, sizeof(H5FD_splitter_fapl_t)); + HDstrncpy(new_fa_ptr->wo_path, old_fa_ptr->wo_path, H5FD_SPLITTER_PATH_MAX); + HDstrncpy(new_fa_ptr->log_file_path, old_fa_ptr->log_file_path, H5FD_SPLITTER_PATH_MAX); + + /* Copy R/W and W/O FAPLs */ + if(H5FD__copy_plist(old_fa_ptr->rw_fapl_id, &(new_fa_ptr->rw_fapl_id)) < 0) + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "can't copy R/W FAPL"); + if(H5FD__copy_plist(old_fa_ptr->wo_fapl_id, &(new_fa_ptr->wo_fapl_id)) < 0) + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "can't copy W/O FAPL"); + + ret_value = (void *)new_fa_ptr; + +done: + if(NULL == ret_value) + if(new_fa_ptr) + new_fa_ptr = H5FL_FREE(H5FD_splitter_fapl_t, new_fa_ptr); + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_fapl_copy() */ + + +/*-------------------------------------------------------------------------- + * Function: H5FD__splitter_fapl_free + * + * Purpose: Releases the file access lists + * + * Return: SUCCEED/FAIL + *-------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_fapl_free(void *_fapl) +{ + H5FD_splitter_fapl_t *fapl = (H5FD_splitter_fapl_t*)_fapl; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Check arguments */ + HDassert(fapl); + + if(H5I_dec_ref(fapl->rw_fapl_id) < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTDEC, FAIL, "can't close R/W FAPL ID") + if(H5I_dec_ref(fapl->wo_fapl_id) < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTDEC, FAIL, "can't close W/O FAPL ID") + + /* Free the property list */ + fapl = H5FL_FREE(H5FD_splitter_fapl_t, fapl); + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_fapl_free() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_open + * + * Purpose: Create and/or opens a file as an HDF5 file. + * + * Return: Success: A pointer to a new file data structure. The + * public fields will be initialized by the + * caller, which is always H5FD_open(). + * Failure: NULL + *------------------------------------------------------------------------- + */ +static H5FD_t * +H5FD__splitter_open(const char *name, unsigned flags, hid_t splitter_fapl_id, haddr_t maxaddr) +{ + H5FD_splitter_t *file_ptr = NULL; /* Splitter VFD info */ + const H5FD_splitter_fapl_t *fapl_ptr = NULL; /* Driver-specific property list */ + H5P_genplist_t *plist_ptr = NULL; + H5FD_t *ret_value = NULL; + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Check arguments */ + if(!name || !*name) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid file name") + if(0 == maxaddr || HADDR_UNDEF == maxaddr) + HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, NULL, "bogus maxaddr") + if(ADDR_OVERFLOW(maxaddr)) + HGOTO_ERROR(H5E_ARGS, H5E_OVERFLOW, NULL, "bogus maxaddr") + if((H5P_FILE_ACCESS_DEFAULT == splitter_fapl_id) || + (H5FD_SPLITTER != H5Pget_driver(splitter_fapl_id)) ) + /* presupposes that H5P_FILE_ACCESS_DEFAULT is not a splitter */ + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "driver is not splitter") + + file_ptr = (H5FD_splitter_t *)H5FL_CALLOC(H5FD_splitter_t); + if(NULL == file_ptr) + HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate file struct") + file_ptr->fa.rw_fapl_id = H5I_INVALID_HID; + file_ptr->fa.wo_fapl_id = H5I_INVALID_HID; + + /* Get the driver-specific file access properties */ + plist_ptr = (H5P_genplist_t *)H5I_object(splitter_fapl_id); + if(NULL == plist_ptr) + HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "not a file access property list") + fapl_ptr = (const H5FD_splitter_fapl_t *)H5P_peek_driver_info(plist_ptr); + if(NULL == fapl_ptr) + HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "unable to get VFL driver info") + + /* Copy simpler info */ + HDstrncpy(file_ptr->fa.wo_path, fapl_ptr->wo_path, H5FD_SPLITTER_PATH_MAX); + HDstrncpy(file_ptr->fa.log_file_path, fapl_ptr->log_file_path, H5FD_SPLITTER_PATH_MAX); + file_ptr->fa.ignore_wo_errs = fapl_ptr->ignore_wo_errs; + + /* Copy R/W and W/O channel FAPLs. */ + if(H5FD__copy_plist(fapl_ptr->rw_fapl_id, &(file_ptr->fa.rw_fapl_id)) < 0) + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "can't copy R/W FAPL"); + if(H5FD__copy_plist(fapl_ptr->wo_fapl_id, &(file_ptr->fa.wo_fapl_id)) < 0) + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "can't copy W/O FAPL"); + + /* Prepare log file if necessary. + * If application wants to ignore the errors from W/O channel and + * provided a name for the log file, then open it + */ + if(!file_ptr->logfp) { + if(file_ptr->fa.log_file_path[0] != '\0') { + file_ptr->logfp = HDfopen(file_ptr->fa.log_file_path, "w"); + if(file_ptr->logfp == NULL) + HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "unable to open log file") + } /* end if logfile path given */ + } /* end if logfile pointer/handle does not exist */ + + file_ptr->rw_file = H5FD_open(name, flags, fapl_ptr->rw_fapl_id, HADDR_UNDEF); + if(!file_ptr->rw_file) + HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "unable to open R/W file") + + file_ptr->wo_file = H5FD_open(fapl_ptr->wo_path, flags, fapl_ptr->wo_fapl_id, HADDR_UNDEF); + if(!file_ptr->wo_file) + H5FD_SPLITTER_WO_ERROR(file_ptr, FUNC, H5E_VFL, H5E_CANTOPENFILE, NULL, "unable to open W/O file") + + ret_value = (H5FD_t*)file_ptr; + +done: + if(NULL == ret_value) { + if(file_ptr) { + if(H5I_INVALID_HID != file_ptr->fa.rw_fapl_id) + H5I_dec_ref(file_ptr->fa.rw_fapl_id); + if(H5I_INVALID_HID != file_ptr->fa.wo_fapl_id) + H5I_dec_ref(file_ptr->fa.wo_fapl_id); + if(file_ptr->rw_file) + H5FD_close(file_ptr->rw_file); + if(file_ptr->wo_file) + H5FD_close(file_ptr->wo_file); + if(file_ptr->logfp) + HDfclose(file_ptr->logfp); + H5FL_FREE(H5FD_splitter_t, file_ptr); + } + } /* end if error */ + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_open() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_close + * + * Purpose: Closes files on both read-write and write-only channels. + * + * Return: Success: SUCCEED + * Failure: FAIL, file not closed. + *------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_close(H5FD_t *_file) +{ + H5FD_splitter_t *file = (H5FD_splitter_t *)_file; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Sanity check */ + HDassert(file); + + if(H5I_dec_ref(file->fa.rw_fapl_id) < 0) + HGOTO_ERROR(H5E_VFL, H5E_ARGS, FAIL, "can't close R/W FAPL") + if(H5I_dec_ref(file->fa.wo_fapl_id) < 0) + HGOTO_ERROR(H5E_VFL, H5E_ARGS, FAIL, "can't close W/O FAPL") + + if(file->rw_file) + if(H5FD_close(file->rw_file) == FAIL) + HGOTO_ERROR(H5E_VFL, H5E_CANTCLOSEFILE, FAIL, "unable to close R/W file") + if(file->wo_file) + if(H5FD_close(file->wo_file) == FAIL) + H5FD_SPLITTER_WO_ERROR(file, FUNC, H5E_VFL, H5E_CANTCLOSEFILE, FAIL, "unable to close W/O file") + + if(file->logfp) { + HDfclose(file->logfp); + file->logfp = NULL; + } + + /* Release the file info */ + file = H5FL_FREE(H5FD_splitter_t, file); + file = NULL; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_close() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_get_eoa + * + * Purpose: Returns the end-of-address marker for the file. The EOA + * marker is the first address past the last byte allocated in + * the format address space. + * + * Return: Success: The end-of-address-marker + * + * Failure: HADDR_UNDEF + *------------------------------------------------------------------------- + */ +static haddr_t +H5FD__splitter_get_eoa(const H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type) +{ + const H5FD_splitter_t *file = (const H5FD_splitter_t *)_file; + haddr_t ret_value = HADDR_UNDEF; + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Sanity check */ + HDassert(file); + HDassert(file->rw_file); + + if((ret_value = H5FD_get_eoa(file->rw_file, type)) == HADDR_UNDEF) + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, HADDR_UNDEF, "unable to get eoa") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_get_eoa */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_set_eoa + * + * Purpose: Set the end-of-address marker for the file. This function is + * called shortly after an existing HDF5 file is opened in order + * to tell the driver where the end of the HDF5 data is located. + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_set_eoa(H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type, haddr_t addr) +{ + H5FD_splitter_t *file = (H5FD_splitter_t *)_file; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC) + + /* Sanity check */ + HDassert(file); + HDassert(file->rw_file); + HDassert(file->wo_file); + + if(H5FD_set_eoa(file->rw_file, type, addr) < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "H5FDset_eoa failed for R/W file") + + if(H5FD_set_eoa(file->wo_file, type, addr) < 0) + H5FD_SPLITTER_WO_ERROR(file, FUNC, H5E_VFL, H5E_CANTSET, FAIL, "unable to set EOA for W/O file") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_set_eoa() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_get_eof + * + * Purpose: Returns the end-of-address marker for the file. The EOA + * marker is the first address past the last byte allocated in + * the format address space. + * + * Return: Success: The end-of-address-marker + * + * Failure: HADDR_UNDEF + *------------------------------------------------------------------------- + */ +static haddr_t +H5FD__splitter_get_eof(const H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type) +{ + const H5FD_splitter_t *file = (const H5FD_splitter_t *)_file; + haddr_t ret_value = HADDR_UNDEF; /* Return value */ + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Sanity check */ + HDassert(file); + HDassert(file->rw_file); + + if(HADDR_UNDEF == (ret_value = H5FD_get_eof(file->rw_file, type))) + HGOTO_ERROR(H5E_VFL, H5E_CANTGET, HADDR_UNDEF, "unable to get eof") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_get_eof */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_truncate + * + * Purpose: Notify driver to truncate the file back to the allocated size. + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_truncate(H5FD_t *_file, hid_t dxpl_id, hbool_t closing) +{ + H5FD_splitter_t *file = (H5FD_splitter_t *)_file; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + HDassert(file); + HDassert(file->rw_file); + HDassert(file->wo_file); + + if(H5FDtruncate(file->rw_file, dxpl_id, closing) < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTUPDATE, FAIL, "unable to truncate R/W file") + + if(H5FDtruncate(file->wo_file, dxpl_id, closing) < 0) + H5FD_SPLITTER_WO_ERROR(file, FUNC, H5E_VFL, H5E_CANTUPDATE, FAIL, "unable to truncate W/O file") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_truncate */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_sb_size + * + * Purpose: Obtains the number of bytes required to store the driver file + * access data in the HDF5 superblock. + * + * Return: Success: Number of bytes required. + * + * Failure: 0 if an error occurs or if the driver has no + * data to store in the superblock. + * + * NOTE: no public API for H5FD_sb_size, it needs to be added + *------------------------------------------------------------------------- + */ +static hsize_t +H5FD__splitter_sb_size(H5FD_t *_file) +{ + H5FD_splitter_t *file = (H5FD_splitter_t *)_file; + hsize_t ret_value = 0; + + FUNC_ENTER_STATIC_NOERR + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Sanity check */ + HDassert(file); + HDassert(file->rw_file); + + if(file->rw_file) + ret_value = H5FD_sb_size(file->rw_file); + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_sb_size */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_sb_encode + * + * Purpose: Encode driver-specific data into the output arguments. + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_sb_encode(H5FD_t *_file, char *name/*out*/, unsigned char *buf/*out*/) +{ + H5FD_splitter_t *file = (H5FD_splitter_t *)_file; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Sanity check */ + HDassert(file); + HDassert(file->rw_file); + + if(file->rw_file && H5FD_sb_encode(file->rw_file, name, buf) < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTENCODE, FAIL, "unable to encode the superblock in R/W file") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_sb_encode */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_sb_decode + * + * Purpose: Decodes the driver information block. + * + * Return: SUCCEED/FAIL + * + * NOTE: no public API for H5FD_sb_size, need to add + *------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_sb_decode(H5FD_t *_file, const char *name, const unsigned char *buf) +{ + H5FD_splitter_t *file = (H5FD_splitter_t *)_file; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Sanity check */ + HDassert(file); + HDassert(file->rw_file); + + if(H5FD_sb_load(file->rw_file, name, buf) < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTDECODE, FAIL, "unable to decode the superblock in R/W file") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_sb_decode */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_cmp + * + * Purpose: Compare the keys of two files. + * + * Return: Success: A value like strcmp() + * Failure: Must never fail + *------------------------------------------------------------------------- + */ +static int +H5FD__splitter_cmp(const H5FD_t *_f1, const H5FD_t *_f2) +{ + const H5FD_splitter_t *f1 = (const H5FD_splitter_t *)_f1; + const H5FD_splitter_t *f2 = (const H5FD_splitter_t *)_f2; + herr_t ret_value = 0; /* Return value */ + + FUNC_ENTER_STATIC_NOERR + + H5FD_SPLITTER_LOG_CALL(FUNC); + + HDassert(f1); + HDassert(f2); + + ret_value = H5FD_cmp(f1->rw_file, f2->rw_file); + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_cmp */ + + +/*-------------------------------------------------------------------------- + * Function: H5FD__splitter_get_handle + * + * Purpose: Returns a pointer to the file handle of low-level virtual + * file driver. + * + * Return: SUCCEED/FAIL + *-------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_get_handle(H5FD_t *_file, hid_t H5_ATTR_UNUSED fapl, + void **file_handle) +{ + H5FD_splitter_t *file = (H5FD_splitter_t*)_file; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Check arguments */ + HDassert(file); + HDassert(file->rw_file); + HDassert(file_handle); + + /* Only do for R/W channel */ + if(H5FD_get_vfd_handle(file->rw_file, file->fa.rw_fapl_id, file_handle) < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "unable to get handle of R/W file") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_get_handle */ + + +/*-------------------------------------------------------------------------- + * Function: H5FD__splitter_lock + * + * Purpose: Sets a file lock. + * + * Return: SUCCEED/FAIL + *-------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_lock(H5FD_t *_file, hbool_t rw) +{ + H5FD_splitter_t *file = (H5FD_splitter_t *)_file; /* VFD file struct */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + HDassert(file); + HDassert(file->rw_file); + + /* Place the lock on each file */ + if(H5FD_lock(file->rw_file, rw) < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTLOCKFILE, FAIL, "unable to lock R/W file") + + if(file->wo_file != NULL) + if(H5FD_lock(file->wo_file, rw) < 0) + H5FD_SPLITTER_WO_ERROR(file, FUNC, H5E_VFL, H5E_CANTLOCKFILE, FAIL, "unable to lock W/O file") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_lock */ + + +/*-------------------------------------------------------------------------- + * Function: H5FD__splitter_unlock + * + * Purpose: Removes a file lock. + * + * Return: SUCCEED/FAIL + *-------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_unlock(H5FD_t *_file) +{ + H5FD_splitter_t *file = (H5FD_splitter_t *)_file; /* VFD file struct */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Check arguments */ + HDassert(file); + HDassert(file->rw_file); + + /* Remove the lock on each file */ + if(H5FD_unlock(file->rw_file) < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTUNLOCKFILE, FAIL, "unable to unlock R/W file") + + if(file->wo_file != NULL) + if(H5FD_unlock(file->wo_file) < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTUNLOCKFILE, FAIL, "unable to unlock W/O file") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_unlock */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_query + * + * Purpose: Set the flags that this VFL driver is capable of supporting. + * (listed in H5FDpublic.h) + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_query(const H5FD_t *_file, unsigned long *flags /* out */) +{ + const H5FD_splitter_t *file = (const H5FD_splitter_t *)_file; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + if(file) { + HDassert(file); + HDassert(file->rw_file); + + if(H5FDquery(file->rw_file, flags) < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTLOCK, FAIL, "unable to query R/W file"); + } + else { + /* There is no file. Because this is a pure passthrough VFD, + * it has no features of its own. + */ + if(flags) + *flags = 0; + } + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_query() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_alloc + * + * Purpose: Allocate file memory. + * + * Return: Address of allocated space (HADDR_UNDEF if error). + *------------------------------------------------------------------------- + */ +static haddr_t +H5FD__splitter_alloc(H5FD_t *_file, H5FD_mem_t type, hid_t dxpl_id, hsize_t size) +{ + H5FD_splitter_t *file = (H5FD_splitter_t *)_file; /* VFD file struct */ + haddr_t ret_value = HADDR_UNDEF; /* Return value */ + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Check arguments */ + HDassert(file); + HDassert(file->rw_file); + + /* Allocate memory for each file, only return the return value for R/W file. */ + if((ret_value = H5FDalloc(file->rw_file, type, dxpl_id, size)) == HADDR_UNDEF) + HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, HADDR_UNDEF, "unable to allocate for R/W file") + + if(H5FDalloc(file->wo_file, type, dxpl_id, size) == HADDR_UNDEF) + H5FD_SPLITTER_WO_ERROR(file, FUNC, H5E_VFL, H5E_CANTINIT, HADDR_UNDEF, "unable to alloc for W/O file") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_alloc() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_get_type_map + * + * Purpose: Retrieve the memory type mapping for this file + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_get_type_map(const H5FD_t *_file, H5FD_mem_t *type_map) +{ + const H5FD_splitter_t *file = (const H5FD_splitter_t *)_file; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Check arguments */ + HDassert(file); + HDassert(file->rw_file); + + /* Retrieve memory type mapping for R/W channel only */ + if(H5FD_get_fs_type_map(file->rw_file, type_map) < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "unable to allocate for R/W file") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_get_type_map() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_free + * + * Purpose: Free the resources for the splitter VFD. + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_free(H5FD_t *_file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, hsize_t size) +{ + H5FD_splitter_t *file = (H5FD_splitter_t *)_file; /* VFD file struct */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Check arguments */ + HDassert(file); + HDassert(file->rw_file); + + if(H5FDfree(file->rw_file, type, dxpl_id, addr, size) < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTFREE, FAIL, "unable to free for R/W file") + + if(H5FDfree(file->wo_file, type, dxpl_id, addr, size) < 0) + H5FD_SPLITTER_WO_ERROR(file, FUNC, H5E_VFL, H5E_CANTINIT, FAIL, "unable to free for W/O file") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_free() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD__splitter_log_error + * + * Purpose: Log an error from the W/O channel appropriately. + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_log_error(const H5FD_splitter_t *file, const char *atfunc, const char *msg) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC_NOERR + + H5FD_SPLITTER_LOG_CALL(FUNC); + + /* Check arguments */ + HDassert(file); + HDassert(atfunc && *atfunc); + HDassert(msg && *msg); + + if(file->logfp != NULL) { + size_t size; + char *s; + + size = HDstrlen(atfunc) + HDstrlen(msg) + 3; /* ':', ' ', '\n' */ + s = (char *)HDmalloc(sizeof(char) * (size + 1)); + if(NULL == s) + ret_value = FAIL; + else if(size < (size_t)HDsnprintf(s, size+1, "%s: %s\n", atfunc, msg)) + ret_value = FAIL; + else if(size != HDfwrite(s, 1, size, file->logfp)) + ret_value = FAIL; + HDfree(s); + } + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_log_error() */ + diff --git a/src/H5FDsplitter.h b/src/H5FDsplitter.h new file mode 100644 index 0000000..5a5ef29 --- /dev/null +++ b/src/H5FDsplitter.h @@ -0,0 +1,99 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the COPYING file, which can be found at the root of the source code * + * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. * + * If you do not have access to either file, you may request a copy from * + * help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Purpose: The public header file for the "splitter" driver. + */ + +#ifndef H5FDsplitter_H +#define H5FDsplitter_H + +#define H5FD_SPLITTER (H5FD_splitter_init()) + +/* The version of the H5FD_splitter_vfd_config_t structure used */ +#define H5FD_CURR_SPLITTER_VFD_CONFIG_VERSION 1 + +/* Maximum length of a filename/path string in the Write-Only channel, + * including the NULL-terminator. + */ +#define H5FD_SPLITTER_PATH_MAX 4096 + +/* Semi-unique constant used to help identify structure pointers */ +#define H5FD_SPLITTER_MAGIC 0x2B916880 + +/* ---------------------------------------------------------------------------- + * Structure: H5FD_spliiter_vfd_config_t + * + * One-stop shopping for configuring a Splitter VFD (rather than many + * paramaters passed into H5Pset/get functions). + * + * magic (int32_t) + * Semi-unique number, used to sanity-check that a given pointer is + * likely (or not) to be this structure type. MUST be first. + * If magic is not H5FD_SPLITTER_MAGIC, the structure (and/or pointer to) + * must be considered invalid. + * + * version (unsigned int) + * Version number of this structure -- informs component membership. + * If not H5FD_CURR_SPLITTER_VFD_CONFIG_VERSION, the structure (and/or + * pointer to) must be considered invalid. + * + * rw_fapl_id (hid_t) + * Library-given identification number of the Read/Write channel driver + * File Access Property List. + * The driver must support read/write access. + * Must be set to H5P_DEFAULT or a valid FAPL ID. + * + * wo_fapl_id (hid_t) + * Library-given identification number of the Read/Write channel driver + * File Access Property List. + * The driver feature flags must include H5FD_FEAT_DEFAULT_VFD_COMPAITBLE. + * Must be set to H5P_DEFAULT or a valid FAPL ID. + * + * wo_file_path (char[H5FD_SPLITTER_PATH_MAX + 1]) + * String buffer for the Write-Only channel target file. + * Must be null-terminated, cannot be empty. + * + * log_file_path (char[H5FD_SPLITTER_PATH_MAX + 1]) + * String buffer for the Splitter VFD logging output. + * Must be null-terminated. + * If null, no logfile is created. + * + * ignore_wo_errors (hbool_t) + * Toggle flag for how judiciously to respond to errors on the Write-Only + * channel. + * + * ---------------------------------------------------------------------------- + */ +typedef struct H5FD_splitter_vfd_config_t { + int32_t magic; + unsigned int version; + hid_t rw_fapl_id; + hid_t wo_fapl_id; + char wo_path[H5FD_SPLITTER_PATH_MAX + 1]; + char log_file_path[H5FD_SPLITTER_PATH_MAX + 1]; + hbool_t ignore_wo_errs; +} H5FD_splitter_vfd_config_t; + +#ifdef __cplusplus +extern "C" { +#endif +H5_DLL hid_t H5FD_splitter_init(void); +H5_DLL herr_t H5Pset_fapl_splitter(hid_t fapl_id, H5FD_splitter_vfd_config_t *config_ptr); +H5_DLL herr_t H5Pget_fapl_splitter(hid_t fapl_id, H5FD_splitter_vfd_config_t *config_ptr); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/Makefile.am b/src/Makefile.am index 0ec6c41..a077a19 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -63,7 +63,8 @@ libhdf5_la_SOURCES= H5.c H5checksum.c H5dbg.c H5system.c H5timer.c H5trace.c \ H5FA.c H5FAcache.c H5FAdbg.c H5FAdblock.c H5FAdblkpage.c H5FAhdr.c \ H5FAint.c H5FAstat.c H5FAtest.c \ H5FD.c H5FDcore.c H5FDfamily.c H5FDhdfs.c H5FDint.c H5FDlog.c \ - H5FDmulti.c H5FDsec2.c H5FDspace.c H5FDstdio.c H5FDtest.c \ + H5FDmulti.c H5FDsec2.c H5FDspace.c \ + H5FDsplitter.c H5FDstdio.c H5FDtest.c \ H5FL.c H5FO.c H5FS.c H5FScache.c H5FSdbg.c H5FSint.c H5FSsection.c \ H5FSstat.c H5FStest.c \ H5G.c H5Gbtree2.c H5Gcache.c \ @@ -138,7 +139,7 @@ include_HEADERS = hdf5.h H5api_adpt.h H5overflow.h H5pubconf.h H5public.h H5vers H5Epubgen.h H5Epublic.h H5Fpublic.h \ H5FDpublic.h H5FDcore.h H5FDdirect.h H5FDfamily.h H5FDhdfs.h \ H5FDlog.h H5FDmpi.h H5FDmpio.h H5FDmulti.h H5FDros3.h \ - H5FDsec2.h H5FDstdio.h H5FDwindows.h \ + H5FDsec2.h H5FDsplitter.h H5FDstdio.h H5FDwindows.h \ H5Gpublic.h H5Ipublic.h H5Lpublic.h \ H5MMpublic.h H5Opublic.h H5Ppublic.h \ H5PLextern.h H5PLpublic.h \ diff --git a/src/hdf5.h b/src/hdf5.h index f5ded15..9a0ea40 100644 --- a/src/hdf5.h +++ b/src/hdf5.h @@ -48,6 +48,7 @@ #include "H5FDmulti.h" /* Usage-partitioned file family */ #include "H5FDros3.h" /* R/O S3 "file" I/O */ #include "H5FDsec2.h" /* POSIX unbuffered file I/O */ +#include "H5FDsplitter.h" /* Twin-channel (R/W & R/O) I/O passthrough */ #include "H5FDstdio.h" /* Standard C buffered I/O */ #ifdef H5_HAVE_WINDOWS #include "H5FDwindows.h" /* Win32 I/O */ diff --git a/test/CMakeTests.cmake b/test/CMakeTests.cmake index 800bfb6..949b926 100644 --- a/test/CMakeTests.cmake +++ b/test/CMakeTests.cmake @@ -446,6 +446,8 @@ set (test_CLEANFILES vds_swmr_src_*.h5 tmp_vds_env/vds_src_2.h5 direct_chunk.h5 + splitter*.h5 + splitter.log ) # Remove any output file left over from previous test run diff --git a/test/Makefile.am b/test/Makefile.am index f60f55c..1c759dc 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -201,7 +201,8 @@ CHECK_CLEANFILES+=accum.h5 cmpd_dset.h5 compact_dataset.h5 dataset.h5 dset_offse flushrefresh_VERIFICATION_DONE atomic_data accum_swmr_big.h5 ohdr_swmr.h5 \ test_swmr*.h5 cache_logging.h5 cache_logging.out vds_swmr.h5 vds_swmr_src_*.h5 \ swmr[0-2].h5 swmr_writer.out swmr_writer.log.* swmr_reader.out.* swmr_reader.log.* \ - tbogus.h5.copy cache_image_test.h5 direct_chunk.h5 + tbogus.h5.copy cache_image_test.h5 direct_chunk.h5 \ + splitter*.h5 splitter.log # Sources for testhdf5 executable testhdf5_SOURCES=testhdf5.c tarray.c tattr.c tchecksum.c tconfig.c tfile.c \ diff --git a/test/h5test.c b/test/h5test.c index 5e6f01a..6307078 100644 --- a/test/h5test.c +++ b/test/h5test.c @@ -1889,6 +1889,84 @@ h5_get_version_string(H5F_libver_t libver) } /* end of h5_get_version_string */ /*------------------------------------------------------------------------- + * Function: h5_compare_file_bytes() + * + * Purpose: Helper function to compare two files byte-for-byte. + * + * Return: Success: 0, if files are identical + * Failure: -1, if files differ + * + * Programmer: Binh-Minh Ribler + * October, 2018 + *------------------------------------------------------------------------- + */ +int +h5_compare_file_bytes(char *f1name, char *f2name) +{ + FILE *f1ptr = NULL; /* two file pointers */ + FILE *f2ptr = NULL; + off_t f1size = 0; /* size of the files */ + off_t f2size = 0; + char f1char = 0; /* one char from each file */ + char f2char = 0; + off_t ii = 0; + int ret_value = 0; /* for error handling */ + + /* Open files for reading */ + f1ptr = HDfopen(f1name, "rb"); + if (f1ptr == NULL) { + HDfprintf(stderr, "Unable to fopen() %s\n", f1name); + ret_value = -1; + goto done; + } + f2ptr = HDfopen(f2name, "rb"); + if (f2ptr == NULL) { + HDfprintf(stderr, "Unable to fopen() %s\n", f2name); + ret_value = -1; + goto done; + } + + /* Get the file sizes and verify that they equal */ + HDfseek(f1ptr , 0 , SEEK_END); + f1size = HDftell(f1ptr); + + HDfseek(f2ptr , 0 , SEEK_END); + f2size = HDftell(f2ptr); + + if (f1size != f2size) { + HDfprintf(stderr, "Files differ in size, %llu vs. %llu\n", f1size, f2size); + ret_value = -1; + goto done; + } + + /* Compare each byte and fail if a difference is found */ + HDrewind(f1ptr); + HDrewind(f2ptr); + for (ii = 0; ii < f1size; ii++) { + if(HDfread(&f1char, 1, 1, f1ptr) != 1) { + ret_value = -1; + goto done; + } + if(HDfread(&f2char, 1, 1, f2ptr) != 1) { + ret_value = -1; + goto done; + } + if (f1char != f2char) { + HDfprintf(stderr, "Mismatch @ 0x%llX: 0x%X != 0x%X\n", ii, f1char, f2char); + ret_value = -1; + goto done; + } + } + +done: + if (f1ptr) + HDfclose(f1ptr); + if (f2ptr) + HDfclose(f2ptr); + return ret_value; +} /* end h5_compare_file_bytes() */ + +/*------------------------------------------------------------------------- * Function: H5_get_srcdir_filename * * Purpose: Append the test file name to the srcdir path and return the whole string @@ -1942,6 +2020,76 @@ const char *H5_get_srcdir(void) } /* end H5_get_srcdir() */ /*------------------------------------------------------------------------- + * Function: h5_duplicate_file_by_bytes + * + * Purpose: Duplicate a file byte-for-byte at filename/path 'orig' + * to a new (or replaced) file at 'dest'. + * + * Return: Success: 0, completed successfully + * Failure: -1 + * + * Programmer: Jake Smith + * 24 June 2020 + * + *------------------------------------------------------------------------- + */ +int +h5_duplicate_file_by_bytes(const char *orig, const char *dest) +{ + FILE *orig_ptr = NULL; + FILE *dest_ptr = NULL; + hsize_t fsize = 0; + hsize_t read_size = 0; + hsize_t max_buf = 0; + void *dup_buf = NULL; + int ret_value = 0; + + max_buf = 4096 * sizeof(char); + + orig_ptr = HDfopen(orig, "rb"); + if (NULL == orig_ptr) { + ret_value = -1; + goto done; + } + + HDfseek(orig_ptr , 0 , SEEK_END); + fsize = (hsize_t)HDftell(orig_ptr); + HDrewind(orig_ptr); + + dest_ptr = HDfopen(dest, "wb"); + if (NULL == dest_ptr) { + ret_value = -1; + goto done; + } + + read_size = MIN(fsize, max_buf); + dup_buf = HDmalloc(read_size); + if (NULL == dup_buf) { + ret_value = -1; + goto done; + } + + while (read_size > 0) { + if (HDfread(dup_buf, read_size, 1, orig_ptr) != 1) { + ret_value = -1; + goto done; + } + HDfwrite(dup_buf, read_size, 1, dest_ptr); + fsize -= read_size; + read_size = MIN(fsize, max_buf); + } + +done: + if (orig_ptr != NULL) + HDfclose(orig_ptr); + if (dest_ptr != NULL) + HDfclose(dest_ptr); + if (dup_buf != NULL) + HDfree(dup_buf); + return ret_value; +} /* end h5_duplicate_file_by_bytes() */ + +/*------------------------------------------------------------------------- * Function: h5_check_if_file_locking_enabled * * Purpose: Checks if file locking is enabled on this file system. diff --git a/test/h5test.h b/test/h5test.h index 35eea98..d328466 100644 --- a/test/h5test.h +++ b/test/h5test.h @@ -206,6 +206,8 @@ H5TEST_DLL int h5_make_local_copy(const char *origfilename, const char *local_co H5TEST_DLL herr_t h5_verify_cached_stabs(const char *base_name[], hid_t fapl); H5TEST_DLL H5FD_class_t *h5_get_dummy_vfd_class(void); H5TEST_DLL const char *h5_get_version_string(H5F_libver_t libver); +H5TEST_DLL int h5_compare_file_bytes(char *fname1, char *fname2); +H5TEST_DLL int h5_duplicate_file_by_bytes(const char *orig, const char *dest); H5TEST_DLL herr_t h5_check_if_file_locking_enabled(hbool_t *are_enabled); /* Functions that will replace components of a FAPL */ diff --git a/test/vfd.c b/test/vfd.c index e88ad57..a783cb1 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 * @@ -2062,6 +2105,1104 @@ 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 = NULL; + + if (NULL == (fetched_info = HDcalloc(1, sizeof(H5FD_splitter_vfd_config_t)))) + SPLITTER_TEST_FAULT("memory allocation for fetched_info struct failed"); + + 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"); + } + 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: + HDfree(fetched_info); + + 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 = NULL; + char *filename_rw = NULL; + FILE *logfile = NULL; + int ret_value = 0; + + if (NULL == (vfd_config = HDcalloc(1, sizeof(H5FD_splitter_vfd_config_t)))) + SPLITTER_TEST_FAULT("memory allocation for vfd_config struct failed"); + if (NULL == (filename_rw = HDcalloc(H5FD_SPLITTER_PATH_MAX + 1, sizeof(char)))) + SPLITTER_TEST_FAULT("memory allocation for filename_rw string failed"); + + 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] = '\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 if 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 { + H5Dclose(dset_id); + H5Sclose(space_id); + H5Pclose(fapl_id_out); + H5Pclose(fapl_id_cpy); + H5Pclose(fapl_id); + H5Fclose(file_id); + } H5E_END_TRY; + } + + if (logfile != NULL) + fclose(logfile); + + HDfree(vfd_config); + HDfree(filename_rw); + + 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 = NULL; + hid_t split_fapl_id = H5I_INVALID_HID; + herr_t ret = SUCCEED; + int ret_value = 0; + + if (NULL == (vfd_config = HDcalloc(1, sizeof(H5FD_splitter_vfd_config_t)))) { + FAIL_PUTS_ERROR("memory allocation for vfd_config struct failed"); + } + + if(H5I_INVALID_HID == (split_fapl_id = H5Pcreate(H5P_FILE_ACCESS))) { + 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); + vfd_config->log_file_path[0] = '\0'; + + 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; + + HDfree(vfd_config); + + return ret_value; + +error: + H5E_BEGIN_TRY { + H5Pclose(split_fapl_id); + } H5E_END_TRY; + + HDfree(vfd_config); + + 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 = NULL; + H5FD_splitter_vfd_config_t *vfd_config = NULL; + hid_t fapl_id = H5I_INVALID_HID; + hid_t file_id = H5I_INVALID_HID; + int ret_value = 0; + + if (NULL == (vfd_config = HDcalloc(1, sizeof(H5FD_splitter_vfd_config_t)))) + SPLITTER_TEST_FAULT("memory allocation for vfd_config struct failed"); + if (NULL == (filename_rw = HDcalloc(H5FD_SPLITTER_PATH_MAX + 1, sizeof(char)))) + SPLITTER_TEST_FAULT("memory allocation for filename_rw string failed"); + + 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 { + H5Pclose(fapl_id); + H5Fclose(file_id); + } H5E_END_TRY; + } + + HDfree(vfd_config); + HDfree(filename_rw); + + 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 { + H5Dclose(dset_id); + H5Sclose(space_id); + 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 { + 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) +{ + const char *filename_tmp = "splitter_tmp.h5"; + char *filename_rw = NULL; + H5FD_splitter_vfd_config_t *vfd_config = NULL; + 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; + + if (NULL == (vfd_config = HDcalloc(1, sizeof(H5FD_splitter_vfd_config_t)))) + SPLITTER_TEST_FAULT("memory allocation for vfd_config struct failed"); + if (NULL == (filename_rw = HDcalloc(H5FD_SPLITTER_PATH_MAX + 1, sizeof(char)))) + SPLITTER_TEST_FAULT("memory allocation for filename_rw string failed"); + + /* 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"); + } + + /* Create instance of file on disk. + * Will be copied verbatim as needed, to avoid issues where differences in + * the creation time would befoul comparisons. + */ + if (splitter_create_single_file_at(filename_tmp, child_fapl_id, &data) < 0) { + SPLITTER_TEST_FAULT("can't write W/O file\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. + * Only W/O file present. + * Should fail. + */ + + if (h5_duplicate_file_by_bytes(filename_tmp, vfd_config->wo_path) < 0) { + SPLITTER_TEST_FAULT("Can't create W/O file copy.\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. + * Only R/W file present. + * Should fail. + */ + + if (h5_duplicate_file_by_bytes(filename_tmp, filename_rw) < 0) { + SPLITTER_TEST_FAULT("Can't create R/W file copy.\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 present. + */ + + if (h5_duplicate_file_by_bytes(filename_tmp, vfd_config->wo_path) < 0) { + SPLITTER_TEST_FAULT("Can't create W/O file copy.\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"); + } + + /* + * H5Fcreate() with TRUNC access. + * Both files present. + */ + + 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"); + } + HDremove(filename_rw); + HDremove(vfd_config->wo_path); + + /* + * H5Fcreate() with TRUNC access. + * R/W already exists. + */ + + if (h5_duplicate_file_by_bytes(filename_tmp, filename_rw) < 0) { + SPLITTER_TEST_FAULT("Can't create R/W file copy.\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"); + } + HDremove(filename_rw); + HDremove(vfd_config->wo_path); + + /* + * H5Fcreate() with TRUNC access. + * Only W/O present. + */ + + if (h5_duplicate_file_by_bytes(filename_tmp, vfd_config->wo_path) < 0) { + SPLITTER_TEST_FAULT("Can't create W/O file copy.\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"); + } + HDremove(filename_rw); + HDremove(vfd_config->wo_path); + + /* 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 { + H5Pclose(fapl_id); + H5Fclose(file_id); + } H5E_END_TRY; + } + + HDfree(vfd_config); + HDfree(filename_rw); + + 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 { + 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) + H5Pclose(child_fapl_id); + + return -1; +} /* end test_splitter() */ + +#undef SPLITTER_TEST_FAULT + + /*------------------------------------------------------------------------- * Function: main * @@ -2091,6 +3232,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", -- cgit v0.12