From b65405439d88be6c09fe94360e7fea9856697926 Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Fri, 13 Mar 2020 17:13:17 -0500 Subject: Add Splitter VFD to library. * "Simultaneous and equivalent" Read-Write and Write-Only channels for file I/O. * Only supports drivers with the H5FD_FEAT_DEFAULT_VFD_COMPATIBLE flag for now, preventing issues with multi-file drivers. Add Mirror VFD to library. * Write-only operations over a network. * Uses TCP/IP sockets. * Server and auxiliary server-shutdown programs provided in a new directory, `utils/mirror_vfd`. * Automated testing via loopback ("remote" of localhost). --- CMakeInstallation.cmake | 7 + CMakeLists.txt | 15 + MANIFEST | 21 + Makefile.am | 6 +- bin/trace | 2 + config/cmake/ConfigureChecks.cmake | 11 + config/cmake/H5pubconf.h.in | 12 + config/cmake/libhdf5.settings.cmake.in | 1 + config/cmake_ext_mod/ConfigureChecks.cmake | 3 + configure.ac | 34 + release_docs/RELEASE.txt | 14 + src/CMakeLists.txt | 4 + src/H5FDmirror.c | 1991 +++++++++++++++++++ src/H5FDmirror.h | 371 ++++ src/H5FDpublic.h | 2 + src/H5FDsec2.c | 6 + src/H5FDsplitter.c | 1467 ++++++++++++++ src/H5FDsplitter.h | 99 + src/H5private.h | 66 +- src/Makefile.am | 9 +- src/hdf5.h | 2 + src/libhdf5.settings.in | 1 + test/CMakeLists.txt | 19 + test/CMakeTests.cmake | 4 + test/Makefile.am | 19 +- test/h5test.c | 75 + test/h5test.h | 1 + test/mirror_vfd.c | 2736 +++++++++++++++++++++++++++ test/test_mirror.sh.in | 100 + test/use.h | 52 +- test/use_append_chunk.c | 237 +-- test/use_append_chunk_mirror.c | 403 ++++ test/use_append_mchunks.c | 180 +- test/use_common.c | 645 +++---- test/vfd.c | 1097 ++++++++++- utils/CMakeLists.txt | 4 + utils/COPYING | 12 + utils/Makefile.am | 26 + utils/mirror_vfd/CMakeLists.txt | 64 + utils/mirror_vfd/Makefile.am | 30 + utils/mirror_vfd/mirror_remote.c | 225 +++ utils/mirror_vfd/mirror_remote.h | 51 + utils/mirror_vfd/mirror_server.c | 645 +++++++ utils/mirror_vfd/mirror_server_halten_sie.c | 214 +++ utils/mirror_vfd/mirror_writer.c | 1064 +++++++++++ 45 files changed, 11482 insertions(+), 565 deletions(-) create mode 100644 src/H5FDmirror.c create mode 100644 src/H5FDmirror.h create mode 100644 src/H5FDsplitter.c create mode 100644 src/H5FDsplitter.h create mode 100644 test/mirror_vfd.c create mode 100644 test/test_mirror.sh.in create mode 100644 test/use_append_chunk_mirror.c create mode 100644 utils/CMakeLists.txt create mode 100644 utils/COPYING create mode 100644 utils/Makefile.am create mode 100644 utils/mirror_vfd/CMakeLists.txt create mode 100644 utils/mirror_vfd/Makefile.am create mode 100644 utils/mirror_vfd/mirror_remote.c create mode 100644 utils/mirror_vfd/mirror_remote.h create mode 100644 utils/mirror_vfd/mirror_server.c create mode 100644 utils/mirror_vfd/mirror_server_halten_sie.c create mode 100644 utils/mirror_vfd/mirror_writer.c diff --git a/CMakeInstallation.cmake b/CMakeInstallation.cmake index 12d8273..94d3139 100644 --- a/CMakeInstallation.cmake +++ b/CMakeInstallation.cmake @@ -554,6 +554,13 @@ The HDF5 data model, file format, API, library, and tools are open and distribut ) endif () + cpack_add_component (utilsapplications + DISPLAY_NAME "HDF5 Utility Applications" + DEPENDS libraries + GROUP Applications + INSTALL_TYPES Full Developer User + ) + if (HDF5_BUILD_TOOLS) cpack_add_component (toolsapplications DISPLAY_NAME "HDF5 Tools Applications" diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bad805..d2eea72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,7 @@ set (HDF5_CPP_LIB_CORENAME "hdf5_cpp") set (HDF5_HL_LIB_CORENAME "hdf5_hl") set (HDF5_HL_CPP_LIB_CORENAME "hdf5_hl_cpp") set (HDF5_TOOLS_LIB_CORENAME "hdf5_tools") +set (HDF5_UTILS_LIB_CORENAME "hdf5_utils") set (HDF5_F90_LIB_CORENAME "hdf5_fortran") set (HDF5_F90_C_LIB_CORENAME "hdf5_f90cstub") set (HDF5_F90_TEST_LIB_CORENAME "hdf5_test_fortran") @@ -156,6 +157,7 @@ set (HDF5_CPP_LIB_NAME "${HDF5_EXTERNAL_LIB_PREFIX}${HDF5_CPP_LIB_COREN set (HDF5_HL_LIB_NAME "${HDF5_EXTERNAL_LIB_PREFIX}${HDF5_HL_LIB_CORENAME}") set (HDF5_HL_CPP_LIB_NAME "${HDF5_EXTERNAL_LIB_PREFIX}${HDF5_HL_CPP_LIB_CORENAME}") set (HDF5_TOOLS_LIB_NAME "${HDF5_EXTERNAL_LIB_PREFIX}${HDF5_TOOLS_LIB_CORENAME}") +set (HDF5_UTILS_LIB_NAME "${HDF5_EXTERNAL_LIB_PREFIX}${HDF5_UTILS_LIB_CORENAME}") set (HDF5_F90_LIB_NAME "${HDF5_EXTERNAL_LIB_PREFIX}${HDF5_F90_LIB_CORENAME}") set (HDF5_F90_C_LIB_NAME "${HDF5_EXTERNAL_LIB_PREFIX}${HDF5_F90_C_LIB_CORENAME}") set (HDF5_F90_TEST_LIB_NAME "${HDF5_EXTERNAL_LIB_PREFIX}${HDF5_F90_TEST_LIB_CORENAME}") @@ -175,6 +177,7 @@ set (HDF5_CPP_LIB_TARGET "${HDF5_CPP_LIB_CORENAME}-static") set (HDF5_HL_LIB_TARGET "${HDF5_HL_LIB_CORENAME}-static") set (HDF5_HL_CPP_LIB_TARGET "${HDF5_HL_CPP_LIB_CORENAME}-static") set (HDF5_TOOLS_LIB_TARGET "${HDF5_TOOLS_LIB_CORENAME}-static") +set (HDF5_UTILS_LIB_TARGET "${HDF5_UTILS_LIB_CORENAME}-static") set (HDF5_F90_LIB_TARGET "${HDF5_F90_LIB_CORENAME}-static") set (HDF5_F90_C_LIB_TARGET "${HDF5_F90_C_LIB_CORENAME}-static") set (HDF5_F90_TEST_LIB_TARGET "${HDF5_F90_TEST_LIB_CORENAME}-static") @@ -190,6 +193,7 @@ set (HDF5_CPP_LIBSH_TARGET "${HDF5_CPP_LIB_CORENAME}-shared") set (HDF5_HL_LIBSH_TARGET "${HDF5_HL_LIB_CORENAME}-shared") set (HDF5_HL_CPP_LIBSH_TARGET "${HDF5_HL_CPP_LIB_CORENAME}-shared") set (HDF5_TOOLS_LIBSH_TARGET "${HDF5_TOOLS_LIB_CORENAME}-shared") +set (HDF5_UTILS_LIBSH_TARGET "${HDF5_UTILS_LIB_CORENAME}-shared") set (HDF5_F90_LIBSH_TARGET "${HDF5_F90_LIB_CORENAME}-shared") set (HDF5_F90_C_LIBSH_TARGET "${HDF5_F90_C_LIB_CORENAME}-shared") set (HDF5_F90_TEST_LIBSH_TARGET "${HDF5_F90_TEST_LIB_CORENAME}-shared") @@ -212,6 +216,7 @@ set (HDF5_HL_TOOLS_DIR ${HDF5_SOURCE_DIR}/hl/tools) set (HDF5_TOOLS_DIR ${HDF5_SOURCE_DIR}/tools) set (HDF5_TOOLS_SRC_DIR ${HDF5_SOURCE_DIR}/tools/src) set (HDF5_PERFORM_SRC_DIR ${HDF5_SOURCE_DIR}/tools/src/perform) +set (HDF5_UTILS_DIR ${HDF5_SOURCE_DIR}/utils) set (HDF5_F90_SRC_DIR ${HDF5_SOURCE_DIR}/fortran) set (HDF5_JAVA_JNI_SRC_DIR ${HDF5_SOURCE_DIR}/java/src/jni) set (HDF5_JAVA_HDF5_SRC_DIR ${HDF5_SOURCE_DIR}/java/src/hdf) @@ -920,6 +925,16 @@ if (BUILD_TESTING) endif () #----------------------------------------------------------------------------- +# Option to build HDF5 Utilities +#----------------------------------------------------------------------------- +if (EXISTS "${HDF5_SOURCE_DIR}/utils" AND IS_DIRECTORY "${HDF5_SOURCE_DIR}/utils") + option (HDF5_BUILD_UTILS "Build HDF5 Utils" ON) + if (HDF5_BUILD_UTILS) + add_subdirectory (utils) + endif () +endif () + +#----------------------------------------------------------------------------- # Option to build HDF5 Tools #----------------------------------------------------------------------------- if (EXISTS "${HDF5_SOURCE_DIR}/tools" AND IS_DIRECTORY "${HDF5_SOURCE_DIR}/tools") diff --git a/MANIFEST b/MANIFEST index 33fab2f..01d2df4 100644 --- a/MANIFEST +++ b/MANIFEST @@ -654,6 +654,8 @@ ./src/H5FDint.c ./src/H5FDlog.c ./src/H5FDlog.h +./src/H5FDmirror.c +./src/H5FDmirror.h ./src/H5FDmodule.h ./src/H5FDmpi.c ./src/H5FDmpi.h @@ -671,6 +673,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 @@ -1113,6 +1117,7 @@ ./test/memleak_H5O_dtype_decode_helper_H5Odtype.h5 ./test/mergemsg.h5 ./test/mf.c +./test/mirror_vfd.c ./test/mount.c ./test/mtime.c ./test/multi_file_v16-r.h5 @@ -1173,6 +1178,7 @@ ./test/testhdf5.c ./test/testhdf5.h ./test/testlibinfo.sh.in +./test/test_mirror.sh.in ./test/test_usecases.sh.in ./test/test_vol_plugin.sh.in ./test/testmeta.c @@ -1216,6 +1222,7 @@ ./test/unlink.c ./test/unregister.c ./test/use_append_chunk.c +./test/use_append_chunk_mirror.c ./test/use_append_mchunks.c ./test/use_common.c ./test/use_disable_mdc_flushes.c @@ -2772,6 +2779,18 @@ ./tools/test/perform/sio_standalone.h ./tools/test/perform/zip_perf.c +# Utils directory +./utils/COPYING +./utils/Makefile.am + +# Mirror VFD utilities +./utils/mirror_vfd/Makefile.am +./utils/mirror_vfd/mirror_remote.c +./utils/mirror_vfd/mirror_remote.h +./utils/mirror_vfd/mirror_server.c +./utils/mirror_vfd/mirror_server_halten_sie.c +./utils/mirror_vfd/mirror_writer.c + # high level libraries ./hl/COPYING ./hl/Makefile.am @@ -3507,6 +3526,8 @@ ./tools/test/misc/vds/CMakeLists.txt ./tools/test/perform/CMakeLists.txt ./tools/test/perform/CMakeTests.cmake +./utils/CMakeLists.txt +./utils/mirror_vfd/CMakeLists.txt # CMake-specific User Scripts ./config/cmake/CTestScript.cmake diff --git a/Makefile.am b/Makefile.am index d96ffe3..9689b58 100644 --- a/Makefile.am +++ b/Makefile.am @@ -85,9 +85,9 @@ else TOOLS_DIR= endif -SUBDIRS = src $(TESTSERIAL_DIR) $(TESTPARALLEL_DIR) bin $(TOOLS_DIR) . \ +SUBDIRS = src $(TESTSERIAL_DIR) $(TESTPARALLEL_DIR) bin utils $(TOOLS_DIR) . \ $(CXX_DIR) $(FORTRAN_DIR) $(JAVA_DIR) $(HDF5_HL_DIR) -DIST_SUBDIRS = src test testpar tools . c++ fortran hl examples java +DIST_SUBDIRS = src test testpar utils tools . c++ fortran hl examples java # Some files generated during configure that should be cleaned DISTCLEANFILES=config/stamp1 config/stamp2 @@ -190,7 +190,7 @@ trace: # Run tests with different Virtual File Drivers. # Currently, only invoke check-vfd in the test directory. check-vfd: - for d in src test; do \ + for d in src utils test; do \ if test $$d != .; then \ (cd $$d && $(MAKE) $(AM_MAKEFLAGS) $@) || exit 1; \ fi; \ diff --git a/bin/trace b/bin/trace index de6a83f..cfeb9d1 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", @@ -158,6 +159,7 @@ $Source = ""; "H5FD_ros3_fapl_t" => "x", "H5FD_hdfs_fapl_t" => "x", "H5FD_file_image_callbacks_t" => "x", + "H5FD_mirror_fapl_t" => "x", "H5G_iterate_t" => "x", "H5G_info_t" => "x", "H5I_free_t" => "x", diff --git a/config/cmake/ConfigureChecks.cmake b/config/cmake/ConfigureChecks.cmake index 11bf39c..1c3d5f9 100644 --- a/config/cmake/ConfigureChecks.cmake +++ b/config/cmake/ConfigureChecks.cmake @@ -171,6 +171,17 @@ option (HDF5_ENABLE_ROS3_VFD "Build the ROS3 Virtual File Driver" OFF) endif () endif () +# ---------------------------------------------------------------------- +# Check whether we can build the Mirror VFD +# Header-check flags set in config/cmake_ext_mod/ConfigureChecks.cmake +# ---------------------------------------------------------------------- +if ( ${HDF_PREFIX}_HAVE_NETINET_IN_H AND + ${HDF_PREFIX}_HAVE_NETDB_H AND + ${HDF_PREFIX}_HAVE_ARPA_INET_H AND + ${HDF_PREFIX}_HAVE_SYS_SOCKET_H) + set (${HDF_PREFIX}_HAVE_MIRROR_VFD 1) +endif() + #----------------------------------------------------------------------------- # Check if C has __float128 extension #----------------------------------------------------------------------------- diff --git a/config/cmake/H5pubconf.h.in b/config/cmake/H5pubconf.h.in index 0836168..6edf6b4 100644 --- a/config/cmake/H5pubconf.h.in +++ b/config/cmake/H5pubconf.h.in @@ -104,6 +104,9 @@ /* Define if the compiler understands the __func__ keyword */ #cmakedefine H5_HAVE_C99_FUNC @H5_HAVE_C99_FUNC@ +/* Define to 1 if you have the header file. */ +#cmakedefine H5_HAVE_ARPA_INET_H @H5_HAVE_ARPA_INET_H@ + /* Define to 1 if you have the `clock_gettime' function. */ #cmakedefine H5_HAVE_CLOCK_GETTIME @H5_HAVE_CLOCK_GETTIME@ @@ -273,6 +276,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine H5_HAVE_MEMORY_H @H5_HAVE_MEMORY_H@ +/* Define if we can build the Mirror VFD */ +#cmakedefine H5_HAVE_MIRROR_VFD @H5_HAVE_MIRROR_VFD@ + /* Define if we have MPE support */ #cmakedefine H5_HAVE_MPE @H5_HAVE_MPE@ @@ -285,6 +291,12 @@ /* Define if MPI_Info_c2f and MPI_Info_f2c exists */ #cmakedefine H5_HAVE_MPI_MULTI_LANG_Info @H5_HAVE_MPI_MULTI_LANG_Info@ +/* Define to 1 if you have the header file. */ +#cmakedefine H5_HAVE_NETDB_H @H5_HAVE_NETDB_H@ + +/* Define to 1 if you have the header file. */ +#cmakedefine H5_HAVE_NETINET_IN_H @H5_HAVE_NETINET_IN_H@ + /* Define to 1 if you have the header file. */ #cmakedefine H5_HAVE_OPENSSL_EVP_H @H5_HAVE_OPENSSL_EVP_H@ diff --git a/config/cmake/libhdf5.settings.cmake.in b/config/cmake/libhdf5.settings.cmake.in index 8397d68..190fce2 100644 --- a/config/cmake/libhdf5.settings.cmake.in +++ b/config/cmake/libhdf5.settings.cmake.in @@ -76,6 +76,7 @@ Parallel Filtered Dataset Writes: @PARALLEL_FILTERED_WRITES@ I/O filters (external): @EXTERNAL_FILTERS@ MPE: @H5_HAVE_LIBLMPE@ Direct VFD: @H5_HAVE_DIRECT@ + Mirror VFD: @H5_HAVE_MIRROR_VFD@ (Read-Only) S3 VFD: @H5_HAVE_ROS3_VFD@ (Read-Only) HDFS VFD: @H5_HAVE_LIBHDFS@ dmalloc: @H5_HAVE_LIBDMALLOC@ diff --git a/config/cmake_ext_mod/ConfigureChecks.cmake b/config/cmake_ext_mod/ConfigureChecks.cmake index 52cb7dd..7a033f4 100644 --- a/config/cmake_ext_mod/ConfigureChecks.cmake +++ b/config/cmake_ext_mod/ConfigureChecks.cmake @@ -165,6 +165,8 @@ CHECK_INCLUDE_FILE_CONCAT ("memory.h" ${HDF_PREFIX}_HAVE_MEMORY_H) CHECK_INCLUDE_FILE_CONCAT ("dlfcn.h" ${HDF_PREFIX}_HAVE_DLFCN_H) CHECK_INCLUDE_FILE_CONCAT ("inttypes.h" ${HDF_PREFIX}_HAVE_INTTYPES_H) CHECK_INCLUDE_FILE_CONCAT ("netinet/in.h" ${HDF_PREFIX}_HAVE_NETINET_IN_H) +CHECK_INCLUDE_FILE_CONCAT ("netdb.h" ${HDF_PREFIX}_HAVE_NETDB_H) +CHECK_INCLUDE_FILE_CONCAT ("arpa/inet.h" ${HDF_PREFIX}_HAVE_ARPA_INET_H) # _Bool type support CHECK_INCLUDE_FILE_CONCAT (stdbool.h ${HDF_PREFIX}_HAVE_STDBOOL_H) @@ -677,3 +679,4 @@ endif () # the cache value is set in it's config file) # set (${HDF_PREFIX}_CONVERT_DENORMAL_FLOAT 1) + diff --git a/configure.ac b/configure.ac index 0eada60..c48e89c 100644 --- a/configure.ac +++ b/configure.ac @@ -1086,6 +1086,7 @@ AC_CHECK_HEADERS([stddef.h setjmp.h features.h]) AC_CHECK_HEADERS([dirent.h]) AC_CHECK_HEADERS([stdint.h], [C9x=yes]) AC_CHECK_HEADERS([stdbool.h]) +AC_CHECK_HEADERS([netdb.h netinet/in.h arpa/inet.h]) ## Darwin AC_CHECK_HEADERS([mach/mach_time.h]) @@ -2838,6 +2839,36 @@ fi AM_CONDITIONAL([DIRECT_VFD_CONDITIONAL], [test "X$DIRECT_VFD" = "Xyes"]) ## ---------------------------------------------------------------------- +## Check whether the Mirror VFD can be built. +## Auto-enabled if the required libraries are present. +## +AC_SUBST([MIRROR_VFD]) + +## Default is no Mirror VFD +MIRROR_VFD=yes + +AC_CHECK_HEADERS([arpa/inet.h],, [unset MIRROR_VFD]) +AC_CHECK_HEADERS([netinet/in.h],, [unset MIRROR_VFD]) +AC_CHECK_HEADERS([netdb.h],, [unset MIRROR_VFD]) +AC_CHECK_HEADERS([sys/socket.h],, [unset MIRROR_VFD]) + +AC_MSG_CHECKING([if the Mirror virtual file driver (VFD) can be built]) +if test "X$MIRROR_VFD" = "Xyes"; then + AC_DEFINE([HAVE_MIRROR_VFD], [1], + [Define whether the Mirror virtual file driver (VFD) will be compiled]) + AC_MSG_RESULT([yes]) +else + AC_MSG_RESULT([no]) + MIRROR_VFD=no + AC_MSG_ERROR([The Mirror VFD was cannot be built. + Missing any of: arpa/inet.h, netinet/in.h, netdb.h, + sys/socket.h.]) +fi + +## Mirror VFD files built only if able. +AM_CONDITIONAL([MIRROR_VFD_CONDITIONAL], [test "X$MIRROR_VFD" = "Xyes"]) + +## ---------------------------------------------------------------------- ## Check if Read-Only S3 virtual file driver is enabled by --enable-ros3-vfd ## AC_SUBST([ROS3_VFD]) @@ -3716,10 +3747,13 @@ AC_CONFIG_FILES([src/libhdf5.settings test/testvds_env.sh test/testvdsswmr.sh test/test_filter_plugin.sh + test/test_mirror.sh test/test_usecases.sh test/test_vol_plugin.sh testpar/Makefile testpar/testpflush.sh + utils/Makefile + utils/mirror_vfd/Makefile tools/Makefile tools/lib/Makefile tools/libtest/Makefile diff --git a/release_docs/RELEASE.txt b/release_docs/RELEASE.txt index ff024c3..8931b0f 100644 --- a/release_docs/RELEASE.txt +++ b/release_docs/RELEASE.txt @@ -270,6 +270,20 @@ New Features Library: -------- + - Add Mirror VFD + + Use TCP/IP sockets to perform write-only (W/O) file I/O on a remote + machine. Must be used in conjunction with the Splitter VFD. + + (JOS - 2020/03/13, TBD) + + - 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) + - Refactored public exposure of haddr_t type in favor of "object tokens" To better accommodate HDF5 VOL connectors where "object addresses in a file" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f582056..6678aaf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -231,6 +231,7 @@ set (H5FD_SOURCES ${HDF5_SRC_DIR}/H5FDhdfs.c ${HDF5_SRC_DIR}/H5FDint.c ${HDF5_SRC_DIR}/H5FDlog.c + ${HDF5_SRC_DIR}/H5FDmirror.c ${HDF5_SRC_DIR}/H5FDmpi.c ${HDF5_SRC_DIR}/H5FDmpio.c ${HDF5_SRC_DIR}/H5FDmulti.c @@ -238,6 +239,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 @@ -249,6 +251,7 @@ set (H5FD_HDRS ${HDF5_SRC_DIR}/H5FDfamily.h ${HDF5_SRC_DIR}/H5FDhdfs.h ${HDF5_SRC_DIR}/H5FDlog.h + ${HDF5_SRC_DIR}/H5FDmirror.h ${HDF5_SRC_DIR}/H5FDmpi.h ${HDF5_SRC_DIR}/H5FDmpio.h ${HDF5_SRC_DIR}/H5FDmulti.h @@ -256,6 +259,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/H5FDmirror.c b/src/H5FDmirror.c new file mode 100644 index 0000000..ea1f516 --- /dev/null +++ b/src/H5FDmirror.c @@ -0,0 +1,1991 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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: Transmit write-only operations to a receiver/writer process on + * a remote host. + */ + +#include "H5FDdrvr_module.h" /* This source code file is part of the H5FD driver module */ + +#include "H5private.h" /* Generic Functions */ + +#ifdef H5_HAVE_MIRROR_VFD + +#include "H5Eprivate.h" /* Error handling */ +#include "H5Fprivate.h" /* File access */ +#include "H5FDprivate.h" /* File drivers */ +#include "H5FDmirror.h" /* "Mirror" definitions */ +#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_MIRROR_g = 0; + +/* Virtual file structure for a Mirror Driver */ +typedef struct H5FD_mirror_t { + H5FD_t pub; /* Public stuff, must be first */ + H5FD_mirror_fapl_t fa; /* Configuration structure */ + haddr_t eoa; /* End of allocated region */ + haddr_t eof; /* End of file; current file size */ + int sock_fd; /* Handle of socket to remote operator */ + H5FD_mirror_xmit_t xmit; /* Primary communication header */ + uint32_t xmit_i; /* Counter of transmission sent and rec'd */ +} H5FD_mirror_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 _BSWAP_64(X) \ + (uint64_t)( (((X) & 0x00000000000000FF) << 56) \ + | (((X) & 0x000000000000FF00) << 40) \ + | (((X) & 0x0000000000FF0000) << 24) \ + | (((X) & 0x00000000FF000000) << 8) \ + | (((X) & 0x000000FF00000000) >> 8) \ + | (((X) & 0x0000FF0000000000) >> 24) \ + | (((X) & 0x00FF000000000000) >> 40) \ + | (((X) & 0xFF00000000000000) >> 56)) + +/* Debugging flabs for verbose tracing -- nonzero to enable */ +#define MIRROR_DEBUG_OP_CALLS 0 +#define MIRROR_DEBUG_XMIT_BYTES 0 + +#if MIRROR_DEBUG_XMIT_BYTES +#define LOG_XMIT_BYTES(label, buf, len) do { \ + ssize_t bytes_written = 0; \ + const unsigned char *b = NULL; \ + \ + HDfprintf(stdout, "%s bytes:\n```\n", (label)); \ + \ + /* print whole lines */ \ + while ((len - bytes_written) >= 32) { \ + b = (const unsigned char *)(buf) + bytes_written; \ + HDfprintf(stdout, \ + "%04zX %02X%02X%02X%02X %02X%02X%02X%02X" \ + " %02X%02X%02X%02X %02X%02X%02X%02X" \ + " %02X%02X%02X%02X %02X%02X%02X%02X" \ + " %02X%02X%02X%02X %02X%02X%02X%02X\n", \ + bytes_written, \ + b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], \ + b[8], b[9], b[10],b[11], b[12],b[13],b[14],b[15], \ + b[16],b[17],b[18],b[19], b[20],b[21],b[22],b[23], \ + b[24],b[25],b[26],b[27], b[28],b[29],b[30],b[31]); \ + bytes_written += 32; \ + } \ + \ + /* start partial line */ \ + if (len > bytes_written) { \ + HDfprintf(stdout, "%04zX ", bytes_written); \ + } \ + \ + /* partial line blocks */ \ + while ((len - bytes_written) >= 4) { \ + HDfprintf(stdout, " %02X%02X%02X%02X", \ + (buf)[bytes_written], (buf)[bytes_written+1], \ + (buf)[bytes_written+2], (buf)[bytes_written+3]); \ + bytes_written += 4; \ + } \ + \ + /* block separator before partial block */ \ + if (len > bytes_written) { \ + HDfprintf(stdout, " "); \ + } \ + \ + /* partial block individual bytes */ \ + while (len > bytes_written) { \ + HDfprintf(stdout, "%02X", (buf)[bytes_written++]); \ + } \ + \ + /* end partial line */ \ + HDfprintf(stdout, "\n"); \ + HDfprintf(stdout, "```\n"); \ + HDfflush(stdout); \ +} while (0) +#else +#define LOG_XMIT_BYTES(label, buf, len) /* no-op */ +#endif /* MIRROR_DEBUG_XMIT_BYTE */ + +#if MIRROR_DEBUG_OP_CALLS +#define LOG_OP_CALL(name) do { \ + HDprintf("called %s()\n", (name)); \ + fflush(stdout); \ +} while (0) +#else +#define LOG_OP_CALL(name) /* no-op */ +#endif /* MIRROR_DEBUG_OP_CALLS */ + +/* Prototypes */ +static herr_t H5FD_mirror_term(void); +static void *H5FD_mirror_fapl_get(H5FD_t *_file); +static void *H5FD_mirror_fapl_copy(const void *_old_fa); +static herr_t H5FD_mirror_fapl_free(void *_fa); +static haddr_t H5FD_mirror_get_eoa(const H5FD_t *_file, H5FD_mem_t type); +static herr_t H5FD_mirror_set_eoa(H5FD_t *_file, H5FD_mem_t type, haddr_t addr); +static haddr_t H5FD_mirror_get_eof(const H5FD_t *_file, H5FD_mem_t type); +static H5FD_t *H5FD_mirror_open(const char *name, unsigned flags, \ + hid_t fapl_id, haddr_t maxaddr); +static herr_t H5FD_mirror_close(H5FD_t *_file); +static herr_t H5FD_mirror_query(const H5FD_t *_file, unsigned long *flags); +static herr_t H5FD_mirror_write(H5FD_t *_file, H5FD_mem_t type, hid_t fapl_id, \ + haddr_t addr, size_t size, const void *buf); +static herr_t H5FD_mirror_read(H5FD_t *_file, H5FD_mem_t type, hid_t fapl_id, + haddr_t addr, size_t size, void *buf); +static herr_t H5FD_mirror_truncate(H5FD_t *_file, hid_t dxpl_id, \ + hbool_t closing); +static herr_t H5FD_mirror_lock(H5FD_t *_file, hbool_t rw); +static herr_t H5FD_mirror_unlock(H5FD_t *_file); + +static herr_t H5FD__mirror_verify_reply(H5FD_mirror_t *file); + +static const H5FD_class_t H5FD_mirror_g = { + "mirror", /* name */ + MAXADDR, /* maxaddr */ + H5F_CLOSE_WEAK, /* fc_degree */ + H5FD_mirror_term, /* terminate */ + NULL, /* sb_size */ + NULL, /* sb_encode */ + NULL, /* sb_decode */ + 0, /* fapl_size */ + H5FD_mirror_fapl_get, /* fapl_get */ + H5FD_mirror_fapl_copy, /* fapl_copy */ + H5FD_mirror_fapl_free, /* fapl_free */ + 0, /* dxpl_size */ + NULL, /* dxpl_copy */ + NULL, /* dxpl_free */ + H5FD_mirror_open, /* open */ + H5FD_mirror_close, /* close */ + NULL, /* cmp */ + H5FD_mirror_query, /* query */ + NULL, /* get_type_map */ + NULL, /* alloc */ + NULL, /* free */ + H5FD_mirror_get_eoa, /* get_eoa */ + H5FD_mirror_set_eoa, /* set_eoa */ + H5FD_mirror_get_eof, /* get_eof */ + NULL, /* get_handle */ + H5FD_mirror_read, /* read */ + H5FD_mirror_write, /* write */ + NULL, /* flush */ + H5FD_mirror_truncate, /* truncate */ + H5FD_mirror_lock, /* lock */ + H5FD_mirror_unlock, /* unlock */ + H5FD_FLMAP_DICHOTOMY /* fl_map */ +}; + +/* Declare a free list to manage the H5FD_mirror_t struct */ +H5FL_DEFINE_STATIC(H5FD_mirror_t); + + +/*------------------------------------------------------------------------- + * Function: H5FD__init_package + * + * Purpose: Initializes any interface-specific data or routines. + * + * Return: Non-negative on success/Negative on failure + *------------------------------------------------------------------------- + */ +static herr_t +H5FD__init_package(void) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC + + LOG_OP_CALL("H5FD__init_package"); + + if (H5FD_mirror_init() < 0) { + HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, + "unable to initialize mirror VFD"); + } + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5FD__init_package() */ + + +/* ------------------------------------------------------------------------- + * Function: H5FD_mirror_init + * + * Purpose: Initialize this driver by registering the driver with the + * library. + * + * Return: Success: The driver ID for the mirror driver. + * Failure: Negative + * ------------------------------------------------------------------------- + */ +hid_t +H5FD_mirror_init(void) +{ + hid_t ret_value = H5I_INVALID_HID; + + FUNC_ENTER_NOAPI(FAIL) + + LOG_OP_CALL("H5FD_mirror_init"); + + if (H5I_VFL != H5I_get_type(H5FD_MIRROR_g)) { + H5FD_MIRROR_g = H5FD_register(&H5FD_mirror_g, sizeof(H5FD_class_t), + FALSE); + } + + ret_value = H5FD_MIRROR_g; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD_mirror_init() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_term + * + * Purpose: Shut down the VFD + * + * Returns: SUCCEED (Can't fail) + * --------------------------------------------------------------------------- + */ +static herr_t +H5FD_mirror_term(void) +{ + FUNC_ENTER_NOAPI_NOINIT_NOERR + + /* Reset VFL ID */ + H5FD_MIRROR_g = 0; + + LOG_OP_CALL("H5FD_mirror_term"); + + FUNC_LEAVE_NOAPI(SUCCEED) +} /* end H5FD_mirror_term() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD__mirror_xmit_decode_uint16 + * + * Purpose: Extract a 16-bit integer in "network" (Big-Endian) word order + * from the byte-buffer and return it with the local word order at + * the destination pointer. + * + * The programmer must ensure that the received buffer holds + * at least the expected size of data. + * + * Return: The number of bytes read from the buffer (2). + * --------------------------------------------------------------------------- + */ +size_t +H5FD__mirror_xmit_decode_uint16(uint16_t *out, const unsigned char *_buf) +{ + uint16_t n = 0; + LOG_OP_CALL("H5FD__mirror_xmit_decode_uint16"); + HDassert(_buf && out); + HDmemcpy(&n, _buf, sizeof(n)); + *out = (uint16_t)HDntohs(n); + return 2; /* number of bytes eaten */ +} /* end H5FD__mirror_xmit_decode_uint16() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD__mirror_xmit_decode_uint32 + * + * Purpose: Extract a 32-bit integer in "network" (Big-Endian) word order + * from the byte-buffer and return it with the local word order at + * the destination pointer. + * + * The programmer must ensure that the received buffer holds + * at least the expected size of data. + * + * Return: The number of bytes read from the buffer (4). + * --------------------------------------------------------------------------- + */ +size_t +H5FD__mirror_xmit_decode_uint32(uint32_t *out, const unsigned char *_buf) +{ + uint32_t n = 0; + LOG_OP_CALL("H5FD__mirror_xmit_decode_uint32"); + HDassert(_buf && out); + HDmemcpy(&n, _buf, sizeof(n)); + *out = (uint32_t)HDntohl(n); + return 4; /* number of bytes eaten */ +} /* end H5FD__mirror_xmit_decode_uint32() */ + + + +/* --------------------------------------------------------------------------- + * Function: is_host_little_endian + * + * Purpose: Determine whether the host machine is is little-endian. + * + * Store an intger with a known value, re-map the memory to a + * character array, and inspect the array's contents. + * + * Return: The number of bytes written to the buffer (8). + * + * Programmer: Jacob Smith + * 2020-03-05 + * --------------------------------------------------------------------------- + */ +static hbool_t +is_host_little_endian(void) +{ + union { + uint32_t u32; + uint8_t u8[4]; + } echeck; + echeck.u32 = 0xA1B2C3D4; + if (echeck.u8[0] == 0xD4) { + return TRUE; + } + else { + return FALSE; + } +} /* end is_host_little_endian() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD__mirror_xmit_decode_uint64 + * + * Purpose: Extract a 64-bit integer in "network" (Big-Endian) word order + * from the byte-buffer and return it with the local word order. + * + * The programmer must ensure that the received buffer holds + * at least the expected size of data. + * + * WARNING: Does not accommodate other forms of endianness, + * e.g. "middle-endian". + * + * Return: The number of bytes written to the buffer (8). + * --------------------------------------------------------------------------- + */ +size_t +H5FD__mirror_xmit_decode_uint64(uint64_t *out, const unsigned char *_buf) +{ + uint64_t n = 0; + LOG_OP_CALL("H5FD__mirror_xmit_decode_uint64"); + HDassert(_buf && out); + HDmemcpy(&n, _buf, sizeof(n)); + if (TRUE == is_host_little_endian()) { + *out = _BSWAP_64(n); + } + else { + *out = n; + } + return 8; +} /* end H5FD__mirror_xmit_decode_uint64() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD__mirror_xmit_decode_uint8 + * + * Purpose: Extract a 8-bit integer in "network" (Big-Endian) word order + * from the byte-buffer and return it with the local word order at + * the destination pointer. + * (yes, it's one byte). + * + * Return: The number of bytes read from the buffer (1). + * --------------------------------------------------------------------------- + */ +size_t +H5FD__mirror_xmit_decode_uint8(uint8_t *out, const unsigned char *_buf) +{ + LOG_OP_CALL("H5FD__mirror_xmit_decode_uint8"); + HDassert(_buf && out); + HDmemcpy(out, _buf, sizeof(uint8_t)); + return 1; /* number of bytes eaten */ +} /* end H5FD__mirror_xmit_decode_uint8() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD__mirror_xmit_encode_uint16 + * + * Purpose: Encode a 16-bit integer in "network" (Big-Endian) word order + * in place in the destination bytes-buffer. + * + * The programmer must ensure that the destination buffer is + * large enough to hold the expected data. + * + * Return: The number of bytes written to the buffer (2). + * --------------------------------------------------------------------------- + */ +size_t +H5FD__mirror_xmit_encode_uint16(unsigned char *_dest, uint16_t v) +{ + uint16_t n = 0; + LOG_OP_CALL("H5FD__mirror_xmit_encode_uint16"); + HDassert(_dest); + n = (uint16_t)HDhtons(v); + HDmemcpy(_dest, &n, sizeof(n)); + return 2; +} /* end H5FD__mirror_xmit_encode_uint16() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD__mirror_xmit_encode_uint32 + * + * Purpose: Encode a 32-bit integer in "network" (Big-Endian) word order + * in place in the destination bytes-buffer. + * + * The programmer must ensure that the destination buffer is + * large enough to hold the expected data. + * + * Return: The number of bytes written to the buffer (4). + * --------------------------------------------------------------------------- + */ +size_t +H5FD__mirror_xmit_encode_uint32(unsigned char *_dest, uint32_t v) +{ + uint32_t n = 0; + LOG_OP_CALL("H5FD__mirror_xmit_encode_uint32"); + HDassert(_dest); + n = (uint32_t)HDhtonl(v); + HDmemcpy(_dest, &n, sizeof(n)); + return 4; +} /* end H5FD__mirror_xmit_encode_uint32() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD__mirror_xmit_encode_uint64 + * + * Purpose: Encode a 64-bit integer in "network" (Big-Endian) word order + * in place in the destination bytes-buffer. + * + * The programmer must ensure that the destination buffer is + * large enough to hold the expected data. + * + * Return: The number of bytes written to the buffer (8). + * --------------------------------------------------------------------------- + */ +size_t +H5FD__mirror_xmit_encode_uint64(unsigned char *_dest, uint64_t v) +{ + uint64_t n = v; + LOG_OP_CALL("H5FD__mirror_xmit_decode_uint64"); + HDassert(_dest); + if (TRUE == is_host_little_endian()) { + n = _BSWAP_64(v); + } + HDmemcpy(_dest, &n, sizeof(n)); + return 8; +} /* H5FD__mirror_xmit_encode_uint64() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD__mirror_xmit_encode_uint8 + * + * Purpose: Encode a 8-bit integer in "network" (Big-Endian) word order + * in place in the destination bytes-buffer. + * (yes, it's one byte). + * + * The programmer must ensure that the destination buffer is + * large enough to hold the expected data. + * + * Return: The number of bytes read from the buffer (1). + * --------------------------------------------------------------------------- + */ +size_t +H5FD__mirror_xmit_encode_uint8(unsigned char *dest, uint8_t v) +{ + LOG_OP_CALL("H5FD__mirror_xmit_encode_uint8"); + HDassert(dest); + HDmemcpy(dest, &v, sizeof(v)); + return 1; +} /* end H5FD__mirror_xmit_encode_uint8() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_decode_header + * + * Purpose: Extract a mirror_xmit_t "header" from the bytes-buffer. + * + * Fields will be lifted from the buffer and stored in the + * target structure, using in the correct location (different + * systems may insert different padding between components) and + * word order (Big- vs Little-Endian). + * + * The resulting structure should be sanity-checked with + * H5FD_mirror_xmit_is_xmit() before use. + * + * The programmer must ensure that the received buffer holds + * at least the expected size of data. + * + * Return: The number of bytes consumed from the buffer. + * --------------------------------------------------------------------------- + */ +size_t +H5FD_mirror_xmit_decode_header(H5FD_mirror_xmit_t *out, + const unsigned char *buf) +{ + size_t n_eaten = 0; + LOG_OP_CALL("H5FD_mirror_xmit_decode_header"); + HDassert(out && buf); + n_eaten += H5FD__mirror_xmit_decode_uint32(&(out->magic), &buf[n_eaten]); + n_eaten += H5FD__mirror_xmit_decode_uint8(&(out->version), &buf[n_eaten]); + n_eaten += H5FD__mirror_xmit_decode_uint32(&(out->session_token), + &buf[n_eaten]); + n_eaten += H5FD__mirror_xmit_decode_uint32(&(out->xmit_count), + &buf[n_eaten]); + n_eaten += H5FD__mirror_xmit_decode_uint8(&(out->op), &buf[n_eaten]); + HDassert(n_eaten == H5FD_MIRROR_XMIT_HEADER_SIZE); + return n_eaten; +} /* end H5FD_mirror_xmit_decode_header() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_decode_lock + * + * Purpose: Extract a mirror_xmit_lock_t from the bytes-buffer. + * + * Fields will be lifted from the buffer and stored in the + * target structure, using in the correct location (different + * systems may insert different padding between components) and + * word order (Big- vs Little-Endian). + * + * The programmer must ensure that the received buffer holds + * at least the expected size of data. + * + * The resulting structure should be sanity-checked with + * H5FD_mirror_xmit_is_lock() before use. + * + * Return: The number of bytes consumed from the buffer. + * --------------------------------------------------------------------------- + */ +size_t +H5FD_mirror_xmit_decode_lock(H5FD_mirror_xmit_lock_t *out, + const unsigned char *buf) +{ + size_t n_eaten = 0; + LOG_OP_CALL("H5FD_mirror_xmit_decode_lock"); + HDassert(out && buf); + n_eaten += H5FD_mirror_xmit_decode_header(&(out->pub), buf); + n_eaten += H5FD__mirror_xmit_decode_uint64(&(out->rw), &buf[n_eaten]); + HDassert(n_eaten == H5FD_MIRROR_XMIT_LOCK_SIZE); + return n_eaten; +} /* end H5FD_mirror_xmit_decode_lock() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_decode_open + * + * Purpose: Extract a mirror_xmit_open_t from the bytes-buffer. + * + * Fields will be lifted from the buffer and stored in the + * target structure, using in the correct location (different + * systems may insert different padding between components) and + * word order (Big- vs Little-Endian). + * + * The programmer must ensure that the received buffer holds + * at least the expected size of data. + * + * The resulting structure should be sanity-checked with + * H5FD_mirror_xmit_is_open() before use. + * + * Return: The maximum number of bytes that this decoding operation might + * have consumed from the buffer. + * --------------------------------------------------------------------------- + */ +size_t +H5FD_mirror_xmit_decode_open(H5FD_mirror_xmit_open_t *out, + const unsigned char *buf) +{ + size_t n_eaten = 0; + LOG_OP_CALL("H5FD_mirror_xmit_decode_open"); + HDassert(out && buf); + n_eaten += H5FD_mirror_xmit_decode_header(&(out->pub), buf); + n_eaten += H5FD__mirror_xmit_decode_uint32(&(out->flags), &buf[n_eaten]); + n_eaten += H5FD__mirror_xmit_decode_uint64(&(out->maxaddr), &buf[n_eaten]); + n_eaten += H5FD__mirror_xmit_decode_uint64(&(out->size_t_blob), + &buf[n_eaten]); + HDassert((H5FD_MIRROR_XMIT_OPEN_SIZE - H5FD_MIRROR_XMIT_FILEPATH_MAX) + == n_eaten); + HDstrncpy(out->filename, (const char *)&buf[n_eaten], + H5FD_MIRROR_XMIT_FILEPATH_MAX-1); + out->filename[H5FD_MIRROR_XMIT_FILEPATH_MAX-1] = 0; /* force final NULL */ + return H5FD_MIRROR_XMIT_OPEN_SIZE; +} /* end H5FD_mirror_xmit_decode_open() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_decode_reply + * + * Purpose: Extract a mirror_xmit_reply_t from the bytes-buffer. + * + * Fields will be lifted from the buffer and stored in the + * target structure, using in the correct location (different + * systems may insert different padding between components) and + * word order (Big- vs Little-Endian). + * + * The programmer must ensure that the received buffer holds + * at least the expected size of data. + * + * The resulting structure should be sanity-checked with + * H5FD_mirror_xmit_is_reply() before use. + * + * Return: The maximum number of bytes that this decoding operation might + * have consumed from the buffer. + * --------------------------------------------------------------------------- + */ +size_t +H5FD_mirror_xmit_decode_reply(H5FD_mirror_xmit_reply_t *out, + const unsigned char *buf) +{ + size_t n_eaten = 0; + LOG_OP_CALL("H5FD_mirror_xmit_decode_reply"); + HDassert(out && buf); + n_eaten += H5FD_mirror_xmit_decode_header(&(out->pub), buf); + n_eaten += H5FD__mirror_xmit_decode_uint32(&(out->status), &buf[n_eaten]); + HDassert((H5FD_MIRROR_XMIT_REPLY_SIZE - H5FD_MIRROR_STATUS_MESSAGE_MAX) + == n_eaten); + HDstrncpy(out->message, (const char *)&buf[n_eaten], + H5FD_MIRROR_STATUS_MESSAGE_MAX-1); + out->message[H5FD_MIRROR_STATUS_MESSAGE_MAX-1] = 0; /* force NULL term */ + return H5FD_MIRROR_XMIT_REPLY_SIZE; +} /* end H5FD_mirror_xmit_decode_reply() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_decode_set_eoa + * + * Purpose: Extract a mirror_xmit_eoa_t from the bytes-buffer. + * + * Fields will be lifted from the buffer and stored in the + * target structure, using in the correct location (different + * systems may insert different padding between components) and + * word order (Big- vs Little-Endian). + * + * The programmer must ensure that the received buffer holds + * at least the expected size of data. + * + * The resulting structure should be sanity-checked with + * H5FD_mirror_xmit_is_set_eoa() before use. + * + * Return: The number of bytes consumed from the buffer. + * --------------------------------------------------------------------------- + */ +size_t +H5FD_mirror_xmit_decode_set_eoa(H5FD_mirror_xmit_eoa_t *out, + const unsigned char *buf) +{ + size_t n_eaten = 0; + LOG_OP_CALL("H5FD_mirror_xmit_decode_set_eoa"); + HDassert(out && buf); + n_eaten += H5FD_mirror_xmit_decode_header(&(out->pub), buf); + n_eaten += H5FD__mirror_xmit_decode_uint8(&(out->type), &buf[n_eaten]); + n_eaten += H5FD__mirror_xmit_decode_uint64(&(out->eoa_addr), &buf[n_eaten]); + HDassert(n_eaten == H5FD_MIRROR_XMIT_EOA_SIZE); + return n_eaten; +} /* end H5FD_mirror_xmit_decode_set_eoa() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_decode_write + * + * Purpose: Extract a mirror_xmit_write_t from the bytes-buffer. + * + * Fields will be lifted from the buffer and stored in the + * target structure, using in the correct location (different + * systems may insert different padding between components) and + * word order (Big- vs Little-Endian). + * + * The programmer must ensure that the received buffer holds + * at least the expected size of data. + * + * The resulting structure should be sanity-checked with + * H5FD_mirror_xmit_is_write() before use. + * + * Return: The number of bytes consumed from the buffer. + * --------------------------------------------------------------------------- + */ +size_t +H5FD_mirror_xmit_decode_write(H5FD_mirror_xmit_write_t *out, + const unsigned char *buf) +{ + size_t n_eaten = 0; + LOG_OP_CALL("H5FD_mirror_xmit_decode_write"); + HDassert(out && buf); + n_eaten += H5FD_mirror_xmit_decode_header(&(out->pub), buf); + n_eaten += H5FD__mirror_xmit_decode_uint8(&(out->type), &buf[n_eaten]); + n_eaten += H5FD__mirror_xmit_decode_uint64(&(out->offset), &buf[n_eaten]); + n_eaten += H5FD__mirror_xmit_decode_uint64(&(out->size), &buf[n_eaten]); + HDassert(n_eaten == H5FD_MIRROR_XMIT_WRITE_SIZE); + return n_eaten; +} /* end H5FD_mirror_xmit_decode_write() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_encode_header + * + * Purpose: Encode a mirror_xmit_t "header" to the bytes-buffer. + * + * Fields will be packed into the buffer in a predictable manner, + * any numbers stored in "network" (Big-Endian) word order. + * + * The programmer must ensure that the destination buffer is + * large enough to hold the expected data. + * + * Return: The number of bytes written to the buffer. + * --------------------------------------------------------------------------- + */ +size_t +H5FD_mirror_xmit_encode_header(unsigned char *dest, + const H5FD_mirror_xmit_t *x) +{ + size_t n_writ = 0; + LOG_OP_CALL("H5FD_mirror_xmit_encode_header"); + HDassert(dest && x); + n_writ += H5FD__mirror_xmit_encode_uint32((dest+n_writ), x->magic); + n_writ += H5FD__mirror_xmit_encode_uint8((dest+n_writ), x->version); + n_writ += H5FD__mirror_xmit_encode_uint32((dest+n_writ), x->session_token); + n_writ += H5FD__mirror_xmit_encode_uint32((dest+n_writ), x->xmit_count); + n_writ += H5FD__mirror_xmit_encode_uint8((dest+n_writ), x->op); + HDassert(n_writ == H5FD_MIRROR_XMIT_HEADER_SIZE); + return n_writ; +} /* end H5FD_mirror_xmit_encode_header() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_encode_lock + * + * Purpose: Encode a mirror_xmit_lock_t to the bytes-buffer. + * Fields will be packed into the buffer in a predictable manner, + * any numbers stored in "network" (Big-Endian) word order. + * + * The programmer must ensure that the destination buffer is + * large enough to hold the expected data. + * + * Return: The number of bytes written to the buffer. + * --------------------------------------------------------------------------- + */ +size_t +H5FD_mirror_xmit_encode_lock(unsigned char *dest, + const H5FD_mirror_xmit_lock_t *x) +{ + size_t n_writ = 0; + LOG_OP_CALL("H5FD_mirror_xmit_encode_lock"); + HDassert(dest && x); + n_writ += H5FD_mirror_xmit_encode_header(dest, + (const H5FD_mirror_xmit_t *)&(x->pub)); + n_writ += H5FD__mirror_xmit_encode_uint64(&dest[n_writ], x->rw); + HDassert(n_writ == H5FD_MIRROR_XMIT_LOCK_SIZE); + return n_writ; +} /* end H5FD_mirror_xmit_encode_lock() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_encode_open + * + * Purpose: Encode a mirror_xmit_open_t to the bytes-buffer. + * Fields will be packed into the buffer in a predictable manner, + * any numbers stored in "network" (Big-Endian) word order. + * + * The programmer must ensure that the destination buffer is + * large enough to hold the expected data. + * + * Return: The maximum number of bytes that this decoding operation might + * have written into the buffer. + * --------------------------------------------------------------------------- + */ +size_t +H5FD_mirror_xmit_encode_open(unsigned char *dest, + const H5FD_mirror_xmit_open_t *x) +{ + size_t n_writ = 0; + LOG_OP_CALL("H5FD_mirror_xmit_encode_open"); + HDassert(dest && x); + HDbzero(dest, H5FD_MIRROR_XMIT_OPEN_SIZE); + n_writ += H5FD_mirror_xmit_encode_header(dest, + (const H5FD_mirror_xmit_t *)&(x->pub)); + n_writ += H5FD__mirror_xmit_encode_uint32(&dest[n_writ], x->flags); + n_writ += H5FD__mirror_xmit_encode_uint64(&dest[n_writ], x->maxaddr); + n_writ += H5FD__mirror_xmit_encode_uint64(&dest[n_writ], x->size_t_blob); + HDassert((H5FD_MIRROR_XMIT_OPEN_SIZE - H5FD_MIRROR_XMIT_FILEPATH_MAX) + == n_writ); + HDstrncpy((char *)&dest[n_writ], x->filename, + H5FD_MIRROR_XMIT_FILEPATH_MAX); + return H5FD_MIRROR_XMIT_OPEN_SIZE; +} /* end H5FD_mirror_xmit_encode_open() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_encode_reply + * + * Purpose: Encode a mirror_xmit_reply_t to the bytes-buffer. + * + * Fields will be packed into the buffer in a predictable manner, + * any numbers stored in "network" (Big-Endian) word order. + * + * The programmer must ensure that the destination buffer is + * large enough to hold the expected data. + * + * Return: The maximum number of bytes that this decoding operation might + * have written into the buffer. + * --------------------------------------------------------------------------- + */ +size_t +H5FD_mirror_xmit_encode_reply(unsigned char *dest, + const H5FD_mirror_xmit_reply_t *x) +{ + size_t n_writ = 0; + LOG_OP_CALL("H5FD_mirror_xmit_encode_reply"); + HDassert(dest && x); + HDbzero(dest, H5FD_MIRROR_XMIT_REPLY_SIZE); + n_writ += H5FD_mirror_xmit_encode_header(dest, + (const H5FD_mirror_xmit_t *)&(x->pub)); + n_writ += H5FD__mirror_xmit_encode_uint32(&dest[n_writ], x->status); + HDassert((H5FD_MIRROR_XMIT_REPLY_SIZE - H5FD_MIRROR_STATUS_MESSAGE_MAX) + == n_writ); + HDstrncpy((char *)&dest[n_writ], x->message, + H5FD_MIRROR_STATUS_MESSAGE_MAX); + return H5FD_MIRROR_XMIT_REPLY_SIZE; +} /* end H5FD_mirror_xmit_encode_reply() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_encode_set_eoa + * + * Purpose: Encode a mirror_xmit_eoa_t to the bytes-buffer. + * + * Fields will be packed into the buffer in a predictable manner, + * any numbers stored in "network" (Big-Endian) word order. + * + * The programmer must ensure that the destination buffer is + * large enough to hold the expected data. + * + * Return: The number of bytes written to the buffer. + * --------------------------------------------------------------------------- + */ +size_t +H5FD_mirror_xmit_encode_set_eoa(unsigned char *dest, + const H5FD_mirror_xmit_eoa_t *x) +{ + size_t n_writ = 0; + LOG_OP_CALL("H5FD_mirror_xmit_encode_set_eoa"); + HDassert(dest && x); + n_writ += H5FD_mirror_xmit_encode_header(dest, + (const H5FD_mirror_xmit_t *)&(x->pub)); + n_writ += H5FD__mirror_xmit_encode_uint8(&dest[n_writ], x->type); + n_writ += H5FD__mirror_xmit_encode_uint64(&dest[n_writ], x->eoa_addr); + HDassert(n_writ == H5FD_MIRROR_XMIT_EOA_SIZE); + return n_writ; +} /* end H5FD_mirror_xmit_encode_set_eoa() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_encode_write + * + * Purpose: Encode a mirror_xmit_write_t to the bytes-buffer. + * + * Fields will be packed into the buffer in a predictable manner, + * any numbers stored in "network" (Big-Endian) word order. + * + * The programmer must ensure that the destination buffer is + * large enough to hold the expected data. + * + * Return: The number of bytes written to the buffer. + * --------------------------------------------------------------------------- + */ +size_t +H5FD_mirror_xmit_encode_write(unsigned char *dest, + const H5FD_mirror_xmit_write_t *x) +{ + size_t n_writ = 0; + LOG_OP_CALL("H5FD_mirror_xmit_encode_write"); + HDassert(dest && x); + n_writ += H5FD_mirror_xmit_encode_header(dest, + (const H5FD_mirror_xmit_t *)&(x->pub)); + n_writ += H5FD__mirror_xmit_encode_uint8(&dest[n_writ], x->type); + n_writ += H5FD__mirror_xmit_encode_uint64(&dest[n_writ], x->offset); + n_writ += H5FD__mirror_xmit_encode_uint64(&dest[n_writ], x->size); + HDassert(n_writ == H5FD_MIRROR_XMIT_WRITE_SIZE); + return n_writ; +} /* end H5FD_mirror_xmit_encode_write() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_is_close + * + * Purpose: Verify that a mirror_xmit_t is a valid CLOSE xmit. + * + * Checks header validity and op code. + * + * Return: TRUE if valid; else FALSE. + * --------------------------------------------------------------------------- + */ +hbool_t +H5FD_mirror_xmit_is_close(const H5FD_mirror_xmit_t *xmit) +{ + LOG_OP_CALL("H5FD_mirror_xmit_is_close"); + HDassert(xmit); + if ( (TRUE == H5FD_mirror_xmit_is_xmit(xmit)) && + (H5FD_MIRROR_OP_CLOSE == xmit->op) ) + { + return TRUE; + } + return FALSE; +} /* end H5FD_mirror_xmit_is_close() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_is_lock + * + * Purpose: Verify that a mirror_xmit_lock_t is a valid LOCK xmit. + * + * Checks header validity and op code. + * + * Return: TRUE if valid; else FALSE. + * --------------------------------------------------------------------------- + */ +hbool_t +H5FD_mirror_xmit_is_lock(const H5FD_mirror_xmit_lock_t *xmit) +{ + LOG_OP_CALL("H5FD_mirror_xmit_is_lock"); + HDassert(xmit); + if ( (TRUE == H5FD_mirror_xmit_is_xmit(&(xmit->pub))) && + (H5FD_MIRROR_OP_LOCK == xmit->pub.op) ) + { + return TRUE; + } + return FALSE; +} /* end H5FD_mirror_xmit_is_lock() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_is_open + * + * Purpose: Verify that a mirror_xmit_open_t is a valid OPEN xmit. + * + * Checks header validity and op code. + * + * Return: TRUE if valid; else FALSE. + * --------------------------------------------------------------------------- + */ +hbool_t +H5FD_mirror_xmit_is_open(const H5FD_mirror_xmit_open_t *xmit) +{ + LOG_OP_CALL("H5FD_mirror_xmit_is_open"); + HDassert(xmit); + if ( (TRUE == H5FD_mirror_xmit_is_xmit(&(xmit->pub))) && + (H5FD_MIRROR_OP_OPEN == xmit->pub.op) ) + { + return TRUE; + } + return FALSE; +} /* end H5FD_mirror_xmit_is_open() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_is_eoa + * + * Purpose: Verify that a mirror_xmit_eoa_t is a valid SET-EOA xmit. + * + * Checks header validity and op code. + * + * Return: TRUE if valid; else FALSE. + * --------------------------------------------------------------------------- + */ +hbool_t +H5FD_mirror_xmit_is_set_eoa(const H5FD_mirror_xmit_eoa_t *xmit) +{ + LOG_OP_CALL("H5FD_mirror_xmit_is_set_eoa"); + HDassert(xmit); + if ( (TRUE == H5FD_mirror_xmit_is_xmit(&(xmit->pub))) && + (H5FD_MIRROR_OP_SET_EOA == xmit->pub.op) ) + { + return TRUE; + } + return FALSE; +} /* end H5FD_mirror_xmit_is_eoa() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_is_reply + * + * Purpose: Verify that a mirror_xmit_reply_t is a valid REPLY xmit. + * + * Checks header validity and op code. + * + * Return: TRUE if valid; else FALSE. + * --------------------------------------------------------------------------- + */ +hbool_t +H5FD_mirror_xmit_is_reply(const H5FD_mirror_xmit_reply_t *xmit) +{ + LOG_OP_CALL("H5FD_mirror_xmit_is_reply"); + HDassert(xmit); + if ( (TRUE == H5FD_mirror_xmit_is_xmit(&(xmit->pub))) && + (H5FD_MIRROR_OP_REPLY == xmit->pub.op) ) + { + return TRUE; + } + return FALSE; +} /* end H5FD_mirror_xmit_is_reply() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_is_write + * + * Purpose: Verify that a mirror_xmit_write_t is a valid WRITE xmit. + * + * Checks header validity and op code. + * + * Return: TRUE if valid; else FALSE. + * --------------------------------------------------------------------------- + */ +hbool_t +H5FD_mirror_xmit_is_write(const H5FD_mirror_xmit_write_t *xmit) +{ + LOG_OP_CALL("H5FD_mirror_xmit_is_write"); + HDassert(xmit); + if ( (TRUE == H5FD_mirror_xmit_is_xmit(&(xmit->pub))) && + (H5FD_MIRROR_OP_WRITE == xmit->pub.op) ) + { + return TRUE; + } + return FALSE; +} /* end H5FD_mirror_xmit_is_write() */ + + +/* --------------------------------------------------------------------------- + * Function: H5FD_mirror_xmit_is_xmit + * + * Purpose: Verify that a mirror_xmit_t is well-formed. + * + * Checks magic number and structure version. + * + * Return: TRUE if valid; else FALSE. + * --------------------------------------------------------------------------- + */ +hbool_t +H5FD_mirror_xmit_is_xmit(const H5FD_mirror_xmit_t *xmit) +{ + LOG_OP_CALL("H5FD_mirror_xmit_is_xmit"); + HDassert(xmit); + if ( (H5FD_MIRROR_XMIT_MAGIC != xmit->magic) || + (H5FD_MIRROR_XMIT_CURR_VERSION != xmit->version) ) + { + return FALSE; + } + return TRUE; +} /* end H5FD_mirror_xmit_is_xmit() */ + + +/* ---------------------------------------------------------------------------- + * Function: H5FD__mirror_verify_reply + * + * Purpose: Wait for and read reply data from remote processes. + * Sanity-check that a reply is well-formed and valid. + * If all checks pass, inspect the reply contents and handle + * reported error, if not an OK reply. + * + * Return: SUCCEED if ok, else FAIL. + * ---------------------------------------------------------------------------- + */ +herr_t +H5FD__mirror_verify_reply(H5FD_mirror_t *file) +{ + char xmit_buf[H5FD_MIRROR_XMIT_REPLY_SIZE]; + struct H5FD_mirror_xmit_reply_t reply; + ssize_t read_ret = 0; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NOINIT; + + LOG_OP_CALL("H5FD__mirror_verify_reply"); + + HDassert(file && file->sock_fd); + + read_ret = HDread(file->sock_fd, xmit_buf, H5FD_MIRROR_XMIT_REPLY_SIZE); + if (read_ret < 0) { + HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "unable to read reply"); + } + if (read_ret != H5FD_MIRROR_XMIT_REPLY_SIZE) { + HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "unexpected read size"); + } + + LOG_XMIT_BYTES("reply", xmit_buf, read_ret); + + if (H5FD_mirror_xmit_decode_reply(&reply, (const unsigned char *)xmit_buf) + != H5FD_MIRROR_XMIT_REPLY_SIZE) + { + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "unable to decode reply xmit"); + } + + if (H5FD_mirror_xmit_is_reply(&reply) != TRUE) { + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "xmit op code was not REPLY"); + } + + if (reply.pub.session_token != file->xmit.session_token) { + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "wrong session"); + } + + if (reply.pub.xmit_count != (file->xmit_i)++) { + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "xmit out of sync"); + } + + if (reply.status != H5FD_MIRROR_STATUS_OK) { + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, + "%s", (const char *)(reply.message)); + } + +done: + FUNC_LEAVE_NOAPI(ret_value); +} /* end H5FD__mirror_verify_reply() */ + + +/* ------------------------------------------------------------------------- + * Function: H5FD_mirror_fapl_get + * + * Purpose: Get the file access propety list which could be used to create + * an identical file. + * + * Return: Success: pointer to the new file access property list value. + * Failure: NULL + * ------------------------------------------------------------------------- + */ +static void * +H5FD_mirror_fapl_get(H5FD_t *_file) +{ + H5FD_mirror_t *file = (H5FD_mirror_t *)_file; + H5FD_mirror_fapl_t *fa = NULL; + void *ret_value = NULL; + + FUNC_ENTER_NOAPI_NOINIT + + LOG_OP_CALL("H5FD_mirror_fapl_get"); + + fa = (H5FD_mirror_fapl_t *)H5MM_calloc(sizeof(H5FD_mirror_fapl_t)); + if (NULL == fa) { + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "calloc failed"); + } + + HDmemcpy(fa, &(file->fa), sizeof(H5FD_mirror_fapl_t)); + + ret_value = fa; + +done: + if (ret_value == NULL) { + if (fa != NULL) { + H5MM_xfree(fa); + } + } + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD_mirror_fapl_get() */ + + +/* ------------------------------------------------------------------------- + * Function: H5FD_mirror_fapl_copy + * + * Purpose: Copies the mirror vfd-specific file access properties. + * + * Return: Success: Pointer to a new property list + * Failure: NULL + * ------------------------------------------------------------------------- + */ +static void * +H5FD_mirror_fapl_copy(const void *_old_fa) +{ + const H5FD_mirror_fapl_t *old_fa = (const H5FD_mirror_fapl_t *)_old_fa; + H5FD_mirror_fapl_t *new_fa = NULL; + void *ret_value = NULL; + + FUNC_ENTER_NOAPI_NOINIT + + LOG_OP_CALL("H5FD_mirror_fapl_copy"); + + new_fa = (H5FD_mirror_fapl_t *)H5MM_malloc(sizeof(H5FD_mirror_fapl_t)); + if (new_fa == NULL) { + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, + "memory allocation failed"); + } + + HDmemcpy(new_fa, old_fa, sizeof(H5FD_mirror_fapl_t)); + ret_value = new_fa; + +done: + if (ret_value == NULL) { + if (new_fa != NULL) { + H5MM_xfree(new_fa); + } + } + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD_mirror_fapl_copy() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_mirror_fapl_free + * + * Purpose: Frees the mirror VFD-specific file access properties. + * + * Return: SUCCEED (cannot fail) + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_mirror_fapl_free(void *_fa) +{ + H5FD_mirror_fapl_t *fa = (H5FD_mirror_fapl_t*)_fa; + + FUNC_ENTER_NOAPI_NOINIT_NOERR + + LOG_OP_CALL("H5FD_mirror_fapl_free"); + + /* sanity check */ + HDassert(fa != NULL); + HDassert(fa->magic == H5FD_MIRROR_FAPL_MAGIC); + + fa->magic += 1; /* invalidate */ + H5MM_xfree(fa); + + FUNC_LEAVE_NOAPI(SUCCEED) +} /* end H5FD_mirror_fapl_free() */ + + +/* ------------------------------------------------------------------------- + * Function: H5Pget_fapl_mirror + * + * Purpose: Get the configuration information for this fapl. + * Data is memcopied into the fa_out pointer. + * + * Return: SUCCEED/FAIL + * ------------------------------------------------------------------------- + */ +herr_t +H5Pget_fapl_mirror(hid_t fapl_id, H5FD_mirror_fapl_t *fa_out) +{ + const H5FD_mirror_fapl_t *fa = NULL; + H5P_genplist_t *plist = NULL; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_API(FAIL) + H5TRACE2("e", "i*x", fapl_id, fa_out); + + LOG_OP_CALL("H5Pget_fapl_mirror"); + + if (NULL == fa_out) { + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "fa_out is NULL"); + } + + plist = H5P_object_verify(fapl_id, H5P_FILE_ACCESS); + if (NULL == plist) { + HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, + "not a file access property list"); + } + + if (H5P_peek_driver(plist) != H5FD_MIRROR) { + HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "incorrect VFL driver"); + } + + fa = (const H5FD_mirror_fapl_t *)H5P_peek_driver_info(plist); + if (NULL == fa) { + HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "bad VFL driver info"); + } + + HDassert(fa->magic == H5FD_MIRROR_FAPL_MAGIC); /* sanity check */ + + HDmemcpy(fa_out, fa, sizeof(H5FD_mirror_fapl_t)); + +done: + FUNC_LEAVE_API(ret_value); +} /* end H5Pget_fapl_mirror() */ + + +/*------------------------------------------------------------------------- + * Function: H5Pset_fapl_mirror + * + * Purpose: Modify the file access property list to use the mirror + * driver (H5FD_MIRROR) defined in this source file. + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +herr_t +H5Pset_fapl_mirror(hid_t fapl_id, H5FD_mirror_fapl_t *fa) +{ + H5P_genplist_t *plist = NULL; + herr_t ret_value = FAIL; + + FUNC_ENTER_API(FAIL) + H5TRACE2("e", "i*x", fapl_id, fa); + + LOG_OP_CALL("H5Pset_fapl_mirror"); + + plist = H5P_object_verify(fapl_id, H5P_FILE_ACCESS); + if (NULL == plist) { + HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, + "not a file access property list"); + } + if (NULL == fa) { + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "null fapl_t pointer"); + } + if (H5FD_MIRROR_FAPL_MAGIC != fa->magic) { + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid fapl_t magic"); + } + if (H5FD_MIRROR_CURR_FAPL_T_VERSION != fa->version) { + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unknown fapl_t version"); + } + + ret_value = H5P_set_driver(plist, H5FD_MIRROR, (const void *)fa); + +done: + FUNC_LEAVE_API(ret_value) +} /* end H5Pset_fapl_mirror() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_mirror_open + * + * Purpose: Create and/or opens a file as an HDF5 file. + * + * Initiate connection with remote Server/Writer. + * If successful, the remote file is open. + * + * 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_mirror_open(const char *name, + unsigned flags, + hid_t fapl_id, + haddr_t maxaddr) +{ +#define MIRR_OPEN_MAXBUF 16 /* local symbol to give meaning to magic number */ + /* #defined because it is needed at compile time */ + /* Large enough to hold a port number string */ + int live_socket = -1; + struct sockaddr_in target_addr; + socklen_t addr_size; + char xmit_buf[H5FD_MIRROR_XMIT_OPEN_SIZE]; + H5FD_mirror_fapl_t fa; + H5FD_mirror_t *file = NULL; + H5FD_mirror_xmit_open_t open_xmit; + H5FD_t *ret_value = NULL; + + FUNC_ENTER_NOAPI_NOINIT + + LOG_OP_CALL("H5FD_mirror_open"); + + /* --------------- */ + /* Check arguments */ + /* --------------- */ + + if (!name || !*name) { + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid file name"); + } + if (HDstrlen(name) >= H5FD_MIRROR_XMIT_FILEPATH_MAX) { + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "filename is too long"); + } + 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 (H5Pget_fapl_mirror(fapl_id, &fa) == FAIL) { + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "can't get config info"); + } + + if (H5FD_MIRROR_FAPL_MAGIC != fa.magic) { + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid fapl magic"); + } + + if (H5FD_MIRROR_CURR_FAPL_T_VERSION != fa.version) { + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid fapl version"); + } + + /* --------------------- */ + /* Handshake with remote */ + /* --------------------- */ + + live_socket = HDsocket(AF_INET, SOCK_STREAM, 0); + if (live_socket < 0) { + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "can't create socket"); + } + + target_addr.sin_family = AF_INET; + target_addr.sin_port = HDhtons((uint16_t)fa.handshake_port); + target_addr.sin_addr.s_addr = HDinet_addr(fa.remote_ip); + HDmemset(target_addr.sin_zero, '\0', sizeof target_addr.sin_zero); + + addr_size = sizeof(target_addr); + if (HDconnect(live_socket, (struct sockaddr *)&target_addr, addr_size) < 0) + { + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, + "can't connect to remote server"); + } + + /* ------------- */ + /* Open the file */ + /* ------------- */ + + file = (H5FD_mirror_t *)H5FL_CALLOC(H5FD_mirror_t); + if (NULL == file) { + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, + "unable to allocate file struct"); + } + + file->sock_fd = live_socket; + file->xmit_i = 0; + + file->xmit.magic = H5FD_MIRROR_XMIT_MAGIC; + file->xmit.version = H5FD_MIRROR_XMIT_CURR_VERSION; + file->xmit.xmit_count = file->xmit_i++; + file->xmit.session_token = (uint32_t)(0x01020304 ^ file->sock_fd); /* TODO: hashing? */ + /* int --> uint32_t may truncate on some systems... shouldn't matter? */ + + file->xmit.op = H5FD_MIRROR_OP_OPEN; + open_xmit.pub = file->xmit; + open_xmit.flags = (uint32_t)flags; + open_xmit.maxaddr = (uint64_t)maxaddr; + open_xmit.size_t_blob = (uint64_t)((size_t)(-1)); + HDsnprintf(open_xmit.filename, H5FD_MIRROR_XMIT_FILEPATH_MAX-1, "%s", name); + + if (H5FD_mirror_xmit_encode_open((unsigned char *)xmit_buf, + (const H5FD_mirror_xmit_open_t *)&open_xmit) + != H5FD_MIRROR_XMIT_OPEN_SIZE) + { + HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, NULL, "unable to encode open"); + } + + LOG_XMIT_BYTES("open", xmit_buf, H5FD_MIRROR_XMIT_OPEN_SIZE); + + if (HDwrite(file->sock_fd, xmit_buf, H5FD_MIRROR_XMIT_OPEN_SIZE) < 0) { + HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, NULL, "unable to transmit open"); + } + + if (H5FD__mirror_verify_reply(file) == FAIL) { + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "invalid reply"); + } + + ret_value = (H5FD_t *)file; + +done: + if (NULL == ret_value) { + if (file) { + file = H5FL_FREE(H5FD_mirror_t, file); + } + if (live_socket >= 0 && HDclose(live_socket) < 0) { + HDONE_ERROR(H5E_VFL, H5E_CANTCLOSEFILE, NULL, + "can't close socket"); + } + } + FUNC_LEAVE_NOAPI(ret_value) +#undef MIRR_OPEN_MAXBUF +} /* end H5FD_mirror_open() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_mirror_close + * + * Purpose: Closes the HDF5 file. + * + * Tries to send a CLOSE op to the remote Writer and expects + * a valid reply, then closes the socket. + * In error, attempts to send a deliberately invalid xmit to the + * Writer to get it to close/abort, then attempts to close the + * socket. + * + * Return: Success: SUCCEED + * Failure: FAIL, file possibly not closed but resources freed. + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_mirror_close(H5FD_t *_file) +{ + H5FD_mirror_t *file = (H5FD_mirror_t *)_file; + unsigned char xmit_buf[H5FD_MIRROR_XMIT_HEADER_SIZE]; + int xmit_encoded = 0; /* monitor point of failure */ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NOINIT + + LOG_OP_CALL("H5FD_mirror_close"); + + /* Sanity check */ + HDassert(file); + HDassert(file->sock_fd >= 0); + + file->xmit.xmit_count = (file->xmit_i)++; + file->xmit.op = H5FD_MIRROR_OP_CLOSE; + + if (H5FD_mirror_xmit_encode_header(xmit_buf, + (const H5FD_mirror_xmit_t *)&(file->xmit)) + != H5FD_MIRROR_XMIT_HEADER_SIZE) + { + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to encode close"); + } + xmit_encoded = 1; + + LOG_XMIT_BYTES("close", xmit_buf, H5FD_MIRROR_XMIT_HEADER_SIZE); + + if (HDwrite(file->sock_fd, xmit_buf, H5FD_MIRROR_XMIT_HEADER_SIZE) < 0) { + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to transmit close"); + } + + if (H5FD__mirror_verify_reply(file) == FAIL) { + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid reply"); + } + + if (HDclose(file->sock_fd) < 0) { + HGOTO_ERROR(H5E_VFL, H5E_CANTCLOSEFILE, FAIL, "can't close socket"); + } + +done: + if (ret_value == FAIL) { + if (xmit_encoded == 0) { + /* Encode failed; send GOODBYE to force writer halt. + * We can ignore any response from the writer, if we receive + * any reply at all. + */ + if (HDwrite(file->sock_fd, "GOODBYE", HDstrlen("GOODBYE")) < 0) { + HDONE_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, + "unable to transmit close"); + if (HDclose(file->sock_fd) < 0) { + HDONE_ERROR(H5E_VFL, H5E_CANTCLOSEFILE, FAIL, + "can't close socket"); + } + file->sock_fd = -1; /* invalidate for later */ + } /* end if problem writing goodbye; go down hard */ + else + if (HDshutdown(file->sock_fd, SHUT_WR) < 0) { + HDONE_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, + "can't shutdown socket write: %s", + HDstrerror(errno)); + } /* end else-if problem shutting down socket */ + } /* end if xmit encode failed */ + + if (file->sock_fd >= 0) { + if (HDclose(file->sock_fd) < 0) { + HDONE_ERROR(H5E_VFL, H5E_CANTCLOSEFILE, FAIL, + "can't close socket"); + } + } /* end if socket not closed by going down hard */ + } /* end if error */ + + file = H5FL_FREE(H5FD_mirror_t, file); /* always release resources */ + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD_mirror_close() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_mirror_query + * + * Purpose: Get the driver feature flags implemented by the driver. + * + * Return: SUCCEED (non-negative) (can't fail) + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_mirror_query(const H5FD_t H5_ATTR_UNUSED *_file, unsigned long *flags) +{ + FUNC_ENTER_NOAPI_NOINIT_NOERR; + + LOG_OP_CALL("H5FD_mirror_query"); + + /* Notice: the Mirror VFD Writer currently uses only the Sec2 driver as + * the underying driver -- as such, the Mirror VFD implementation copies + * the Sec2 feature flags as its own. + * + * File pointer is always NULL/unused -- the H5FD_FEAT_IGNORE_DRVRINFO flag + * is never included. + * -- JOS 2020-01-13 + */ + if (flags) { + *flags = 0 \ + | H5FD_FEAT_AGGREGATE_METADATA \ + | H5FD_FEAT_ACCUMULATE_METADATA \ + | H5FD_FEAT_DATA_SIEVE \ + | H5FD_FEAT_AGGREGATE_SMALLDATA \ + | H5FD_FEAT_POSIX_COMPAT_HANDLE \ + | H5FD_FEAT_SUPPORTS_SWMR_IO \ + | H5FD_FEAT_DEFAULT_VFD_COMPATIBLE; + } + + FUNC_LEAVE_NOAPI(SUCCEED); +} /* end H5FD_mirror_query() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_mirror_get_eoa + * + * Purpose: Gets 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. + * + * Required to register the driver. + * + * Return: The end-of-address marker. + *------------------------------------------------------------------------- + */ +static haddr_t +H5FD_mirror_get_eoa(const H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type) +{ + const H5FD_mirror_t *file = (const H5FD_mirror_t *)_file; + + FUNC_ENTER_NOAPI_NOINIT_NOERR + + LOG_OP_CALL("H5FD_mirror_get_eoa"); + + HDassert(file); + + FUNC_LEAVE_NOAPI(file->eoa) +} /* end H5FD_mirror_get_eoa() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_mirror_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_mirror_set_eoa(H5FD_t *_file, H5FD_mem_t type, haddr_t addr) +{ + H5FD_mirror_xmit_eoa_t xmit_eoa; + unsigned char xmit_buf[H5FD_MIRROR_XMIT_EOA_SIZE]; + H5FD_mirror_t *file = (H5FD_mirror_t *)_file; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NOINIT + + LOG_OP_CALL("H5FD_mirror_set_eoa"); + + HDassert(file); + + file->eoa = addr; /* local copy */ + + file->xmit.xmit_count = (file->xmit_i)++; + file->xmit.op = H5FD_MIRROR_OP_SET_EOA; + + xmit_eoa.pub = file->xmit; + xmit_eoa.type = (uint8_t)type; + xmit_eoa.eoa_addr = (uint64_t)addr; + + if (H5FD_mirror_xmit_encode_set_eoa(xmit_buf, + (const H5FD_mirror_xmit_eoa_t *)&xmit_eoa) + != H5FD_MIRROR_XMIT_EOA_SIZE) + { + HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "unable to encode set-eoa"); + } + + LOG_XMIT_BYTES("set-eoa", xmit_buf, H5FD_MIRROR_XMIT_EOA_SIZE); + + if (HDwrite(file->sock_fd, xmit_buf, H5FD_MIRROR_XMIT_EOA_SIZE) < 0) { + HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, + "unable to transmit set-eoa"); + } + + if (H5FD__mirror_verify_reply(file) == FAIL) { + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid reply"); + } + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD_mirror_set_eoa() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_mirror_get_eof + * + * Purpose: Returns the end-of-file marker, which is the greater of + * either the filesystem end-of-file or the HDF5 end-of-address + * markers. + * + * Required to register the driver. + * + * Return: End of file address, the first address past the end of the + * "file", either the filesystem file or the HDF5 file. + *------------------------------------------------------------------------- + */ +static haddr_t +H5FD_mirror_get_eof(const H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type) +{ + const H5FD_mirror_t *file = (const H5FD_mirror_t *)_file; + + FUNC_ENTER_NOAPI_NOINIT_NOERR + + LOG_OP_CALL("H5FD_mirror_get_eof"); + + HDassert(file); + + FUNC_LEAVE_NOAPI(file->eof) +} /* end H5FD_mirror_get_eof() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_mirror_read + * + * Purpose: Required to register the driver. + * If called, MUST fail. + * + * Return: FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_mirror_read(H5FD_t H5_ATTR_UNUSED *_file, + H5FD_mem_t H5_ATTR_UNUSED type, + hid_t H5_ATTR_UNUSED fapl_id, + haddr_t H5_ATTR_UNUSED addr, + size_t H5_ATTR_UNUSED size, + void H5_ATTR_UNUSED *buf) +{ + herr_t ret_value = FAIL; + + FUNC_ENTER_NOAPI_NOINIT_NOERR + + LOG_OP_CALL("H5FD_mirror_read"); + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD_mirror_read() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_mirror_write + * + * Purpose: Writes SIZE bytes of data to FILE beginning at address ADDR + * from buffer BUF according to data transfer properties in + * DXPL_ID. + * + * Send metadata regarding the write (location, size) to the + * remote Writer, then separately transmits the data. + * Both transmission expect an OK reply from the Writer. + * This two-exchange approach incurs significant overhead, + * but is a simple and modular approach. + * Start optimizations here. + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_mirror_write(H5FD_t *_file, + H5FD_mem_t type, + hid_t H5_ATTR_UNUSED dxpl_id, + haddr_t addr, + size_t size, + const void *buf) +{ + H5FD_mirror_xmit_write_t xmit_write; + unsigned char xmit_buf[H5FD_MIRROR_XMIT_WRITE_SIZE]; + H5FD_mirror_t *file = (H5FD_mirror_t *)_file; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NOINIT + + LOG_OP_CALL("H5FD_mirror_write"); + + HDassert(file); + HDassert(buf); + + file->xmit.xmit_count = (file->xmit_i)++; + file->xmit.op = H5FD_MIRROR_OP_WRITE; + + xmit_write.pub = file->xmit; + xmit_write.size = (uint64_t)size; + xmit_write.offset = (uint64_t)addr; + xmit_write.type = (uint8_t)type; + + + /* Notify Writer of incoming data to write. */ + if (H5FD_mirror_xmit_encode_write(xmit_buf, + (const H5FD_mirror_xmit_write_t *)&xmit_write) + != H5FD_MIRROR_XMIT_WRITE_SIZE) + { + HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "unable to encode write"); + } + + LOG_XMIT_BYTES("write", xmit_buf, H5FD_MIRROR_XMIT_WRITE_SIZE); + + if (HDwrite(file->sock_fd, xmit_buf, H5FD_MIRROR_XMIT_WRITE_SIZE) < 0) { + HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "unable to transmit write"); + } + + /* Check that our write xmission was received */ + if (H5FD__mirror_verify_reply(file) == FAIL) { + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid reply"); + } + + /* Send the data to be written */ + if (HDwrite(file->sock_fd, buf, size) < 0) { + HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "unable to transmit data"); + } + + /* Writer should reply that it got the data and is still okay/ready */ + if (H5FD__mirror_verify_reply(file) == FAIL) { + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid reply"); + } + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD_mirror_write() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_mirror_truncate + * + * Purpose: Makes sure that the true file size is the same (or larger) + * than the end-of-address. + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_mirror_truncate(H5FD_t *_file, + hid_t H5_ATTR_UNUSED dxpl_id, + hbool_t H5_ATTR_UNUSED closing) +{ + unsigned char xmit_buf[H5FD_MIRROR_XMIT_HEADER_SIZE]; + H5FD_mirror_t *file = (H5FD_mirror_t *)_file; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NOINIT + + LOG_OP_CALL("H5FD_mirror_truncate"); + + file->xmit.xmit_count = (file->xmit_i)++; + file->xmit.op = H5FD_MIRROR_OP_TRUNCATE; + + if (H5FD_mirror_xmit_encode_header(xmit_buf, &(file->xmit)) + != H5FD_MIRROR_XMIT_HEADER_SIZE) + { + HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "unable to encode truncate"); + } + + LOG_XMIT_BYTES("truncate", xmit_buf, H5FD_MIRROR_XMIT_HEADER_SIZE); + + if (HDwrite(file->sock_fd, xmit_buf, H5FD_MIRROR_XMIT_HEADER_SIZE) < 0) { + HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, + "unable to transmit truncate"); + } + + if (H5FD__mirror_verify_reply(file) == FAIL) { + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid reply"); + } + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD_mirror_truncate() */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_mirror_lock + * + * Purpose: To place an advisory lock on a file. + * The lock type to apply depends on the parameter "rw": + * TRUE--opens for write: an exclusive lock + * FALSE--opens for read: a shared lock + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_mirror_lock(H5FD_t *_file, hbool_t rw) +{ + H5FD_mirror_xmit_lock_t xmit_lock; + unsigned char xmit_buf[H5FD_MIRROR_XMIT_LOCK_SIZE]; + H5FD_mirror_t *file = (H5FD_mirror_t *)_file; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NOINIT; + + LOG_OP_CALL("H5FD_mirror_lock"); + + file->xmit.xmit_count = (file->xmit_i)++; + file->xmit.op = H5FD_MIRROR_OP_LOCK; + + xmit_lock.pub = file->xmit; + xmit_lock.rw = (uint64_t)rw; + + if (H5FD_mirror_xmit_encode_lock(xmit_buf, + (const H5FD_mirror_xmit_lock_t *)&xmit_lock) + != H5FD_MIRROR_XMIT_LOCK_SIZE) + { + HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "unable to encode lock"); + } + + LOG_XMIT_BYTES("lock", xmit_buf, H5FD_MIRROR_XMIT_LOCK_SIZE); + + if (HDwrite(file->sock_fd, xmit_buf, H5FD_MIRROR_XMIT_LOCK_SIZE) < 0) { + HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "unable to transmit lock"); + } + + if (H5FD__mirror_verify_reply(file) == FAIL) { + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid reply"); + } + +done: + FUNC_LEAVE_NOAPI(ret_value); +} /* end H5FD_mirror_lock */ + + +/*------------------------------------------------------------------------- + * Function: H5FD_mirror_unlock + * + * Purpose: Remove the existing lock on the file. + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_mirror_unlock(H5FD_t *_file) +{ + unsigned char xmit_buf[H5FD_MIRROR_XMIT_HEADER_SIZE]; + H5FD_mirror_t *file = (H5FD_mirror_t *)_file; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NOINIT; + + LOG_OP_CALL("H5FD_mirror_unlock"); + + file->xmit.xmit_count = (file->xmit_i)++; + file->xmit.op = H5FD_MIRROR_OP_UNLOCK; + + if (H5FD_mirror_xmit_encode_header(xmit_buf, &(file->xmit)) + != H5FD_MIRROR_XMIT_HEADER_SIZE) + { + HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "unable to encode unlock"); + } + + LOG_XMIT_BYTES("unlock", xmit_buf, H5FD_MIRROR_XMIT_HEADER_SIZE); + + if (HDwrite(file->sock_fd, xmit_buf, H5FD_MIRROR_XMIT_HEADER_SIZE) < 0) { + HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "unable to transmit unlock"); + } + + if (H5FD__mirror_verify_reply(file) == FAIL) { + HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid reply"); + } + +done: + FUNC_LEAVE_NOAPI(ret_value); +} /* end H5FD_mirror_unlock */ + +#endif /* H5_HAVE_MIRROR_VFD */ + diff --git a/src/H5FDmirror.h b/src/H5FDmirror.h new file mode 100644 index 0000000..fb66b7b --- /dev/null +++ b/src/H5FDmirror.h @@ -0,0 +1,371 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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: Public, shared definitions for Mirror VFD & remote Writer. + */ + +#ifndef H5FDmirror_H +#define H5FDmirror_H + +#ifdef H5_HAVE_MIRROR_VFD + +#define H5FD_MIRROR (H5FD_mirror_init()) + +#ifdef __cplusplus +extern "C" { +#endif + +/* ============================================================================ + * Mirror VFD use and operation. + * ============================================================================ + */ + +/* --------------------------------------------------------------------------- + * Structure: H5FD_mirror_fapl_t + * + * Used to pass configuraiton information to the Mirror VFD. + * Populate components as appropriate and pass structure pointer to + * `H5Pset_fapl_mirror()`. + * + * `magic` (uint32_t) + * Semi-unique number to sanity-check pointers to this structure type. + * MUST equal H5FD_MIRROR_FAPL_MAGIC to be considered valid. + * + * `version` (uint32_t) + * Indicates expected components of the structure. + * + * `handshake_port (int) + * Port number to expect to reach the "Mirror Server" on the remote host. + * + * `remote_ip` (char[]) + * IP address string of "Mirror Server" remote host. + * --------------------------------------------------------------------------- + */ +#define H5FD_MIRROR_FAPL_MAGIC 0xF8DD514C +#define H5FD_MIRROR_CURR_FAPL_T_VERSION 1 +#define H5FD_MIRROR_MAX_IP_LEN 32 +typedef struct H5FD_mirror_fapl_t { + uint32_t magic; + uint32_t version; + int handshake_port; + char remote_ip[H5FD_MIRROR_MAX_IP_LEN + 1]; +} H5FD_mirror_fapl_t; + +H5_DLL hid_t H5FD_mirror_init(void); +H5_DLL herr_t H5Pget_fapl_mirror(hid_t fapl_id, H5FD_mirror_fapl_t *fa_out); +H5_DLL herr_t H5Pset_fapl_mirror(hid_t fapl_id, H5FD_mirror_fapl_t *fa); + +/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + * IPC - Mirror VFD and Remote Worker application. + * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + */ + + +/* The maximum allowed size for a receiving buffer when accepting bytes to + * write. Writes larger than this size are performed by multiple accept-write + * steps by the Writer. */ +#define H5FD_MIRROR_DATA_BUFFER_MAX H5_GB /* 1 Gigabyte */ + +#define H5FD_MIRROR_XMIT_CURR_VERSION 1 +#define H5FD_MIRROR_XMIT_MAGIC 0x87F8005B + +#define H5FD_MIRROR_OP_OPEN 1 +#define H5FD_MIRROR_OP_CLOSE 2 +#define H5FD_MIRROR_OP_WRITE 3 +#define H5FD_MIRROR_OP_TRUNCATE 4 +#define H5FD_MIRROR_OP_REPLY 5 +#define H5FD_MIRROR_OP_SET_EOA 6 +#define H5FD_MIRROR_OP_LOCK 7 +#define H5FD_MIRROR_OP_UNLOCK 8 + +#define H5FD_MIRROR_STATUS_OK 0 +#define H5FD_MIRROR_STATUS_ERROR 1 +#define H5FD_MIRROR_STATUS_MESSAGE_MAX 256 /* Dedicated error message size */ + +/* Maximum length of a path/filename string, including the NULL-terminator. + * Must not be smaller than H5FD_SPLITTER_PATH_MAX. */ +#define H5FD_MIRROR_XMIT_FILEPATH_MAX 4097 + +/* Define the exact sizes of the various xmit blobs as sent over the wire. + * This is used to minimize the number of bytes transmitted as well as to + * sanity-check received bytes. + * Any modifications to the xmit structures and/or the encode/decode functions + * must be reflected here. + * */ +#define H5FD_MIRROR_XMIT_HEADER_SIZE 14 +#define H5FD_MIRROR_XMIT_EOA_SIZE (H5FD_MIRROR_XMIT_HEADER_SIZE + 9) +#define H5FD_MIRROR_XMIT_LOCK_SIZE (H5FD_MIRROR_XMIT_HEADER_SIZE + 8) +#define H5FD_MIRROR_XMIT_OPEN_SIZE (H5FD_MIRROR_XMIT_HEADER_SIZE + 20 + H5FD_MIRROR_XMIT_FILEPATH_MAX) +#define H5FD_MIRROR_XMIT_REPLY_SIZE (H5FD_MIRROR_XMIT_HEADER_SIZE + 4 + H5FD_MIRROR_STATUS_MESSAGE_MAX) +#define H5FD_MIRROR_XMIT_WRITE_SIZE (H5FD_MIRROR_XMIT_HEADER_SIZE + 17) + +/* Maximum length of any xmit. */ +#define H5FD_MIRROR_XMIT_BUFFER_MAX MAX2( MAX3(H5FD_MIRROR_XMIT_HEADER_SIZE, \ + H5FD_MIRROR_XMIT_EOA_SIZE, \ + H5FD_MIRROR_XMIT_LOCK_SIZE), \ + MAX3(H5FD_MIRROR_XMIT_OPEN_SIZE, \ + H5FD_MIRROR_XMIT_REPLY_SIZE, \ + H5FD_MIRROR_XMIT_WRITE_SIZE) ) \ + +/* --------------------------------------------------------------------------- + * Structure: H5FD_mirror_xmit_t + * + * Common structure 'header' for all mirror VFD/worker IPC. + * Must be the first component of a derived operation xmit structure, + * such as file-open or write command. + * + * `magic` (uint32_t) + * A "unique" number identifying the structure and endianness of + * transmitting maching. + * Must be set to H5FD_MIRROR_XMIT_MAGIC native to the VFD "sender". + * + * `version` (uint8_t) + * Number used to identify the structure membership. + * Allows sane modifications to this structure in the future. + * Must be set to H5FD_MIRROR_XMIT_CURR_VERSION. + * + * `session_token` (uint32_t) + * A "unique" number identifying the session between VFD sender and + * remote receiver/worker/writer. Exists to help sanity-check. + * + * `xmit_count` (uint32_t) + * Which transmission this is since the session began. + * Used to sanity-check transmission errors. + * First xmit (file-open) must be 0. + * + * `op` (uint8_t) + * Number identifying which operation to perform. + * Corresponds with the extended structure outside of this xmit header. + * Possible values are all defined H5FD_MIRROR_OP_* constants. + * + * --------------------------------------------------------------------------- + */ +typedef struct H5FD_mirror_xmit_t { + uint32_t magic; + uint8_t version; + uint32_t session_token; + uint32_t xmit_count; + uint8_t op; +} H5FD_mirror_xmit_t; + +/* --------------------------------------------------------------------------- + * Structure: H5FD_mirror_xmit_eoa_t + * + * Structure containing eoa-set information from VFD sender. + * + * `pub` (H5FD_mirror_xmit_t) + * Common transmission header, containing session information. + * Must be first. + * + * `type` (uint8_t) + * System-independent alias for H5F[D]_mem_t. + * Specifies datatype to be written. + * + * `eoa_addr` (uint64_t) + * New address for eoa. + * (Natively 'haddr_t', always a 64-bit field) + * + * --------------------------------------------------------------------------- + */ +typedef struct H5FD_mirror_xmit_eoa_t { + H5FD_mirror_xmit_t pub; + uint8_t type; + uint64_t eoa_addr; +} H5FD_mirror_xmit_eoa_t; + +/* --------------------------------------------------------------------------- + * Structure: H5FD_mirror_xmit_lock_t + * + * Structure containing eoa-set information from VFD sender. + * + * `pub` (H5FD_mirror_xmit_t) + * Common transmission header, containing session information. + * Must be first. + * + * `rw` (uint64_t) + * The Read/Write mode flag passed into H5FDlock(). + * (Natively `hbool_t`, an 'int') TODO: native int may be 64-bit? + * + * --------------------------------------------------------------------------- + */ +typedef struct H5FD_mirror_xmit_lock_t { + H5FD_mirror_xmit_t pub; + uint64_t rw; +} H5FD_mirror_xmit_lock_t; + +/* --------------------------------------------------------------------------- + * Structure: H5FD_mirror_xmit_open_t + * + * Structure containing file-open information from the VFD sender. + * + * `pub` (H5FD_mirror_xmit_t) + * Common transmission header, containing session information. + * Must be first. + * + * `flags` (uint32_t) + * VFL-layer file-open flags passed directly to H5FDopen(). + * (Natively 'unsigned [int]') TODO: native int may be 64-bit? + * + * `maxaddr` (uint64_t) + * VFL-layer maximum allowed address space for the file to open passed + * directly to H5FDopen(). + * (Natively 'haddr_t', always a 64-bit field) + * + * `size_t_blob` (uint64_t) + * A number indicating how large a size_t is on the sending system. + * Must be set to (uint64_t)((size_t)(-1)) + * (maximum possible value of size_t, cast to uint64_t). + * The receiving system inspects this value -- if the local (remote) + * size_t is smaller than that of the Sender, issues a warning. + * Not an error, as: + * 1. It is assumed that underlying file systems/drivers have become + * smart enough to handle file sizes that otherwise might be + * constrained. + * 2. The Mirror Writer ingests bytes to write multiple 'slices' if the + * size is greater than H5FD_MIRROR_DATA_BUFFER_MAX, regardless of + * any size_t storage size disparity. + * + * `filename` (char[]) + * String giving the filename and path of file to open. + * + * --------------------------------------------------------------------------- + */ +typedef struct H5FD_mirror_xmit_open_t { + H5FD_mirror_xmit_t pub; + uint32_t flags; + uint64_t maxaddr; + uint64_t size_t_blob; + char filename[H5FD_MIRROR_XMIT_FILEPATH_MAX]; +} H5FD_mirror_xmit_open_t; + +/* --------------------------------------------------------------------------- + * Structure: H5FD_mirror_xmit_reply_t + * + * Structure used by the remote receiver/worker/writer to respond to + * a command from the VFD sender. + * + * `pub` (H5FD_mirror_xmit_t) + * Common transmission header, containing session information. + * Must be first. + * + * `status` (uint32_t) + * Number indicating whether the command was successful or if an + * occured. + * Allowed values are H5FD_MIRROR_STATUS_OK and + * H5FD_MIRROR_STATUS_ERROR. + * + * `message` (char[]) + * Error message. Populated if and only if there was a problem. + * It is possible that a message may reach the end of the alloted + * space without a NULL terminator -- the onus is on the programmer to + * handle this situation. + * + * --------------------------------------------------------------------------- + */ +typedef struct H5FD_mirror_xmit_reply_t { + H5FD_mirror_xmit_t pub; + uint32_t status; + char message[H5FD_MIRROR_STATUS_MESSAGE_MAX]; +} H5FD_mirror_xmit_reply_t; + +/* --------------------------------------------------------------------------- + * Structure: H5FD_mirror_xmit_write_t + * + * Structure containing data-write information from VFD sender. + * + * The data to be written is transmitted in subsequent, packets + * and may be broken up into more than one transmission buffer. + * The VFD sender and remote receiver/worker/writer must coordinate + * the receipt of data. + * + * `pub` (H5FD_mirror_xmit_t) + * Common transmission header, containing session information. + * Must be first. + * + * `type` (uint8_t) + * Specifies datatype to be written. + * (Natively 'H5FD_mem_t', an enumerated type in H5Fpublic.h) + * + * `offset` (uint64_t) + * Start location of write in file. + * (Natively 'haddr_t', always a 64-bit field) + * + * `size` (uint64_t) + * Size of the data to be written, in bytes. + * (Natively 'size_t', accommodate the largest possible as 64-bits) + * + * --------------------------------------------------------------------------- + */ +typedef struct H5FD_mirror_xmit_write_t { + H5FD_mirror_xmit_t pub; + uint8_t type; + uint64_t offset; + uint64_t size; +} H5FD_mirror_xmit_write_t; + + + +/* Encode/decode routines are required to "pack" the xmit data into a known + * byte format for transmission over the wire. + * + * All component numbers must be stored in "network" word order (Big-Endian). + * + * All components must be packed in the order given in the structure definition. + * + * All components must be packed with zero padding between. + */ + +H5_DLL size_t H5FD__mirror_xmit_decode_uint16(uint16_t *out, const unsigned char *buf); +H5_DLL size_t H5FD__mirror_xmit_decode_uint32(uint32_t *out, const unsigned char *buf); +H5_DLL size_t H5FD__mirror_xmit_decode_uint64(uint64_t *out, const unsigned char *buf); +H5_DLL size_t H5FD__mirror_xmit_decode_uint8(uint8_t *out, const unsigned char *buf); +H5_DLL size_t H5FD__mirror_xmit_encode_uint16(unsigned char *dest, uint16_t v); +H5_DLL size_t H5FD__mirror_xmit_encode_uint32(unsigned char *dest, uint32_t v); +H5_DLL size_t H5FD__mirror_xmit_encode_uint64(unsigned char *dest, uint64_t v); +H5_DLL size_t H5FD__mirror_xmit_encode_uint8(unsigned char *dest, uint8_t v); + +H5_DLL size_t H5FD_mirror_xmit_decode_header(H5FD_mirror_xmit_t *out, const unsigned char *buf); +H5_DLL size_t H5FD_mirror_xmit_decode_lock(H5FD_mirror_xmit_lock_t *out, const unsigned char *buf); +H5_DLL size_t H5FD_mirror_xmit_decode_open(H5FD_mirror_xmit_open_t *out, const unsigned char *buf); +H5_DLL size_t H5FD_mirror_xmit_decode_reply(H5FD_mirror_xmit_reply_t *out, const unsigned char *buf); +H5_DLL size_t H5FD_mirror_xmit_decode_set_eoa(H5FD_mirror_xmit_eoa_t *out, const unsigned char *buf); +H5_DLL size_t H5FD_mirror_xmit_decode_write(H5FD_mirror_xmit_write_t *out, const unsigned char *buf); + +H5_DLL size_t H5FD_mirror_xmit_encode_header(unsigned char *dest, const H5FD_mirror_xmit_t *x); +H5_DLL size_t H5FD_mirror_xmit_encode_lock(unsigned char *dest, const H5FD_mirror_xmit_lock_t *x); +H5_DLL size_t H5FD_mirror_xmit_encode_open(unsigned char *dest, const H5FD_mirror_xmit_open_t *x); +H5_DLL size_t H5FD_mirror_xmit_encode_reply(unsigned char *dest, const H5FD_mirror_xmit_reply_t *x); +H5_DLL size_t H5FD_mirror_xmit_encode_set_eoa(unsigned char *dest, const H5FD_mirror_xmit_eoa_t *x); +H5_DLL size_t H5FD_mirror_xmit_encode_write(unsigned char *dest, const H5FD_mirror_xmit_write_t *x); + +H5_DLL hbool_t H5FD_mirror_xmit_is_close(const H5FD_mirror_xmit_t *xmit); +H5_DLL hbool_t H5FD_mirror_xmit_is_lock(const H5FD_mirror_xmit_lock_t *xmit); +H5_DLL hbool_t H5FD_mirror_xmit_is_open(const H5FD_mirror_xmit_open_t *xmit); +H5_DLL hbool_t H5FD_mirror_xmit_is_reply(const H5FD_mirror_xmit_reply_t *xmit); +H5_DLL hbool_t H5FD_mirror_xmit_is_set_eoa(const H5FD_mirror_xmit_eoa_t *xmit); +H5_DLL hbool_t H5FD_mirror_xmit_is_write(const H5FD_mirror_xmit_write_t *xmit); +H5_DLL hbool_t H5FD_mirror_xmit_is_xmit(const H5FD_mirror_xmit_t *xmit); + +#ifdef __cplusplus +} +#endif + +#else /* H5_HAVE_MIRROR_VFD */ + +#define H5FD_MIRROR (H5I_INAVLID_HID) + +#endif /* H5_HAVE_MIRROR_VFD */ + +#endif /* H5FDmirror_H */ + + diff --git a/src/H5FDpublic.h b/src/H5FDpublic.h index 514d1bf..124bac6 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/H5FDsec2.c b/src/H5FDsec2.c index a393490..d718a93 100644 --- a/src/H5FDsec2.c +++ b/src/H5FDsec2.c @@ -521,6 +521,12 @@ H5FD_sec2_query(const H5FD_t *_file, unsigned long *flags /* out */) FUNC_ENTER_NOAPI_NOINIT_NOERR /* Set the VFL feature flags that this driver supports */ + /* Notice: the Mirror VFD Writer currently uses only the Sec2 driver as + * the underying driver -- as such, the Mirror VFD implementation copies + * these feature flags as its own. Any modifications made here must be + * reflected in H5FDmirror.c + * -- JOS 2020-01-13 + */ if(flags) { *flags = 0; *flags |= H5FD_FEAT_AGGREGATE_METADATA; /* OK to aggregate metadata allocations */ diff --git a/src/H5FDsplitter.c b/src/H5FDsplitter.c new file mode 100644 index 0000000..13816a5 --- /dev/null +++ b/src/H5FDsplitter.c @@ -0,0 +1,1467 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 "H5FDsec2.h" /* Generic Functions */ +#include "H5FDstdio.h" /* Generic Functions */ +#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)); \ + fflush(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); + + +/*------------------------------------------------------------------------- + * 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("H5FD__init_package"); + + 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("H5FD_splitter_init"); + + 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_NOAPI_NOINIT_NOERR + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_term"); + + /* 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD__copy_plist"); + + 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; + H5P_genplist_t *plist_ptr = NULL; + herr_t ret_value = SUCCEED; + + H5Eclear2(H5E_DEFAULT); + + FUNC_ENTER_API(FAIL) + H5TRACE2("e", "i*Dr", fapl_id, vfd_config); + + H5FD_SPLITTER_LOG_CALL("H5Pset_fapl_splitter"); + + 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, "invlaid 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.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: + 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("H5Pget_fapl_splitter"); + + /* 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_flush"); + + /* 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, "H5FD_splitter_flush", + 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 /*out*/) +{ + H5FD_splitter_t *file = (H5FD_splitter_t *)_file; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_read"); + + 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_write"); + + 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, "H5FD_splitter_write", + 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_NOAPI_NOINIT_NOERR + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_fapl_get"); + + ret_value = H5FD_splitter_fapl_copy(&(file->fa)); + + FUNC_LEAVE_NOAPI(ret_value) +} + + +/*------------------------------------------------------------------------- + * 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_fapl_copy"); + + HDassert(old_fa_ptr); + + new_fa_ptr = (H5FD_splitter_fapl_t *)H5MM_calloc(sizeof(H5FD_splitter_fapl_t)); + if (NULL == new_fa_ptr) { + HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate log file FAPL") + } + + if (HDmemcpy(new_fa_ptr, old_fa_ptr, sizeof(H5FD_splitter_fapl_t)) == NULL) { + HGOTO_ERROR(H5E_ARGS, H5E_CANTCOPY, NULL, "unable to shallow-copy info") + } + if (HDstrncpy(new_fa_ptr->wo_path, old_fa_ptr->wo_path, H5FD_SPLITTER_PATH_MAX) == NULL) { + HGOTO_ERROR(H5E_ARGS, H5E_CANTCOPY, NULL, "unable to copy write-only channel file path") + } + if (HDstrncpy(new_fa_ptr->log_file_path, old_fa_ptr->log_file_path, H5FD_SPLITTER_PATH_MAX) == NULL) { + HGOTO_ERROR(H5E_ARGS, H5E_CANTCOPY, NULL, "unable to copy log file path") + } + + /* 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) { + H5MM_free(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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_fapl_free"); + + /* 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 */ + H5MM_free(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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_open"); + + /* 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_RESOURCE, H5E_NOSPACE, 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 */ + if (HDstrncpy(file_ptr->fa.wo_path, fapl_ptr->wo_path, H5FD_SPLITTER_PATH_MAX) == NULL) { + HGOTO_ERROR(H5E_VFL, H5E_CANTCOPY, NULL, "unable to copy write-only path") + } + if (HDstrncpy(file_ptr->fa.log_file_path, fapl_ptr->log_file_path, H5FD_SPLITTER_PATH_MAX) == NULL) { + HGOTO_ERROR(H5E_VFL, H5E_CANTCOPY, NULL, "unable to copy logfile path") + } + 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, "H5FD_splitter_open", + 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); + } + 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_close"); + + /* 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, "H5FD_splitter_close", + 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_get_eoa"); + + /* 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_set_eoa";) + + /* 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, "H5FD_splitter_set_eoa", + 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_get_eof"); + + /* 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_truncate"); + + 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, "H5FD_splitter_truncate", + 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_NOAPI_NOINIT_NOERR + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_sb_size"); + + /* 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_sb_encode"); + + /* 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_sb_decode"); + + /* 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_NOAPI_NOINIT_NOERR + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_cmp"); + + 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_get_handle"); + + /* 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_lock"); + + 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_CANTLOCK, 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, "H5FD_splitter_lock", + H5E_VFL, H5E_CANTLOCK, 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_unlock"); + + /* 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_CANTUNLOCK, FAIL, "unable to unlock R/W file") + } + if (file->wo_file != NULL) { + if (H5FD_unlock(file->wo_file) < 0) { + HGOTO_ERROR(H5E_VFL, H5E_CANTUNLOCK, 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_query"); + + 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_alloc"); + + /* 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, "H5FD_splitter_alloc", + 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_get_type_map"); + + /* 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_NOAPI_NOINIT + + H5FD_SPLITTER_LOG_CALL("H5FD_splitter_free"); + + /* 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, "H5FD_splitter_free", + 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) +{ + size_t size = 0; + char *s = NULL; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC_NOERR + + H5FD_SPLITTER_LOG_CALL("H5FD__splitter_log_error"); + + /* Check arguments */ + HDassert(file); + HDassert(atfunc && *atfunc); + HDassert(msg && *msg); + + if (file->logfp != NULL) { + size = strlen(atfunc) + strlen(msg) + 3; /* ':', ' ', '\n' */ + s = (char *)malloc(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/H5private.h b/src/H5private.h index f570723..f7fb40b 100644 --- a/src/H5private.h +++ b/src/H5private.h @@ -365,6 +365,22 @@ #endif /* __cplusplus */ /* + * Networking headers used by the mirror VFD and related tests and utilities. + */ +#ifdef H5_HAVE_ARPA_INET_H +# include +#endif +#ifdef H5_HAVE_NETDB_H +# include +#endif +#ifdef H5_HAVE_NETINET_IN_H +# include +#endif +#ifdef H5_HAVE_SYS_SOCKET_H +# include +#endif + +/* * Status return values for the `herr_t' type. * Since some unix/c routines use 0 and -1 (or more precisely, non-negative * vs. negative) as their return code, and some assumption had been made in @@ -646,6 +662,9 @@ typedef struct { #ifndef HDabs #define HDabs(X) abs(X) #endif /* HDabs */ +#ifndef HDaccept + #define HDaccept(A,B,C) accept((A),(B),(C)) /* mirror VFD */ +#endif /* HDaccept */ #ifndef HDaccess #define HDaccess(F,M) access(F, M) #endif /* HDaccess */ @@ -692,9 +711,15 @@ typedef struct { #ifndef HDatoll #define HDatoll(S) atoll(S) #endif /* HDatol */ +#ifndef HDbind + #define HDbind(A,B,C) bind((A),(B),(C)) /* mirror VFD */ +#endif /* HDbind */ #ifndef HDbsearch #define HDbsearch(K,B,N,Z,F) bsearch(K,B,N,Z,F) #endif /* HDbsearch */ +#ifndef HDbzero + #define HDbzero(A,B) bzero((A),(B)) /* mirror VFD */ +#endif /* HDbzero */ #ifndef HDcalloc #define HDcalloc(N,Z) calloc(N,Z) #endif /* HDcalloc */ @@ -734,6 +759,9 @@ typedef struct { #ifndef HDclosedir #define HDclosedir(D) closedir(D) #endif /* HDclosedir */ +#ifndef HDconnect + #define HDconnect(A,B,C) connect((A),(B),(C)) /* mirror VFD */ +#endif /* HDconnect */ #ifndef HDcos #define HDcos(X) cos(X) #endif /* HDcos */ @@ -978,9 +1006,12 @@ typedef off_t h5_stat_size_t; #ifndef HDgetgroups #define HDgetgroups(Z,G) getgroups(Z,G) #endif /* HDgetgroups */ +#ifndef HDgethostbyaddr + #define HDgethostbyaddr(A,B,C) gethostbyaddr((A),(B),(C)) /* mirror VFD */ +#endif /* HDgethostbyaddr */ #ifndef HDgethostname #define HDgethostname(N,L) gethostname(N,L) -#endif /* HDgetlogin */ +#endif /* HDgethostname */ #ifndef HDgetlogin #define HDgetlogin() getlogin() #endif /* HDgetlogin */ @@ -1014,6 +1045,18 @@ typedef off_t h5_stat_size_t; #ifndef HDgmtime #define HDgmtime(T) gmtime(T) #endif /* HDgmtime */ +#ifndef HDhtonl + #define HDhtonl(X) htonl((X)) /* mirror VFD */ +#endif /* HDhtonl */ +#ifndef HDhtons + #define HDhtons(X) htons((X)) /* mirror VFD */ +#endif /* HDhtons */ +#ifndef HDinet_addr + #define HDinet_addr(C) inet_addr((C)) /* mirror VFD */ +#endif /* HDinet_addr */ +#ifndef HDinet_ntoa + #define HDinet_ntoa(C) inet_ntoa((C)) /* mirror VFD */ +#endif /* HDinet_ntoa */ #ifndef HDisalnum #define HDisalnum(C) isalnum((int)(C)) /*cast for solaris warning*/ #endif /* HDisalnum */ @@ -1068,6 +1111,9 @@ typedef off_t h5_stat_size_t; #ifndef HDlink #define HDlink(OLD,NEW) link(OLD,NEW) #endif /* HDlink */ +#ifndef HDlisten + #define HDlisten(A,B) listen((A),(B)) /* mirror VFD */ +#endif /* HDlisten */ #ifndef HDllround #define HDllround(V) llround(V) #endif /* HDround */ @@ -1149,6 +1195,12 @@ typedef off_t h5_stat_size_t; #ifndef HDnanosleep #define HDnanosleep(N, O) nanosleep(N, O) #endif /* HDnanosleep */ +#ifndef HDntohl + #define HDntohl(A) ntohl((A)) /* mirror VFD */ +#endif /* HDntohl */ +#ifndef HDntohs + #define HDntohs(A) ntohs((A)) /* mirror VFD */ +#endif /* HDntohs */ #ifndef HDopen #define HDopen(F,...) open(F,__VA_ARGS__) #endif /* HDopen */ @@ -1296,12 +1348,21 @@ typedef off_t h5_stat_size_t; #ifndef HDsetsid #define HDsetsid() setsid() #endif /* HDsetsid */ +#ifndef HDsetsockopt + #define HDsetsockopt(A,B,C,D,E) setsockopt((A),(B),(C),(D),(E)) /* mirror VFD */ +#endif /* HDsetsockopt */ #ifndef HDsetuid #define HDsetuid(U) setuid(U) #endif /* HDsetuid */ #ifndef HDsetvbuf #define HDsetvbuf(F,S,M,Z) setvbuf(F,S,M,Z) #endif /* HDsetvbuf */ +#ifndef HDshutdown + #define HDshutdown(A, B) shutdown((A),(B)) /* mirror VFD */ +#endif /* HDshutdown */ +#ifndef HDsigaction + #define HDsigaction(S,A,O) sigaction((S),(A),(O)) +#endif /* HDsigaction */ #ifndef HDsigaddset #define HDsigaddset(S,N) sigaddset(S,N) #endif /* HDsigaddset */ @@ -1347,6 +1408,9 @@ typedef off_t h5_stat_size_t; #ifndef HDsnprintf #define HDsnprintf snprintf /*varargs*/ #endif /* HDsnprintf */ +#ifndef HDsocket + #define HDsocket(A,B,C) socket((A),(B),(C)) /* mirror VFD */ +#endif /* HDsocket */ #ifndef HDsprintf #define HDsprintf sprintf /*varargs*/ #endif /* HDsprintf */ diff --git a/src/Makefile.am b/src/Makefile.am index 0c07c1b..4f40e60 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -62,7 +62,8 @@ libhdf5_la_SOURCES= H5.c H5checksum.c H5dbg.c H5lib_settings.c H5system.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 \ + H5FDmirror.c H5FDmulti.c H5FDros3.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 H5Gcompact.c H5Gdense.c H5Gdeprec.c \ @@ -135,9 +136,9 @@ include_HEADERS = hdf5.h H5api_adpt.h H5overflow.h H5pubconf.h H5public.h H5vers H5Apublic.h H5ACpublic.h \ H5Cpublic.h H5Dpublic.h \ H5Epubgen.h H5Epublic.h H5ESpublic.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 \ + H5FDpublic.h H5FDcore.h H5FDdirect.h H5FDfamily.h H5FDhdfs.h \ + H5FDlog.h H5FDmirror.h H5FDmpi.h H5FDmpio.h H5FDmulti.h H5FDros3.h \ + H5FDsec2.h H5FDsplitter.h H5FDstdio.h H5FDwindows.h \ H5Gpublic.h H5Ipublic.h H5Lpublic.h \ H5Mpublic.h H5MMpublic.h H5Opublic.h H5Ppublic.h \ H5PLextern.h H5PLpublic.h \ diff --git a/src/hdf5.h b/src/hdf5.h index 8367122..eaaf8ae 100644 --- a/src/hdf5.h +++ b/src/hdf5.h @@ -46,10 +46,12 @@ #include "H5FDfamily.h" /* File families */ #include "H5FDhdfs.h" /* Hadoop HDFS */ #include "H5FDlog.h" /* sec2 driver with I/O logging (for debugging) */ +#include "H5FDmirror.h" /* Mirror VFD and IPC definitions */ #include "H5FDmpi.h" /* MPI-based file drivers */ #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/src/libhdf5.settings.in b/src/libhdf5.settings.in index 1591bed..df7ddf2 100644 --- a/src/libhdf5.settings.in +++ b/src/libhdf5.settings.in @@ -80,6 +80,7 @@ Parallel Filtered Dataset Writes: @PARALLEL_FILTERED_WRITES@ MPE: @MPE@ Map (H5M) API: @MAP_API@ Direct VFD: @DIRECT_VFD@ + Mirror VFD: @MIRROR_VFD@ (Read-Only) S3 VFD: @ROS3_VFD@ (Read-Only) HDFS VFD: @HAVE_LIBHDFS@ dmalloc: @HAVE_DMALLOC@ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2594477..cba1d3a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -211,6 +211,11 @@ set (cache_image_SOURCES ${HDF5_TEST_SOURCE_DIR}/genall5.c ) +set(mirror_vfd_SOURCES + ${HDF5_TEST_SOURCE_DIR}/mirror_vfd.c + ${HDF5_TEST_SOURCE_DIR}/genall5.c +) + set (ttsafe_SOURCES ${HDF5_TEST_SOURCE_DIR}/ttsafe.c ${HDF5_TEST_SOURCE_DIR}/ttsafe_dcreate.c @@ -273,6 +278,7 @@ set (H5_TESTS ros3 s3comms hdfs + mirror_vfd ntypes dangle dtransform @@ -310,6 +316,7 @@ set (H5_TESTS_MULTIPLE testhdf5 cache_image ttsafe + mirror_vfd ) # Only build single source tests here foreach (h5_test ${H5_TESTS}) @@ -389,6 +396,18 @@ else () endif () set_target_properties (ttsafe PROPERTIES FOLDER test) +#-- Adding test for mirror_vfd +add_executable (mirror_vfd ${mirror_vfd_SOURCES}) +target_include_directories (mirror_vfd PRIVATE "${HDF5_SRC_DIR};${HDF5_BINARY_DIR};$<$:${MPI_C_INCLUDE_DIRS}>") +if (NOT BUILD_SHARED_LIBS) + TARGET_C_PROPERTIES (mirror_vfd STATIC) + target_link_libraries (mirror_vfd PRIVATE ${HDF5_TEST_LIB_TARGET}) +else () + TARGET_C_PROPERTIES (mirror_vfd SHARED) + target_link_libraries (mirror_vfd PRIVATE ${HDF5_TEST_LIBSH_TARGET}) +endif () +set_target_properties (mirror_vfd PROPERTIES FOLDER test) + ############################################################################## ### A D D I T I O N A L T E S T S ### ############################################################################## diff --git a/test/CMakeTests.cmake b/test/CMakeTests.cmake index b7eaa56..c73aeda 100644 --- a/test/CMakeTests.cmake +++ b/test/CMakeTests.cmake @@ -446,6 +446,10 @@ set (test_CLEANFILES vds_swmr_src_*.h5 tmp_vds_env/vds_src_2.h5 direct_chunk.h5 + splitter*.h5 + splitter.log + mirror_rw/* + mirror_wo/* native_vol_test.h5 ) diff --git a/test/Makefile.am b/test/Makefile.am index dd0a579..6bea16a 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -33,9 +33,11 @@ AM_CPPFLAGS+=-I$(top_srcdir)/src -I$(top_builddir)/src # testvdsswmr.sh: vds_swmr* # testabort_fail.sh: filenotclosed.c and del_many_dense_attrs.c # test_filter_plugin.sh: filter_plugin.c +# test_mirror.sh: mirror_vfd ../utils/mirror_vfd/* # test_usecases.sh: use_append_chunk, use_append_mchunks, use_disable_mdc_flushes TEST_SCRIPT = testerror.sh testlibinfo.sh testcheck_version.sh testlinks_env.sh testexternal_env.sh \ - testswmr.sh testvds_env.sh testvdsswmr.sh testflushrefresh.sh test_usecases.sh testabort_fail.sh + testswmr.sh testvds_env.sh testvdsswmr.sh testflushrefresh.sh test_usecases.sh testabort_fail.sh \ + test_mirror.sh SCRIPT_DEPEND = error_test$(EXEEXT) err_compat$(EXEEXT) links_env$(EXEEXT) \ external_env$(EXEEXT) filenotclosed$(EXEEXT) del_many_dense_attrs$(EXEEXT) \ flushrefresh$(EXEEXT) use_append_chunk$(EXEEXT) use_append_mchunks$(EXEEXT) use_disable_mdc_flushes$(EXEEXT) \ @@ -78,16 +80,18 @@ TEST_PROG= testhdf5 \ # swmr_* files (besides swmr.c) are used by testswmr.sh. # vds_swmr_* files are used by testvdsswmr.sh # vds_env is used by testvds_env.sh +# mirror_vfd is used by test_mirror.sh # 'make check' doesn't run them directly, so they are not included in TEST_PROG. # Also build testmeta, which is used for timings test. It builds quickly, # and this lets automake keep all its test programs in one place. check_PROGRAMS=$(TEST_PROG) error_test err_compat tcheck_version \ testmeta accum_swmr_reader atomic_writer atomic_reader external_env \ links_env filenotclosed del_many_dense_attrs flushrefresh \ - use_append_chunk use_append_mchunks use_disable_mdc_flushes \ + use_append_chunk use_append_chunk_mirror use_append_mchunks use_disable_mdc_flushes \ swmr_generator swmr_start_write swmr_reader swmr_writer swmr_remove_reader \ swmr_remove_writer swmr_addrem_writer swmr_sparse_reader swmr_sparse_writer \ - swmr_check_compat_vfd vds_env vds_swmr_gen vds_swmr_reader vds_swmr_writer + swmr_check_compat_vfd vds_env vds_swmr_gen vds_swmr_reader vds_swmr_writer \ + mirror_vfd if HAVE_SHARED_CONDITIONAL check_PROGRAMS+= filter_plugin vol_plugin endif @@ -144,6 +148,7 @@ LDADD=libh5test.la $(LIBHDF5) ttsafe_SOURCES=ttsafe.c ttsafe_dcreate.c ttsafe_error.c ttsafe_cancel.c \ ttsafe_acreate.c cache_image_SOURCES=cache_image.c genall5.c +mirror_vfd_SOURCES=mirror_vfd.c genall5.c VFD_LIST = sec2 stdio core core_paged split multi family if DIRECT_VFD_CONDITIONAL @@ -213,7 +218,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 native_vol_test.h5 + tbogus.h5.copy cache_image_test.h5 direct_chunk.h5 native_vol_test.h5 \ + splitter*.h5 splitter.log # Sources for testhdf5 executable testhdf5_SOURCES=testhdf5.c tarray.c tattr.c tchecksum.c tconfig.c tfile.c \ @@ -223,12 +229,13 @@ testhdf5_SOURCES=testhdf5.c tarray.c tattr.c tchecksum.c tconfig.c tfile.c \ # Sources for Use Cases use_append_chunk_SOURCES=use_append_chunk.c use_common.c +use_append_chunk_mirror_SOURCES=use_append_chunk_mirror.c use_common.c use_append_mchunks_SOURCES=use_append_mchunks.c use_common.c use_disable_mdc_flushes_SOURCES=use_disable_mdc_flushes.c # Temporary files. DISTCLEANFILES=testerror.sh testlibinfo.sh testcheck_version.sh testlinks_env.sh test_filter_plugin.sh \ - testexternal_env.sh testswmr.sh testvds_env.sh testvdsswmr.sh test_usecases.sh testflushrefresh.sh testabort_fail.sh \ - test_vol_plugin.sh + testexternal_env.sh testswmr.sh testvds_env.sh testvdsswmr.sh test_usecases.sh testflushrefresh.sh \ + testabort_fail.sh test_vol_plugin.sh test_mirror.sh include $(top_srcdir)/config/conclude.am diff --git a/test/h5test.c b/test/h5test.c index 03731c6..e7d91b6 100644 --- a/test/h5test.c +++ b/test/h5test.c @@ -2043,6 +2043,80 @@ 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; + hsize_t f1size = 0; /* size of the files */ + hsize_t f2size = 0; + char f1char = 0; /* one char from each file */ + char f2char = 0; + hsize_t ii = 0; + int ret_value = 0; /* for error handling */ + + /* Open files for reading */ + f1ptr = fopen(f1name, "r"); + if (f1ptr == NULL) { + HDfprintf(stderr, "Unable to fopen() %s\n", f1name); + ret_value = -1; + goto done; + } + f2ptr = fopen(f2name, "r"); + 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 */ + fseek(f1ptr , 0 , SEEK_END); + f1size = ftell(f1ptr); + + fseek(f2ptr , 0 , SEEK_END); + f2size = ftell(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 */ + rewind(f1ptr); + rewind(f2ptr); + for (ii = 0; ii < f1size; ii++) { + fread(&f1char, 1, 1, f1ptr); + fread(&f2char, 1, 1, f2ptr); + if (f1char != f2char) { + HDfprintf(stderr, "Mismatch @ 0x%llX: 0x%X != 0x%X\n", ii, f1char, f2char); + ret_value = -1; + goto done; + } + } + +done: + if (f1ptr) { + fclose(f1ptr); + } + if (f2ptr) { + fclose(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 @@ -2094,3 +2168,4 @@ const char *H5_get_srcdir(void) else return(NULL); } /* end H5_get_srcdir() */ + diff --git a/test/h5test.h b/test/h5test.h index e67e559..39ec9d7 100644 --- a/test/h5test.h +++ b/test/h5test.h @@ -150,6 +150,7 @@ 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 H5VL_class_t *h5_get_dummy_vol_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); /* Functions that will replace components of a FAPL */ H5TEST_DLL herr_t h5_get_vfd_fapl(hid_t fapl_id); diff --git a/test/mirror_vfd.c b/test/mirror_vfd.c new file mode 100644 index 0000000..00902dd --- /dev/null +++ b/test/mirror_vfd.c @@ -0,0 +1,2736 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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: Test the Mirror VFD functionality. + */ + +/* WARNING: The use of realpath() is probably system-dependent, as are + * other things here such as the socket calls. + * Notable to realpath() in particular is the use of "PATH_MAX", which + * apparently has some major potential issues if paths are abused. + * http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html + * so BE CAREFUL about the paths we throw around? + */ + +#include "h5test.h" +#include "cache_common.h" +#include "genall5.h" + +#ifdef H5_HAVE_MIRROR_VFD + +/* For future consideration, IP address and port number might be + * environment variables? + */ +#define SERVER_IP_ADDRESS "127.0.0.1" + +/* Primary listening port on server. */ +#define SERVER_HANDSHAKE_PORT 3000 + +#define DATABUFFER_SIZE 128 +#define DSET_NAME_LEN 16 + +/* Parameters for the "large chunked dataset" writing */ +#define MAX_DSET_COUNT 255 +#define DSET_DIM 32 +#define CHUNK_DIM 8 + +#define CONCURRENT_COUNT 3 /* Number of files in concurrent test */ + +/* Macro: LOGPRINT() + * Prints logging and debugging messages to the output stream based + * on the level of verbosity. + * 0 : no logging + * 1 : errors only + * 2 : details + * 3 : all + */ +#define DEFAULT_VERBOSITY 1 +static unsigned int g_verbosity = DEFAULT_VERBOSITY; + +/* Macro for selective debug printing / logging */ +#define LOGPRINT(lvl, ...) \ +do { \ + if ((lvl) <= g_verbosity) { \ + fprintf(g_log_stream, __VA_ARGS__); \ + fflush(g_log_stream); \ + } \ +} while (0) + +#define MIRROR_RW_DIR "mirror_rw/" +#define MIRROR_WO_DIR "mirror_wo/" + +/* String buffer for error messages */ +#define MIRR_MESG_SIZE 128 +static char mesg[MIRR_MESG_SIZE + 1]; + +/* Convenience structure for passing file names via helper functions. + */ +struct mirrortest_filenames { + char rw[H5FD_SPLITTER_PATH_MAX+1]; + char wo[H5FD_SPLITTER_PATH_MAX+1]; + char log[H5FD_SPLITTER_PATH_MAX+1]; +}; + +static FILE *g_log_stream = NULL; /* initialized at runtime */ + +static herr_t _verify_datasets(unsigned min_dset, unsigned max_dset, + hid_t *filespace_id, hid_t *dataset_id, hid_t memspace_id); + +static herr_t _create_chunking_ids(hid_t file_id, unsigned min_dset, + unsigned max_dset, hsize_t *chunk_dims, hsize_t *dset_dims, + hid_t *dataspace_ids, hid_t *filespace_ids, hid_t *dataset_ids, + hid_t *memspace_id); + +static herr_t _close_chunking_ids(unsigned min_dset, unsigned max_dset, + hid_t *dataspace_ids, hid_t *filespace_ids, hid_t *dataset_ids, + hid_t *memspace_id); + +static herr_t _populate_filepath(const char *dirname, const char *_basename, + hid_t fapl_id, char *path_out, hbool_t h5suffix); + +static hid_t create_mirroring_split_fapl(const char *_basename, + struct mirrortest_filenames *names); + + +/* ---------------------------------------------------------------------------- + * Function: _populate_filepath + * + * Purpose: Given a directory name and a base name, concatenate the two and + * run h5fixname() to get the "actual" path to the intented target. + * `h5suffix' should be FALSE to keep the base name unaltered; + * TRUE will append the '.h5' h5suffix to the basename... + * FALSE -> h5fixname_no_suffix(), TRUE -> h5fixname() + * / / <_basename> + * + * Programmer: Jacob Smith + * 2019-08-16 + * ---------------------------------------------------------------------------- + */ +static herr_t +_populate_filepath(const char *dirname, const char *_basename, hid_t fapl_id, + char *path_out, hbool_t h5suffix) +{ + char _path[H5FD_SPLITTER_PATH_MAX]; + + if ((_basename == NULL) || + (*_basename == 0) || + (dirname == NULL) || + (*dirname == 0) || + (path_out == NULL)) + { + TEST_ERROR; + } + + if (HDsnprintf(_path, H5FD_SPLITTER_PATH_MAX, "%s%s%s", dirname, + (dirname[strlen(dirname)] == '/') ? "" : "/", /* slash iff needed */ + _basename) + > H5FD_SPLITTER_PATH_MAX) + { + TEST_ERROR; + } + + if (h5suffix == TRUE) { + if (h5_fixname(_path, fapl_id, path_out, + H5FD_SPLITTER_PATH_MAX) + == NULL) + { + TEST_ERROR; + } + } + else { + if (h5_fixname_no_suffix(_path, fapl_id, path_out, + H5FD_SPLITTER_PATH_MAX) + == NULL) + { + TEST_ERROR; + } + } + + return SUCCEED; + +error: + return FAIL; +} /* end _populate_filepath() */ + + +/* --------------------------------------------------------------------------- + * Function: build_paths + * + * Purpose: Convenience function to create the three file paths used in + * most mirror tests. + * + * Return: SUCCEED/FAIL + * + * Programmer: Jacob Smith + * 2019-08-16 + * --------------------------------------------------------------------------- + */ +static herr_t +build_paths( + const char *_basename, + H5FD_splitter_vfd_config_t *splitter_config, + struct mirrortest_filenames *names) +{ + char baselogname[H5FD_SPLITTER_PATH_MAX]; + + if (_populate_filepath(MIRROR_RW_DIR, _basename, + splitter_config->rw_fapl_id, names->rw, TRUE) + == FAIL) + { + TEST_ERROR; + } + + if (_populate_filepath(MIRROR_WO_DIR, _basename, + splitter_config->wo_fapl_id, names->wo, TRUE) + == FAIL) + { + TEST_ERROR; + } + + if (_basename == NULL || *_basename == 0) + return FAIL; + if (HDsnprintf(baselogname, H5FD_SPLITTER_PATH_MAX, "%s_err.log", + _basename) + > H5FD_SPLITTER_PATH_MAX) + { + TEST_ERROR; + } + + if (_populate_filepath(MIRROR_WO_DIR, baselogname, + splitter_config->wo_fapl_id, names->log, FALSE) + == FAIL) + { + TEST_ERROR; + } + + return SUCCEED; + +error: + return FAIL; +} /* end build_paths() */ + + +/* --------------------------------------------------------------------------- + * Function: test_fapl_configuration + * + * Purpose: Test FAPL configuration and examination. + * + * Return: Success: 0 + * Failure: -1 + * + * Programmer: Jacob Smith + * 2019-03-12 + * --------------------------------------------------------------------------- + */ +static int +test_fapl_configuration(void) +{ + hid_t fapl_id; + H5FD_mirror_fapl_t mirror_conf = { + H5FD_MIRROR_FAPL_MAGIC, /* magic */ + H5FD_MIRROR_CURR_FAPL_T_VERSION, /* version */ + SERVER_HANDSHAKE_PORT, /* handhake_port */ + SERVER_IP_ADDRESS, /* remote_ip "IP address" */ + }; + H5FD_mirror_fapl_t fa_out = {0, 0, 0, ""}; + + TESTING("Mirror fapl configuration (set/get)"); + + fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == fapl_id) { + TEST_ERROR; + } + + if (H5Pset_fapl_mirror(fapl_id, &mirror_conf) == FAIL) { + TEST_ERROR; + } + + if (H5Pget_fapl_mirror(fapl_id, &fa_out) == FAIL) { + TEST_ERROR; + } + if (H5FD_MIRROR_FAPL_MAGIC != fa_out.magic) { + TEST_ERROR; + } + if (H5FD_MIRROR_CURR_FAPL_T_VERSION != fa_out.version) { + TEST_ERROR; + } + if (SERVER_HANDSHAKE_PORT != fa_out.handshake_port) { + TEST_ERROR; + } + if (HDstrncmp(SERVER_IP_ADDRESS, (const char *)fa_out.remote_ip, + H5FD_MIRROR_MAX_IP_LEN)) + { + TEST_ERROR; + } + + if (H5Pclose(fapl_id) == FAIL) { + TEST_ERROR; + } + + PASSED(); + return 0; + +error: + if (H5I_INVALID_HID != fapl_id) { + (void)H5Pclose(fapl_id); + } + return -1; +} /* end test_fapl_configuration() */ + + + +#define PRINT_BUFFER_DIFF(act, exp, len) do { \ + size_t _x = 0; \ + while ((act)[_x] == (exp)[_x]) { \ + _x++; \ + } \ + if (_x != (len)) { \ + size_t _y = 0; \ + HDprintf("First bytes differ at %zu\n", _x); \ + HDprintf("exp "); \ + for (_y = _x; _y < (len); _y++) { \ + HDprintf("%02X", (unsigned char)(exp)[_y]); \ + } \ + HDprintf("\nact "); \ + for (_y = _x; _y < (len); _y++) { \ + HDprintf("%02X", (unsigned char)(act)[_y]); \ + } \ + HDprintf("\n"); \ + } \ +} while (0); /* end PRINT_BUFFER_DIFF */ + + +/* --------------------------------------------------------------------------- + * Function: test_xmit_encode_decode + * + * Purpose: Test byte-encoding operations for network transport. + * + * Return: Success: 0 + * Failure: -1 + * + * Programmer: Jacob Smith + * 2020-02-02 + * --------------------------------------------------------------------------- + */ +static int +test_xmit_encode_decode(void) +{ + H5FD_mirror_xmit_t xmit_mock; /* re-used header in various xmit tests */ + + TESTING("Mirror encode/decode of xmit elements"); + + /* Set bogus values matching expected; encoding doesn't care + * Use sequential values to easily generate the expected buffer with a + * for loop. + */ + xmit_mock.magic = 0x00010203; + xmit_mock.version = 0x04; + xmit_mock.session_token = 0x05060708; + xmit_mock.xmit_count = 0x090A0B0C; + xmit_mock.op = 0x0D; + + /* Test uint8_t encode/decode + */ + do { + unsigned char buf[8]; + unsigned char expected[8]; + const uint8_t v = 200; + unsigned char out = 0; + + /* Start of buffer uint8_t + */ + HDbzero(buf, 8); + HDbzero(expected, 8); + expected[0] = 200; + out = 0; + if (H5FD__mirror_xmit_encode_uint8(buf, v) != 1) { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, 8) != 0) { + PRINT_BUFFER_DIFF(buf, expected, 8); + TEST_ERROR; + } + if (H5FD__mirror_xmit_decode_uint8(&out, buf) != 1) { + TEST_ERROR; + } + if (v != out) { + TEST_ERROR; + } + + /* Middle of buffer uint8_t + */ + HDbzero(buf, 8); + HDbzero(expected, 8); + expected[3] = v; + out = 0; + if (H5FD__mirror_xmit_encode_uint8((buf+3), v) != 1) { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, 8) != 0) { + PRINT_BUFFER_DIFF(buf, expected, 8); + TEST_ERROR; + } + if (H5FD__mirror_xmit_decode_uint8(&out, (buf+3)) != 1) { + TEST_ERROR; + } + if (v != out) { + TEST_ERROR; + } + + /* End of buffer uint8_t + */ + HDbzero(buf, 8); + HDbzero(expected, 8); + expected[7] = v; + out = 0; + if (H5FD__mirror_xmit_encode_uint8((buf+7), v) != 1) { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, 8) != 0) { + PRINT_BUFFER_DIFF(buf, expected, 8); + TEST_ERROR; + } + if (H5FD__mirror_xmit_decode_uint8(&out, (buf+7)) != 1) { + TEST_ERROR; + } + if (v != out) { + TEST_ERROR; + } + + } while (0); /* end uint8_t en/decode */ + + /* Test uint16_t encode/decode + */ + do { + unsigned char buf[8]; + unsigned char expected[8]; + const uint16_t v = 0x8F02; + uint16_t out = 0; + + /* Start of buffer uint16_t + */ + HDbzero(buf, 8); + HDbzero(expected, 8); + expected[0] = 0x8F; + expected[1] = 0x02; + out = 0; + if (H5FD__mirror_xmit_encode_uint16(buf, v) != 2) { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, 8) != 0) { + PRINT_BUFFER_DIFF(buf, expected, 8); + TEST_ERROR; + } + if (H5FD__mirror_xmit_decode_uint16(&out, buf) != 2) { + TEST_ERROR; + } + if (out != v) { + TEST_ERROR; + } + + /* Middle of buffer uint16_t + */ + HDbzero(buf, 8); + HDbzero(expected, 8); + expected[3] = 0x8F; + expected[4] = 0x02; + out = 0; + if (H5FD__mirror_xmit_encode_uint16((buf+3), v) != 2) { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, 8) != 0) { + PRINT_BUFFER_DIFF(buf, expected, 8); + TEST_ERROR; + } + if (H5FD__mirror_xmit_decode_uint16(&out, (buf+3)) != 2) { + TEST_ERROR; + } + if (out != v) { + TEST_ERROR; + } + /* slice */ + if (H5FD__mirror_xmit_decode_uint16(&out, (buf+4)) != 2) { + TEST_ERROR; + } + if (out != 0x0200) { + TEST_ERROR; + } + + /* End of buffer uint16_t + */ + HDbzero(buf, 8); + HDbzero(expected, 8); + expected[6] = 0x8F; + expected[7] = 0x02; + out = 0; + if (H5FD__mirror_xmit_encode_uint16((buf+6), v) != 2) { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, 8) != 0) { + PRINT_BUFFER_DIFF(buf, expected, 8); + TEST_ERROR; + } + if (H5FD__mirror_xmit_decode_uint16(&out, (buf+6)) != 2) { + TEST_ERROR; + } + if (out != v) { + TEST_ERROR; + } + + } while (0); /* end uint16_t en/decode */ + + /* Test uint32_t encode/decode + */ + do { + unsigned char buf[8]; + unsigned char expected[8]; + const uint32_t v = 0x8F020048; + uint32_t out = 0; + + /* Start of buffer uint32_t + */ + HDbzero(buf, 8); + HDbzero(expected, 8); + expected[0] = 0x8F; + expected[1] = 0x02; + expected[2] = 0x00; + expected[3] = 0x48; + out = 0; + if (H5FD__mirror_xmit_encode_uint32(buf, v) != 4) { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, 8) != 0) { + PRINT_BUFFER_DIFF(buf, expected, 8); + TEST_ERROR; + } + if (H5FD__mirror_xmit_decode_uint32(&out, buf) != 4) { + TEST_ERROR; + } + if (out != v) { + TEST_ERROR; + } + + /* Middle of buffer uint32_t + */ + HDbzero(buf, 8); + HDbzero(expected, 8); + expected[3] = 0x8F; + expected[4] = 0x02; + expected[5] = 0x00; + expected[6] = 0x48; + out = 0; + if (H5FD__mirror_xmit_encode_uint32((buf+3), v) != 4) { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, 8) != 0) { + PRINT_BUFFER_DIFF(buf, expected, 8); + TEST_ERROR; + } + if (H5FD__mirror_xmit_decode_uint32(&out, (buf+3)) != 4) { + TEST_ERROR; + } + if (out != v) { + TEST_ERROR; + } + /* slice */ + if (H5FD__mirror_xmit_decode_uint32(&out, (buf+4)) != 4) { + TEST_ERROR; + } + if (out != 0x02004800) { + TEST_ERROR; + } + + /* End of buffer uint32_t + */ + HDbzero(buf, 8); + HDbzero(expected, 8); + expected[4] = 0x8F; + expected[5] = 0x02; + expected[6] = 0x00; + expected[7] = 0x48; + out = 0; + if (H5FD__mirror_xmit_encode_uint32((buf+4), v) != 4) { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, 8) != 0) { + PRINT_BUFFER_DIFF(buf, expected, 8); + TEST_ERROR; + } + if (H5FD__mirror_xmit_decode_uint32(&out, (buf+4)) != 4) { + TEST_ERROR; + } + if (out != v) { + TEST_ERROR; + } + + } while (0); /* end uint32_t en/decode */ + + /* Test uint64_t encode/decode + */ + do { + unsigned char buf[16]; + unsigned char expected[16]; + const uint64_t v = 0x90DCBE17939CE4BB; + uint64_t out = 0; + + /* Start of buffer uint64_t + */ + HDbzero(buf, 16); + HDbzero(expected, 16); + expected[0] = 0x90; + expected[1] = 0xDC; + expected[2] = 0xBE; + expected[3] = 0x17; + expected[4] = 0x93; + expected[5] = 0x9C; + expected[6] = 0xE4; + expected[7] = 0xBB; + out = 0; + if (H5FD__mirror_xmit_encode_uint64(buf, v) != 8) { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, 16) != 0) { + PRINT_BUFFER_DIFF(buf, expected, 16); + TEST_ERROR; + } + if (H5FD__mirror_xmit_decode_uint64(&out, buf) != 8) { + TEST_ERROR; + } + if (out != v) { + TEST_ERROR; + } + + /* Middle of buffer uint64_t + */ + HDbzero(buf, 16); + HDbzero(expected, 16); + expected[3] = 0x90; + expected[4] = 0xDC; + expected[5] = 0xBE; + expected[6] = 0x17; + expected[7] = 0x93; + expected[8] = 0x9C; + expected[9] = 0xE4; + expected[10] = 0xBB; + out = 0; + if (H5FD__mirror_xmit_encode_uint64((buf+3), v) != 8) { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, 16) != 0) { + PRINT_BUFFER_DIFF(buf, expected, 16); + TEST_ERROR; + } + if (H5FD__mirror_xmit_decode_uint64(&out, (buf+3)) != 8) { + TEST_ERROR; + } + if (out != v) { + TEST_ERROR; + } + /* slice */ + if (H5FD__mirror_xmit_decode_uint64(&out, (buf+6)) != 8) { + TEST_ERROR; + } + if (out != 0x17939CE4BB000000) { + TEST_ERROR; + } + + /* End of buffer uint64_t + */ + HDbzero(buf, 16); + HDbzero(expected, 16); + expected[8] = 0x90; + expected[9] = 0xDC; + expected[10] = 0xBE; + expected[11] = 0x17; + expected[12] = 0x93; + expected[13] = 0x9C; + expected[14] = 0xE4; + expected[15] = 0xBB; + out = 0; + if (H5FD__mirror_xmit_encode_uint64((buf+8), v) != 8) { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, 16) != 0) { + PRINT_BUFFER_DIFF(buf, expected, 16); + TEST_ERROR; + } + if (H5FD__mirror_xmit_decode_uint64(&out, (buf+8)) != 8) { + TEST_ERROR; + } + if (out != v) { + TEST_ERROR; + } + + } while (0); /* end uint64_t en/decode */ + + /* Test xmit header structure encode/decode + * Write bogus but easily verifiable data to inside a buffer, and compare. + * Then decode the buffer and compare the structure contents. + * Then repeat from a different offset in the buffer and compare. + */ + do { + unsigned char buf[H5FD_MIRROR_XMIT_HEADER_SIZE+8]; + unsigned char expected[H5FD_MIRROR_XMIT_HEADER_SIZE+8]; + H5FD_mirror_xmit_t xmit_out; + size_t i = 0; + + /* sanity check */ + if (14 != H5FD_MIRROR_XMIT_HEADER_SIZE) { + FAIL_PUTS_ERROR("Header size definition does not match test\n"); + } + + /* Populate the expected buffer; expect end padding of 0xFF + */ + HDmemset(expected, 0xFF, H5FD_MIRROR_XMIT_HEADER_SIZE+8); + for (i=0; i < H5FD_MIRROR_XMIT_HEADER_SIZE; i++) { + expected[i+2] = (unsigned char)i; + } + + /* Encode, and compare buffer contents + * Initial buffer is filled with 0xFF to match expected padding + */ + HDmemset(buf, 0xFF, H5FD_MIRROR_XMIT_HEADER_SIZE+8); + if (H5FD_mirror_xmit_encode_header((buf+2), &xmit_mock) + != H5FD_MIRROR_XMIT_HEADER_SIZE) + { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, H5FD_MIRROR_XMIT_HEADER_SIZE+8) != 0) { + PRINT_BUFFER_DIFF(buf, expected, H5FD_MIRROR_XMIT_HEADER_SIZE+8); + TEST_ERROR; + } + + /* Decode from buffer + */ + if (H5FD_mirror_xmit_decode_header(&xmit_out, (buf+2)) + != H5FD_MIRROR_XMIT_HEADER_SIZE) + { + TEST_ERROR; + } + if (xmit_out.magic != xmit_mock.magic) TEST_ERROR; + if (xmit_out.version != xmit_mock.version) TEST_ERROR; + if (xmit_out.session_token != xmit_mock.session_token) TEST_ERROR; + if (xmit_out.xmit_count != xmit_mock.xmit_count) TEST_ERROR; + if (xmit_out.op != xmit_mock.op) TEST_ERROR; + + /* Decode from different offset in buffer + * Observe changes when ingesting the padding + */ + if (H5FD_mirror_xmit_decode_header(&xmit_out, (buf)) + != H5FD_MIRROR_XMIT_HEADER_SIZE) + { + TEST_ERROR; + } + if (xmit_out.magic != 0xFFFF0001) TEST_ERROR; + if (xmit_out.version != 0x02) TEST_ERROR; + if (xmit_out.session_token != 0x03040506) TEST_ERROR; + if (xmit_out.xmit_count != 0x0708090A) TEST_ERROR; + if (xmit_out.op != 0x0B) TEST_ERROR; + + } while (0); /* end xmit header en/decode */ + + /* Test xmit set-eoa structure encode/decode + * Write bogus but easily verifiable data to inside a buffer, and compare. + * Then decode the buffer and compare the structure contents. + * Then repeat from a different offset in the buffer and compare. + */ + do { + unsigned char buf[H5FD_MIRROR_XMIT_EOA_SIZE+8]; + unsigned char expected[H5FD_MIRROR_XMIT_EOA_SIZE+8]; + H5FD_mirror_xmit_eoa_t xmit_in; + H5FD_mirror_xmit_eoa_t xmit_out; + size_t i = 0; + + /* sanity check */ + if ((14+9) != H5FD_MIRROR_XMIT_EOA_SIZE) { + FAIL_PUTS_ERROR("Header size definition does not match test\n"); + } + if (xmit_mock.op != 0x0D) { + FAIL_PUTS_ERROR("shared header structure is not in expected state"); + } + + /* Populate the expected buffer; expect end padding of 0xFF + */ + HDmemset(expected, 0xFF, H5FD_MIRROR_XMIT_EOA_SIZE+8); + for (i=0; i < H5FD_MIRROR_XMIT_EOA_SIZE; i++) { + expected[i+2] = (unsigned char)i; + } + + /* Set xmit_in + */ + xmit_in.pub = xmit_mock; /* shared/common */ + xmit_in.type = 0x0E; + xmit_in.eoa_addr = 0x0F10111213141516; + + /* Encode, and compare buffer contents + * Initial buffer is filled with 0xFF to match expected padding + */ + HDmemset(buf, 0xFF, H5FD_MIRROR_XMIT_EOA_SIZE+8); + if (H5FD_mirror_xmit_encode_set_eoa((buf+2), &xmit_in) + != H5FD_MIRROR_XMIT_EOA_SIZE) + { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, H5FD_MIRROR_XMIT_EOA_SIZE+8) != 0) { + PRINT_BUFFER_DIFF(buf, expected, H5FD_MIRROR_XMIT_EOA_SIZE+8); + TEST_ERROR; + } + + /* Decode from buffer + */ + if (H5FD_mirror_xmit_decode_set_eoa(&xmit_out, (buf+2)) + != H5FD_MIRROR_XMIT_EOA_SIZE) + { + TEST_ERROR; + } + if (xmit_out.pub.magic != xmit_mock.magic) TEST_ERROR; + if (xmit_out.pub.version != xmit_mock.version) TEST_ERROR; + if (xmit_out.pub.session_token != xmit_mock.session_token) TEST_ERROR; + if (xmit_out.pub.xmit_count != xmit_mock.xmit_count) TEST_ERROR; + if (xmit_out.pub.op != xmit_mock.op) TEST_ERROR; + if (xmit_out.type != 0x0E) TEST_ERROR; + if (xmit_out.eoa_addr != 0x0F10111213141516) TEST_ERROR; + + /* Decode from different offset in buffer + * Observe changes when ingesting the padding + */ + if (H5FD_mirror_xmit_decode_set_eoa(&xmit_out, (buf)) + != H5FD_MIRROR_XMIT_EOA_SIZE) + { + TEST_ERROR; + } + if (xmit_out.pub.magic != 0xFFFF0001) TEST_ERROR; + if (xmit_out.pub.version != 0x02) TEST_ERROR; + if (xmit_out.pub.session_token != 0x03040506) TEST_ERROR; + if (xmit_out.pub.xmit_count != 0x0708090A) TEST_ERROR; + if (xmit_out.pub.op != 0x0B) TEST_ERROR; + if (xmit_out.type != 0x0C) TEST_ERROR; + if (xmit_out.eoa_addr != 0x0D0E0F1011121314) TEST_ERROR; + + } while (0); /* end xmit set-eoa en/decode */ + + /* Test xmit lock structure encode/decode + * Write bogus but easily verifiable data to inside a buffer, and compare. + * Then decode the buffer and compare the structure contents. + * Then repeat from a different offset in the buffer and compare. + */ + do { + unsigned char buf[H5FD_MIRROR_XMIT_LOCK_SIZE+8]; + unsigned char expected[H5FD_MIRROR_XMIT_LOCK_SIZE+8]; + H5FD_mirror_xmit_lock_t xmit_in; + H5FD_mirror_xmit_lock_t xmit_out; + size_t i = 0; + + /* sanity check */ + if ((14+8) != H5FD_MIRROR_XMIT_LOCK_SIZE) { + FAIL_PUTS_ERROR("Header size definition does not match test\n"); + } + if (xmit_mock.op != 0x0D) { + FAIL_PUTS_ERROR("shared header structure is not in expected state"); + } + + /* Populate the expected buffer; expect end padding of 0xFF + */ + HDmemset(expected, 0xFF, H5FD_MIRROR_XMIT_LOCK_SIZE+8); + for (i=0; i < H5FD_MIRROR_XMIT_LOCK_SIZE; i++) { + expected[i+2] = (unsigned char)i; + } + + /* Set xmit_in + */ + xmit_in.pub = xmit_mock; /* shared/common */ + xmit_in.rw = 0x0E0F101112131415; + + /* Encode, and compare buffer contents + * Initial buffer is filled with 0xFF to match expected padding + */ + HDmemset(buf, 0xFF, H5FD_MIRROR_XMIT_LOCK_SIZE+8); + if (H5FD_mirror_xmit_encode_lock((buf+2), &xmit_in) + != H5FD_MIRROR_XMIT_LOCK_SIZE) + { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, H5FD_MIRROR_XMIT_LOCK_SIZE+8) != 0) { + PRINT_BUFFER_DIFF(buf, expected, H5FD_MIRROR_XMIT_LOCK_SIZE+8); + TEST_ERROR; + } + + /* Decode from buffer + */ + if (H5FD_mirror_xmit_decode_lock(&xmit_out, (buf+2)) + != H5FD_MIRROR_XMIT_LOCK_SIZE) + { + TEST_ERROR; + } + if (xmit_out.pub.magic != xmit_mock.magic) TEST_ERROR; + if (xmit_out.pub.version != xmit_mock.version) TEST_ERROR; + if (xmit_out.pub.session_token != xmit_mock.session_token) TEST_ERROR; + if (xmit_out.pub.xmit_count != xmit_mock.xmit_count) TEST_ERROR; + if (xmit_out.pub.op != xmit_mock.op) TEST_ERROR; + if (xmit_out.rw != 0x0E0F101112131415) TEST_ERROR; + + /* Decode from different offset in buffer + * Observe changes when ingesting the padding + */ + if (H5FD_mirror_xmit_decode_lock(&xmit_out, (buf)) + != H5FD_MIRROR_XMIT_LOCK_SIZE) + { + TEST_ERROR; + } + if (xmit_out.pub.magic != 0xFFFF0001) TEST_ERROR; + if (xmit_out.pub.version != 0x02) TEST_ERROR; + if (xmit_out.pub.session_token != 0x03040506) TEST_ERROR; + if (xmit_out.pub.xmit_count != 0x0708090A) TEST_ERROR; + if (xmit_out.pub.op != 0x0B) TEST_ERROR; + if (xmit_out.rw != 0x0C0D0E0F10111213) TEST_ERROR; + + } while (0); /* end xmit lock en/decode */ + + /* Test xmit open structure encode/decode + * Write bogus but easily verifiable data to inside a buffer, and compare. + * Then decode the buffer and compare the structure contents. + * Then repeat from a different offset in the buffer and compare. + * + * Verifies that the first zero character in the filepath will end the + * string, with all following bytes in the encoded buffer being zeroed. + */ + do { + unsigned char buf[H5FD_MIRROR_XMIT_OPEN_SIZE+8]; + unsigned char expected[H5FD_MIRROR_XMIT_OPEN_SIZE+8]; + H5FD_mirror_xmit_open_t xmit_in; + H5FD_mirror_xmit_open_t xmit_out; + size_t i = 0; + + /* sanity check */ + if ((14+20+4097) != H5FD_MIRROR_XMIT_OPEN_SIZE) { + FAIL_PUTS_ERROR("Header size definition does not match test\n"); + } + if (xmit_mock.op != 0x0D) { + FAIL_PUTS_ERROR("shared header structure is not in expected state"); + } + + /* Populate the expected buffer; expect end padding of 0xFF + */ + HDmemset(expected, 0xFF, H5FD_MIRROR_XMIT_OPEN_SIZE+8); + for (i=0; i < H5FD_MIRROR_XMIT_OPEN_SIZE; i++) { + /* 0x100 is "zero" in a byte, so encode will treat it as a NULL- + * terminator in the filepath string. Expect all zeroes following. + */ + expected[i+2] = (i > 0xFF) ? 0 : (unsigned char)i; + } + + /* Set xmit_in + */ + xmit_in.pub = xmit_mock; /* shared/common */ + xmit_in.flags = 0x0E0F1011; + xmit_in.maxaddr = 0x1213141516171819; + xmit_in.size_t_blob = 0x1A1B1C1D1E1F2021; + for (i=0x22; i < H5FD_MIRROR_XMIT_FILEPATH_MAX+0x22; i++) { + /* nonzero values repeat after 0x100, but will not be encoded */ + xmit_in.filename[i-0x22] = (char)(i % 0x100); + } + xmit_in.filename[H5FD_MIRROR_XMIT_FILEPATH_MAX-1] = 0; + + /* Encode, and compare buffer contents + * Initial buffer is filled with 0xFF to match expected padding + */ + HDmemset(buf, 0xFF, H5FD_MIRROR_XMIT_OPEN_SIZE+8); + if (H5FD_mirror_xmit_encode_open((buf+2), &xmit_in) + != H5FD_MIRROR_XMIT_OPEN_SIZE) + { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, H5FD_MIRROR_XMIT_OPEN_SIZE+8) != 0) { + PRINT_BUFFER_DIFF(buf, expected, H5FD_MIRROR_XMIT_OPEN_SIZE+8); + TEST_ERROR; + } + + /* Decode from buffer + */ + if (H5FD_mirror_xmit_decode_open(&xmit_out, (buf+2)) + != H5FD_MIRROR_XMIT_OPEN_SIZE) + { + TEST_ERROR; + } + if (xmit_out.pub.magic != xmit_mock.magic) TEST_ERROR; + if (xmit_out.pub.version != xmit_mock.version) TEST_ERROR; + if (xmit_out.pub.session_token != xmit_mock.session_token) TEST_ERROR; + if (xmit_out.pub.xmit_count != xmit_mock.xmit_count) TEST_ERROR; + if (xmit_out.pub.op != xmit_mock.op) TEST_ERROR; + if (xmit_out.flags != xmit_in.flags) TEST_ERROR; + if (xmit_out.maxaddr != xmit_in.maxaddr) TEST_ERROR; + if (xmit_out.size_t_blob != xmit_in.size_t_blob) TEST_ERROR; + if (HDstrncmp(xmit_out.filename, xmit_in.filename, + H5FD_MIRROR_XMIT_FILEPATH_MAX) + != 0) + { + PRINT_BUFFER_DIFF(xmit_out.filename, xmit_in.filename, + H5FD_MIRROR_XMIT_FILEPATH_MAX); + TEST_ERROR; + } + + /* Decode from different offset in buffer + * Observe changes when ingesting the padding + */ + if (H5FD_mirror_xmit_decode_open(&xmit_out, (buf)) + != H5FD_MIRROR_XMIT_OPEN_SIZE) + { + TEST_ERROR; + } + if (xmit_out.pub.magic != 0xFFFF0001) TEST_ERROR; + if (xmit_out.pub.version != 0x02) TEST_ERROR; + if (xmit_out.pub.session_token != 0x03040506) TEST_ERROR; + if (xmit_out.pub.xmit_count != 0x0708090A) TEST_ERROR; + if (xmit_out.pub.op != 0x0B) TEST_ERROR; + if (xmit_out.flags != 0x0C0D0E0F) TEST_ERROR; + if (xmit_out.maxaddr != 0x1011121314151617) TEST_ERROR; + if (xmit_out.size_t_blob != 0x18191A1B1C1D1E1F) TEST_ERROR; + /* update expected "filepath" in structure */ + for (i=0x20; i < H5FD_MIRROR_XMIT_FILEPATH_MAX+0x20; i++) { + xmit_in.filename[i-0x20] = (i > 0xFF) ? 0 : (char)i; + } + if (HDstrncmp(xmit_out.filename, xmit_in.filename, + H5FD_MIRROR_XMIT_FILEPATH_MAX) + != 0) + { + PRINT_BUFFER_DIFF(xmit_out.filename, xmit_in.filename, + H5FD_MIRROR_XMIT_FILEPATH_MAX); + TEST_ERROR; + } + + } while (0); /* end xmit open en/decode */ + + /* Test xmit reply structure encode/decode + * Write bogus but easily verifiable data to inside a buffer, and compare. + * Then decode the buffer and compare the structure contents. + * Then repeat from a different offset in the buffer and compare. + * + * Verifies that the first zero character in the filepath will end the + * string, with all following bytes in the encoded buffer being zeroed. + */ + do { + unsigned char buf[H5FD_MIRROR_XMIT_REPLY_SIZE+8]; + unsigned char expected[H5FD_MIRROR_XMIT_REPLY_SIZE+8]; + H5FD_mirror_xmit_reply_t xmit_in; + H5FD_mirror_xmit_reply_t xmit_out; + size_t i = 0; + + /* sanity check */ + if ((14+4+256) != H5FD_MIRROR_XMIT_REPLY_SIZE) { + FAIL_PUTS_ERROR("Header size definition does not match test\n"); + } + if (xmit_mock.op != 0x0D) { + FAIL_PUTS_ERROR("shared header structure is not in expected state"); + } + + /* Populate the expected buffer; expect end padding of 0xFF + */ + HDmemset(expected, 0xFF, H5FD_MIRROR_XMIT_REPLY_SIZE+8); + for (i=0; i < H5FD_MIRROR_XMIT_REPLY_SIZE; i++) { + /* 0x100 is "zero" in a byte, so encode will treat it as a NULL- + * terminator in the filepath string. Expect all zeroes following. + */ + expected[i+2] = (i > 0xFF) ? 0 : (unsigned char)i; + } + + /* Set xmit_in + */ + xmit_in.pub = xmit_mock; /* shared/common */ + xmit_in.status = 0x0E0F1011; + for (i=0x12; i < H5FD_MIRROR_STATUS_MESSAGE_MAX+0x12; i++) { + /* nonzero values repeat after 0x100, but will not be encoded */ + xmit_in.message[i-0x12] = (char)(i % 0x100); + } + xmit_in.message[H5FD_MIRROR_STATUS_MESSAGE_MAX-1] = 0; + + /* Encode, and compare buffer contents + * Initial buffer is filled with 0xFF to match expected padding + */ + HDmemset(buf, 0xFF, H5FD_MIRROR_XMIT_REPLY_SIZE+8); + if (H5FD_mirror_xmit_encode_reply((buf+2), &xmit_in) + != H5FD_MIRROR_XMIT_REPLY_SIZE) + { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, H5FD_MIRROR_XMIT_REPLY_SIZE+8) != 0) { + PRINT_BUFFER_DIFF(buf, expected, H5FD_MIRROR_XMIT_REPLY_SIZE+8); + TEST_ERROR; + } + + /* Decode from buffer + */ + if (H5FD_mirror_xmit_decode_reply(&xmit_out, (buf+2)) + != H5FD_MIRROR_XMIT_REPLY_SIZE) + { + TEST_ERROR; + } + if (xmit_out.pub.magic != xmit_mock.magic) TEST_ERROR; + if (xmit_out.pub.version != xmit_mock.version) TEST_ERROR; + if (xmit_out.pub.session_token != xmit_mock.session_token) TEST_ERROR; + if (xmit_out.pub.xmit_count != xmit_mock.xmit_count) TEST_ERROR; + if (xmit_out.pub.op != xmit_mock.op) TEST_ERROR; + if (xmit_out.status != xmit_in.status) TEST_ERROR; + if (HDstrncmp(xmit_out.message, xmit_in.message, + H5FD_MIRROR_STATUS_MESSAGE_MAX) + != 0) + { + PRINT_BUFFER_DIFF(xmit_out.message, xmit_in.message, + H5FD_MIRROR_STATUS_MESSAGE_MAX); + TEST_ERROR; + } + + /* Decode from different offset in buffer + * Observe changes when ingesting the padding + */ + if (H5FD_mirror_xmit_decode_reply(&xmit_out, (buf)) + != H5FD_MIRROR_XMIT_REPLY_SIZE) + { + TEST_ERROR; + } + if (xmit_out.pub.magic != 0xFFFF0001) TEST_ERROR; + if (xmit_out.pub.version != 0x02) TEST_ERROR; + if (xmit_out.pub.session_token != 0x03040506) TEST_ERROR; + if (xmit_out.pub.xmit_count != 0x0708090A) TEST_ERROR; + if (xmit_out.pub.op != 0x0B) TEST_ERROR; + if (xmit_out.status != 0x0C0D0E0F) TEST_ERROR; + /* update expected "message" in structure */ + for (i=0x10; i < H5FD_MIRROR_STATUS_MESSAGE_MAX+0x10; i++) { + xmit_in.message[i-0x10] = (i > 0xFF) ? 0 : (char)i; + } + if (HDstrncmp(xmit_out.message, xmit_in.message, + H5FD_MIRROR_STATUS_MESSAGE_MAX) + != 0) + { + PRINT_BUFFER_DIFF(xmit_out.message, xmit_in.message, + H5FD_MIRROR_STATUS_MESSAGE_MAX); + TEST_ERROR; + } + + } while (0); /* end xmit reply en/decode */ + + /* Test xmit write structure encode/decode + * Write bogus but easily verifiable data to inside a buffer, and compare. + * Then decode the buffer and compare the structure contents. + * Then repeat from a different offset in the buffer and compare. + */ + do { + unsigned char buf[H5FD_MIRROR_XMIT_WRITE_SIZE+8]; + unsigned char expected[H5FD_MIRROR_XMIT_WRITE_SIZE+8]; + H5FD_mirror_xmit_write_t xmit_in; + H5FD_mirror_xmit_write_t xmit_out; + size_t i = 0; + + /* sanity check */ + if ((14+17) != H5FD_MIRROR_XMIT_WRITE_SIZE) { + FAIL_PUTS_ERROR("Header size definition does not match test\n"); + } + if (xmit_mock.op != 0x0D) { + FAIL_PUTS_ERROR("shared header structure is not in expected state"); + } + + /* Populate the expected buffer; expect end padding of 0xFF + */ + HDmemset(expected, 0xFF, H5FD_MIRROR_XMIT_WRITE_SIZE+8); + for (i=0; i < H5FD_MIRROR_XMIT_WRITE_SIZE; i++) { + expected[i+2] = (unsigned char)i; + } + + /* Set xmit_in + */ + xmit_in.pub = xmit_mock; /* shared/common */ + xmit_in.type = 0x0E; + xmit_in.offset = 0x0F10111213141516; + xmit_in.size = 0x1718191A1B1C1D1E; + + /* Encode, and compare buffer contents + * Initial buffer is filled with 0xFF to match expected padding + */ + HDmemset(buf, 0xFF, H5FD_MIRROR_XMIT_WRITE_SIZE+8); + if (H5FD_mirror_xmit_encode_write((buf+2), &xmit_in) + != H5FD_MIRROR_XMIT_WRITE_SIZE) + { + TEST_ERROR; + } + if (HDmemcmp(buf, expected, H5FD_MIRROR_XMIT_WRITE_SIZE+8) != 0) { + PRINT_BUFFER_DIFF(buf, expected, H5FD_MIRROR_XMIT_WRITE_SIZE+8); + TEST_ERROR; + } + + /* Decode from buffer + */ + if (H5FD_mirror_xmit_decode_write(&xmit_out, (buf+2)) + != H5FD_MIRROR_XMIT_WRITE_SIZE) + { + TEST_ERROR; + } + if (xmit_out.pub.magic != xmit_mock.magic) TEST_ERROR; + if (xmit_out.pub.version != xmit_mock.version) TEST_ERROR; + if (xmit_out.pub.session_token != xmit_mock.session_token) TEST_ERROR; + if (xmit_out.pub.xmit_count != xmit_mock.xmit_count) TEST_ERROR; + if (xmit_out.pub.op != xmit_mock.op) TEST_ERROR; + if (xmit_out.type != 0x0E) TEST_ERROR; + if (xmit_out.offset != 0x0F10111213141516) TEST_ERROR; + if (xmit_out.size != 0x1718191A1B1C1D1E) TEST_ERROR; + + /* Decode from different offset in buffer + * Observe changes when ingesting the padding + */ + if (H5FD_mirror_xmit_decode_write(&xmit_out, (buf)) + != H5FD_MIRROR_XMIT_WRITE_SIZE) + { + TEST_ERROR; + } + if (xmit_out.pub.magic != 0xFFFF0001) TEST_ERROR; + if (xmit_out.pub.version != 0x02) TEST_ERROR; + if (xmit_out.pub.session_token != 0x03040506) TEST_ERROR; + if (xmit_out.pub.xmit_count != 0x0708090A) TEST_ERROR; + if (xmit_out.pub.op != 0x0B) TEST_ERROR; + if (xmit_out.type != 0x0C) TEST_ERROR; + if (xmit_out.offset != 0x0D0E0F1011121314) TEST_ERROR; + if (xmit_out.size != 0x15161718191A1B1C) TEST_ERROR; + + } while (0); /* end xmit write en/decode */ + + PASSED(); + return 0; + +error: + return -1; +} /* end test_xmit_encode_decode */ + + +/* --------------------------------------------------------------------------- + * Function: create_mirroring_split_fapl + * + * Purpose: Create and populate a mirroring FAPL ID. + * Creates target files with the given base name -- ideally the + * test name -- and creates mirroring/split FAPL set to use the + * global mirroring info and a sec2 R/W channel driver. + * + * TODO: receive target IP from caller? + * + * Return: Success: HID of the top-level (splitter) FAPL, a non-negative + * value. + * Failure: H5I_INVALID_HID, a negative value. + * + * Programmer: Jacob Smith + * 2019 + * --------------------------------------------------------------------------- + */ +static hid_t +create_mirroring_split_fapl(const char *_basename, + struct mirrortest_filenames *names) +{ + H5FD_splitter_vfd_config_t splitter_config; + H5FD_mirror_fapl_t mirror_conf; + hid_t ret_value = H5I_INVALID_HID; + + if (_basename == NULL || *_basename == '\0') { + TEST_ERROR; + } + + splitter_config.magic = H5FD_SPLITTER_MAGIC; + splitter_config.version = H5FD_CURR_SPLITTER_VFD_CONFIG_VERSION; + splitter_config.ignore_wo_errs = FALSE; + + /* Create Splitter R/W channel driver (sec2) + */ + splitter_config.rw_fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == splitter_config.rw_fapl_id) { + TEST_ERROR; + } + if (H5Pset_fapl_sec2(splitter_config.rw_fapl_id) == FAIL) { + TEST_ERROR; + } + + /* Create Splitter W/O channel driver (mirror) + */ + mirror_conf.magic = H5FD_MIRROR_FAPL_MAGIC; + mirror_conf.version = H5FD_MIRROR_CURR_FAPL_T_VERSION; + mirror_conf.handshake_port = SERVER_HANDSHAKE_PORT; + if (HDstrncpy(mirror_conf.remote_ip, SERVER_IP_ADDRESS, + H5FD_MIRROR_MAX_IP_LEN) + == NULL) + { + TEST_ERROR; + } + splitter_config.wo_fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == splitter_config.wo_fapl_id) { + TEST_ERROR; + } + if (H5Pset_fapl_mirror(splitter_config.wo_fapl_id, &mirror_conf) == FAIL) { + TEST_ERROR; + } + + /* Build r/w, w/o, and log file paths + */ + if (build_paths(_basename, &splitter_config, names) < 0) { + TEST_ERROR; + } + + /* Set file paths for w/o and logfile + */ + if (HDstrncpy(splitter_config.wo_path, (const char *)names->wo, + H5FD_SPLITTER_PATH_MAX) + == NULL) + { + TEST_ERROR; + } + if (HDstrncpy(splitter_config.log_file_path, (const char *)names->log, + H5FD_SPLITTER_PATH_MAX) + == NULL) + { + TEST_ERROR; + } + + /* Create Splitter FAPL + */ + ret_value = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == ret_value) { + TEST_ERROR; + } + if (H5Pset_fapl_splitter(ret_value, &splitter_config) == FAIL) { + TEST_ERROR; + } + + /* Close FAPLs created for child channels + */ + if (H5Pclose(splitter_config.rw_fapl_id) < 0) { + TEST_ERROR; + } + splitter_config.rw_fapl_id = H5I_INVALID_HID; + if (H5Pclose(splitter_config.wo_fapl_id) < 0) { + TEST_ERROR; + } + splitter_config.wo_fapl_id = H5I_INVALID_HID; + + return ret_value; + +error: + if (splitter_config.wo_fapl_id >= 0) { + (void)H5Pclose(splitter_config.wo_fapl_id); + } + if (splitter_config.rw_fapl_id >= 0) { + (void)H5Pclose(splitter_config.rw_fapl_id); + } + if (ret_value >= 0) { + (void)H5Pclose(ret_value); + } + return H5I_INVALID_HID; +} /* end create_mirroring_split_fapl() */ + + +/* --------------------------------------------------------------------------- + * Function: test_create_and_close + * + * Purpose: Test/demonstrate a do-nothing file open and close. + * + * Verifying file existence and contents is part of other tests. + * + * TODO: receive target IP from caller? + * + * Return: Success: 0 + * Failure: -1 + * + * Programmer: Jacob Smith + * 2019-12-17 + * --------------------------------------------------------------------------- + */ +static int +test_create_and_close(void) +{ + struct mirrortest_filenames names; + hid_t file_id = H5I_INVALID_HID; + hid_t fapl_id = H5P_DEFAULT; + + TESTING("File creation and immediate close"); + + /* Create FAPL for Splitter[sec2|mirror] + */ + fapl_id = create_mirroring_split_fapl("basic_create", &names); + if (H5I_INVALID_HID == fapl_id) { + TEST_ERROR; + } + + /* -------------------- */ + /* TEST: Create and Close */ + + file_id = H5Fcreate(names.rw, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id); + if (H5I_INVALID_HID == file_id) { + TEST_ERROR; + } + + /* -------------------- */ + /* Standard cleanup */ + + if (H5Fclose(file_id) == FAIL) { + TEST_ERROR; + } + if (fapl_id != H5P_DEFAULT && fapl_id >= 0) { + if (H5Pclose(fapl_id) == FAIL) { + TEST_ERROR; + } + } + + PASSED(); + return 0; + +error: + H5E_BEGIN_TRY{ + (void)H5Fclose(file_id); + (void)H5Pclose(fapl_id); + } H5E_END_TRY; + return -1; +} /* end test_create_and_close() */ + + +/* ---------------------------------------------------------------------------- + * Function: create_datasets + * + * Purpose: Given a file ID and least and greateset dataset indices, create + * populated chunked datasets in the target file from min_dset to + * (and including) max_dset. + * Uses #defined constants to determine chunk and dataset sizes + * and values. + * + * Return: SUCCEED/FAIL + * + * Programmer: Jacob Smith + * 2019-08-14 + * ---------------------------------------------------------------------------- + */ +static herr_t +create_datasets(hid_t file_id, + unsigned min_dset, + unsigned max_dset) +{ + hid_t dataspace_ids[MAX_DSET_COUNT + 1]; + hid_t dataset_ids[MAX_DSET_COUNT + 1]; + hid_t filespace_ids[MAX_DSET_COUNT + 1]; + int data_chunk[CHUNK_DIM][CHUNK_DIM]; + unsigned int i, j, k, l, m; + hsize_t offset[2]; + hid_t memspace_id = H5I_INVALID_HID; + hsize_t a_size[2] = {CHUNK_DIM, CHUNK_DIM}; + hsize_t chunk_dims[2] = {CHUNK_DIM, CHUNK_DIM}; + hsize_t dset_dims[2] = {DSET_DIM, DSET_DIM}; + + HDassert(file_id >= 0); + HDassert(min_dset <= max_dset); + HDassert(max_dset <= MAX_DSET_COUNT); + + LOGPRINT(2, "create_dataset()\n"); + + /* --------------------------------- + * "Clear" ID arrays + */ + + for (i = 0; i < MAX_DSET_COUNT; i++) { + LOGPRINT(3, "clearing IDs [%d]\n", i); + dataspace_ids[i] = H5I_INVALID_HID; + dataset_ids[i] = H5I_INVALID_HID; + filespace_ids[i] = H5I_INVALID_HID; + } + + /* --------------------------------- + * Generate dataspace, dataset, and 'filespace' IDs + */ + + if (_create_chunking_ids(file_id, min_dset, max_dset, chunk_dims, + dset_dims, dataspace_ids, filespace_ids, dataset_ids, &memspace_id) + == FAIL) + { + TEST_ERROR; + } + + /* --------------------------------- + * Initialize (write) all datasets in a "round robin"... + * for a given chunk 'location', write chunk data to each dataset. + */ + + for (i = 0; i < DSET_DIM; i += CHUNK_DIM) + { + LOGPRINT(3, "i: %d\n", i); + for (j = 0; j < DSET_DIM; j += CHUNK_DIM) + { + LOGPRINT(3, " j: %d\n", j); + for (m = min_dset; m <= max_dset; m++) + { + LOGPRINT(3, " m: %d\n", m); + for (k = 0; k < CHUNK_DIM; k++) + { + for (l = 0; l < CHUNK_DIM; l++) + { + data_chunk[k][l] = (int)((DSET_DIM * DSET_DIM * m) + + (DSET_DIM * (i + k)) + j + l); + LOGPRINT(3, " data_chunk[%d][%d]: %d\n", + k, l, data_chunk[k][l]); + } + } + + /* select on disk hyperslab */ + offset[0] = (hsize_t)i; + offset[1] = (hsize_t)j; + LOGPRINT(3, " H5Sselect_hyperslab()\n"); + if (H5Sselect_hyperslab(filespace_ids[m], H5S_SELECT_SET, + offset, NULL, a_size, NULL) + < 0) + { + TEST_ERROR; + } + + LOGPRINT(3, " H5Dwrite()\n"); + if (H5Dwrite(dataset_ids[m], H5T_NATIVE_INT, memspace_id, + filespace_ids[m], H5P_DEFAULT, data_chunk) + < 0) + { + TEST_ERROR; + } + + } + } + } + + /* --------------------------------- + * Read and verify data from datasets + */ + + if (_verify_datasets(min_dset, max_dset, filespace_ids, dataset_ids, + memspace_id) + == FAIL) + { + TEST_ERROR; + } + + /* --------------------------------- + * Cleanup + */ + + if (_close_chunking_ids(min_dset, max_dset, dataspace_ids, filespace_ids, + dataset_ids, &memspace_id) + == FAIL) + { + TEST_ERROR; + } + + return SUCCEED; + +error: + (void)_close_chunking_ids(min_dset, max_dset, dataspace_ids, + filespace_ids, dataset_ids, &memspace_id); + LOGPRINT(1, "create_datasets() FAILED\n"); + return FAIL; +} /* end create_datasets() */ + + +/* ---------------------------------------------------------------------------- + * Function: _create_chunking_ids + * + * Purpose: Create new IDs to be used with the associated file. + * + * Return: SUCCEED/FAIL + * + * Programer: Jacob Smith + * 2019 + * ---------------------------------------------------------------------------- + */ +static herr_t +_create_chunking_ids(hid_t file_id, + unsigned min_dset, + unsigned max_dset, + hsize_t *chunk_dims, + hsize_t *dset_dims, + hid_t *dataspace_ids, + hid_t *filespace_ids, + hid_t *dataset_ids, + hid_t *memspace_id) +{ + char dset_name[DSET_NAME_LEN + 1]; + unsigned m = 0; + hid_t dcpl_id = H5I_INVALID_HID; + + LOGPRINT(2, "_create_chunking_ids()\n"); + + /* -------------------- + * Create chunking DCPL + */ + + dcpl_id = H5Pcreate(H5P_DATASET_CREATE); + if (dcpl_id < 0) { + TEST_ERROR; + } + if (H5Pset_chunk(dcpl_id, 2, chunk_dims) == FAIL) { + TEST_ERROR; + } + + /* -------------------- + * Create dataspace IDs + */ + + for (m = min_dset; m <= max_dset; m++) { + dataspace_ids[m] = H5Screate_simple(2, dset_dims, NULL); + if (dataspace_ids[m] < 0) { + HDsnprintf(mesg, MIRR_MESG_SIZE, + "unable to create dataspace ID %d\n", m); + FAIL_PUTS_ERROR(mesg); + } + } + + /* -------------------- + * Create dataset IDs + */ + + for (m = min_dset; m <= max_dset; m++) { + if (HDsnprintf(dset_name, DSET_NAME_LEN, "/dset%03d", m) + > DSET_NAME_LEN) + { + HDsnprintf(mesg, MIRR_MESG_SIZE, + "unable to compose dset name %d\n", m); + FAIL_PUTS_ERROR(mesg); + } + + dataset_ids[m] = H5Dcreate(file_id, dset_name, H5T_STD_I32BE, + dataspace_ids[m], H5P_DEFAULT, dcpl_id, H5P_DEFAULT); + if (dataset_ids[m] < 0) { + HDsnprintf(mesg, MIRR_MESG_SIZE, + "unable to create dset ID %d\n", m); + FAIL_PUTS_ERROR(mesg); + } + } + + /* -------------------- + * Get file space IDs + */ + + for (m = min_dset; m <= max_dset; m++) { + filespace_ids[m] = H5Dget_space(dataset_ids[m]); + if (filespace_ids[m] < 0) { + HDsnprintf(mesg, MIRR_MESG_SIZE, + "unable to create filespace ID %d\n", m); + FAIL_PUTS_ERROR(mesg); + } + } + + /* -------------------- + * Create mem space to be used to read and write chunks + */ + + *memspace_id = H5Screate_simple(2, chunk_dims, NULL); + if (*memspace_id < 0) { + TEST_ERROR; + } + + /* -------------------- + * Clean up the DCPL, even if there were errors before + */ + + if (dcpl_id != H5P_DEFAULT && dcpl_id != H5I_INVALID_HID) { + if (H5Pclose(dcpl_id) == FAIL) { + TEST_ERROR; + } + } + + return SUCCEED; + +error: + if (dcpl_id != H5P_DEFAULT && dcpl_id != H5I_INVALID_HID) { + (void)H5Pclose(dcpl_id); + } + LOGPRINT(1, "_create_chunking_ids() FAILED\n"); + return FAIL; +} /* end _create_chunking_ids() */ + + +/* ---------------------------------------------------------------------------- + * Function: _open_chunking_ids + * + * Purpose: Open/access IDs from the given file. + * + * Return: SUCCEED/FAIL + * + * Programmer: Jacob Smith + * 2019 + * ---------------------------------------------------------------------------- + */ +static herr_t +_open_chunking_ids( + hid_t file_id, + unsigned min_dset, + unsigned max_dset, + hsize_t *chunk_dims, + hid_t *filespace_ids, + hid_t *dataset_ids, + hid_t *memspace_id) +{ + char dset_name[DSET_NAME_LEN+1]; + unsigned m = 0; + + LOGPRINT(2, "_open_chunking_ids()\n"); + + /* -------------------- + * Open dataset IDs + */ + + for (m = min_dset; m <= max_dset; m++) { + if (HDsnprintf(dset_name, DSET_NAME_LEN, "/dset%03d", m) + > DSET_NAME_LEN) + { + HDsnprintf(mesg, MIRR_MESG_SIZE, + "unable to compose dset name %d\n", m); + FAIL_PUTS_ERROR(mesg); + } + + dataset_ids[m] = H5Dopen2(file_id, dset_name, H5P_DEFAULT); + if (dataset_ids[m] < 0) { + HDsnprintf(mesg, MIRR_MESG_SIZE, "unable to open dset ID %d\n", m); + FAIL_PUTS_ERROR(mesg); + } + } + + /* -------------------- + * Open filespace IDs + */ + + for (m = min_dset; m <= max_dset; m++) { + filespace_ids[m] = H5Dget_space(dataset_ids[m]); + if (filespace_ids[m] < 0) { + HDsnprintf(mesg, MIRR_MESG_SIZE, + "unable to get filespace ID %d\n", m); + FAIL_PUTS_ERROR(mesg); + } + } + + /* -------------------- + * Create mem space to be used to read and write chunks + */ + + *memspace_id = H5Screate_simple(2, chunk_dims, NULL); + if (*memspace_id < 0) { + TEST_ERROR; + } + + return SUCCEED; + +error: + LOGPRINT(1, "_open_chunking_ids() FAILED\n"); + return FAIL; +} /* end _open_chunking_ids() */ + + +/* --------------------------------------------------------------------------- + * Function: _close_chunking_ids + * + * Purpose: Close IDs that were created or opened. + * Pass NULL into `dataspace_ids` when closing items opened with + * _open_chunking_ids(). (as opposed to created IDs) + * + * Return: SUCCEED/FAIL + * + * Programmer: Jacob Smith + * 2019 + * --------------------------------------------------------------------------- + */ +static herr_t +_close_chunking_ids(unsigned min_dset, + unsigned max_dset, + hid_t *dataspace_ids, + hid_t *filespace_ids, + hid_t *dataset_ids, + hid_t *memspace_id) +{ + unsigned m; + + LOGPRINT(2, "_close_chunking_ids()\n"); + + for (m = min_dset; m <= max_dset; m++) { + LOGPRINT(3, "closing ids[%d]\n", m); + if (dataspace_ids) { + if (H5Sclose(dataspace_ids[m]) < 0) { + HDsnprintf(mesg, MIRR_MESG_SIZE, + "unable to close dataspace_id[%d]\n", m); + FAIL_PUTS_ERROR(mesg); + } + } + if (H5Dclose(dataset_ids[m]) < 0) { + HDsnprintf(mesg, MIRR_MESG_SIZE, + "unable to close dataset_id[%d]\n", m); + FAIL_PUTS_ERROR(mesg); + } + if (H5Sclose(filespace_ids[m]) < 0) { + HDsnprintf(mesg, MIRR_MESG_SIZE, + "unable to close filespace_id[%d]\n", m); + FAIL_PUTS_ERROR(mesg); + } + } + + if ( (*memspace_id != H5I_INVALID_HID) && + (H5Sclose(*memspace_id) < 0) ) + { + TEST_ERROR; + } + + return SUCCEED; + +error: + LOGPRINT(1, "_close_chunking_ids() FAILED\n"); + return FAIL; +} /* end _close_chunking_ids() */ + + +/* --------------------------------------------------------------------------- + * Function: _verify_datasets + * + * Purpose: Check that each chunk's contents are as expected, as pertaining + * to create_datasets(). + * + * Return: SUCCEED/FAIL + * + * Programmer: Jacob Smith + * 2019 + * --------------------------------------------------------------------------- + */ +static herr_t +_verify_datasets(unsigned min_dset, + unsigned max_dset, + hid_t *filespace_ids, + hid_t *dataset_ids, + hid_t memspace_id) +{ + unsigned i, j, k, l, m; + int data_chunk[CHUNK_DIM][CHUNK_DIM]; + hsize_t offset[2]; + hsize_t a_size[2] = {CHUNK_DIM, CHUNK_DIM}; + + LOGPRINT(2, "_verify_datasets()\n"); + + for (i = 0; i < DSET_DIM; i += CHUNK_DIM) + { + LOGPRINT(3, "i: %d\n", i); + for (j = 0; j < DSET_DIM; j += CHUNK_DIM) + { + LOGPRINT(3, " j: %d\n", j); + for (m = min_dset; m <= max_dset; m++) + { + LOGPRINT(3, " m: %d\n", m); + + /* select on disk hyperslab */ + offset[0] = (hsize_t)i; + offset[1] = (hsize_t)j; + if (H5Sselect_hyperslab(filespace_ids[m], H5S_SELECT_SET, + offset, NULL, a_size, NULL) + < 0) + { + TEST_ERROR; + } + + if (H5Dread(dataset_ids[m], H5T_NATIVE_INT, memspace_id, + filespace_ids[m], H5P_DEFAULT, data_chunk) + < 0) + { + HDsnprintf(mesg, MIRR_MESG_SIZE, + " H5Dread() [%d][%d][%d]\n", + i, j, m); + FAIL_PUTS_ERROR(mesg); + } + + for (k = 0; k < CHUNK_DIM; k++) { + for (l = 0; l < CHUNK_DIM; l++) { + if ((unsigned)data_chunk[k][l] + != + ((DSET_DIM * DSET_DIM * m) + + (DSET_DIM * (i + k)) + j + l)) + { + HDsnprintf(mesg, MIRR_MESG_SIZE, + " MISMATCH [%d][%d][%d][%d][%d]\n", + i, j, m, k, l); + FAIL_PUTS_ERROR(mesg); + } + } + } + + } + } + } + + return SUCCEED; + +error: + LOGPRINT(1, "_verify_datasets() FAILED\n"); + return FAIL; +} /* end _verify_datasets() */ + + +/* --------------------------------------------------------------------------- + * Function: verify_datasets + * + * Purpose: Inspect the datasets in the file created by create_datasets(). + * Wrapper for _verify_datasets() -- this function sets up and + * tears down accessor information. + * + * Return: SUCCEED/FAIL + * + * Programmer: Jacob Smith + * 2019 + * --------------------------------------------------------------------------- + */ +static herr_t +verify_datasets(hid_t file_id, + unsigned min_dset, + unsigned max_dset) +{ + hid_t dataset_ids[MAX_DSET_COUNT + 1]; + hid_t filespace_ids[MAX_DSET_COUNT + 1]; + unsigned i; + hid_t memspace_id = H5I_INVALID_HID; + hsize_t chunk_dims[2] = {CHUNK_DIM, CHUNK_DIM}; + + HDassert(file_id >= 0); + HDassert(min_dset <= max_dset); + HDassert(max_dset <= MAX_DSET_COUNT); + + LOGPRINT(2, "verify_datasets()\n"); + + /* --------------------------------- + * "Clear" ID arrays + */ + + for (i = 0; i < MAX_DSET_COUNT; i++) { + LOGPRINT(3, "clearing IDs [%d]\n", i); + dataset_ids[i] = H5I_INVALID_HID; + filespace_ids[i] = H5I_INVALID_HID; + } + + /* --------------------------------- + * Generate dataspace, dataset, and 'filespace' IDs + */ + + if (_open_chunking_ids(file_id, min_dset, max_dset, chunk_dims, + filespace_ids, dataset_ids, &memspace_id) + == FAIL) + { + TEST_ERROR; + } + + /* --------------------------------- + * Read and verify data from datasets + */ + + if (_verify_datasets(min_dset, max_dset, filespace_ids, dataset_ids, + memspace_id) + == FAIL) + { + TEST_ERROR; + } + + /* --------------------------------- + * Cleanup + */ + + if (_close_chunking_ids(min_dset, max_dset, NULL, filespace_ids, + dataset_ids, &memspace_id) + == FAIL) + { + TEST_ERROR; + } + + return SUCCEED; + +error: + LOGPRINT(1, "verify_datasets() FAILED\n"); + (void)_close_chunking_ids(min_dset, max_dset, NULL, filespace_ids, + dataset_ids, &memspace_id); + return FAIL; + +} /* end verify_datasets() */ + + +/* --------------------------------------------------------------------------- + * Function: test_basic_dataset_write + * + * Purpose: Create and close files; repoen files and write a dataset, + * close; compare files. + * + * TODO: receive target IP from caller? + * + * Return: Success: 0 + * Failure: -1 + * + * Programmer: Jacob Smith + * 2019 + * --------------------------------------------------------------------------- + */ +static int +test_basic_dataset_write(void) +{ + struct mirrortest_filenames names; + hid_t file_id = H5I_INVALID_HID; + hid_t fapl_id = H5P_DEFAULT; + hid_t dset_id = H5I_INVALID_HID; + hid_t dspace_id = H5I_INVALID_HID; + hid_t dtype_id = H5T_NATIVE_INT; + hsize_t dims[2] = { DATABUFFER_SIZE, DATABUFFER_SIZE }; + int *buf = NULL; + int i = 0; + int j = 0; + + TESTING("Mirror open and dataset writing"); + + /* Create FAPL for Splitter[sec2|mirror] + */ + fapl_id = create_mirroring_split_fapl("basic_write", &names); + if (H5I_INVALID_HID == fapl_id) { + TEST_ERROR; + } + + /* Prepare data to be written + */ + buf = (int *)HDmalloc(DATABUFFER_SIZE * DATABUFFER_SIZE * sizeof(int)); + if (NULL == buf) { + TEST_ERROR; + } + for (i = 0; i < DATABUFFER_SIZE; i++) { + for (j = 0; j < DATABUFFER_SIZE; j++) { + int k = i * DATABUFFER_SIZE + j; + buf[k] = k; + } + } + + /* -------------------- */ + /* TEST: Create and Close */ + + file_id = H5Fcreate(names.rw, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id); + if (H5I_INVALID_HID == file_id) { + TEST_ERROR; + } + if (H5Fclose(file_id) == FAIL) { + TEST_ERROR; + } + file_id = H5I_INVALID_HID; + + + /* -------------------- */ + /* TEST: Repoen and Write */ + + file_id = H5Fopen(names.rw, H5F_ACC_RDWR, fapl_id); + if (H5I_INVALID_HID == file_id) { + TEST_ERROR; + } + + dspace_id = H5Screate_simple(2, dims, NULL); + if (H5I_INVALID_HID == dspace_id) { + TEST_ERROR; + } + dset_id = H5Dcreate2(file_id, "dataset", dtype_id, dspace_id, H5P_DEFAULT, + H5P_DEFAULT, H5P_DEFAULT); + if (H5I_INVALID_HID == dset_id) { + TEST_ERROR; + } + + if (H5Dwrite(dset_id, dtype_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, buf) + == FAIL) + { + TEST_ERROR; + } + + /* -------------------- */ + /* Standard cleanup */ + + HDfree(buf); + buf = NULL; + if (H5Dclose(dset_id) == FAIL) { + TEST_ERROR; + } + if (H5Sclose(dspace_id) == FAIL) { + TEST_ERROR; + } + if (H5Fclose(file_id) == FAIL) { + TEST_ERROR; + } + if (fapl_id != H5P_DEFAULT && fapl_id > 0) { + if (H5Pclose(fapl_id) == FAIL) { + TEST_ERROR; + } + } + + /* -------------------- */ + /* TEST: Verify that the R/W and W/O files are identical */ + + if (h5_compare_file_bytes(names.rw, names.wo) < 0) { + TEST_ERROR; + } + + PASSED(); + return 0; + +error: + H5E_BEGIN_TRY{ + (void)H5Fclose(file_id); + if (buf) { + HDfree(buf); + } + (void)H5Dclose(dset_id); + (void)H5Sclose(dspace_id); + if (fapl_id != H5P_DEFAULT && fapl_id > 0) { + (void)H5Pclose(fapl_id); + } + } H5E_END_TRY; + return -1; +} /* end test_basic_dataset_write() */ + + +/* --------------------------------------------------------------------------- + * Function: test_chunked_dataset_write + * + * Purpose: Create and close files; repoen files and write a dataset, + * close; compare files. + * + * TODO: receive target IP from caller? + * + * Return: Success: 0 + * Failure: -1 + * + * Programmer: Jacob Smith + * 2019 + * --------------------------------------------------------------------------- + */ +static int +test_chunked_dataset_write(void) +{ + struct mirrortest_filenames names; + hid_t file_id = H5I_INVALID_HID; + hid_t fapl_id = H5P_DEFAULT; + + TESTING("Mirror open and dataset writing (chunked)"); + + /* Create FAPL for Splitter[sec2|mirror] + */ + fapl_id = create_mirroring_split_fapl("chunked_write", &names); + if (H5I_INVALID_HID == fapl_id) { + TEST_ERROR; + } + + /* -------------------- */ + /* TEST: Create and Close */ + + file_id = H5Fcreate(names.rw, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id); + if (H5I_INVALID_HID == file_id) { + TEST_ERROR; + } + if (H5Fclose(file_id) == FAIL) { + TEST_ERROR; + } + file_id = H5I_INVALID_HID; + + /* -------------------- */ + /* TEST: Reopen and Write */ + + file_id = H5Fopen(names.rw, H5F_ACC_RDWR, fapl_id); + if (H5I_INVALID_HID == file_id) { + TEST_ERROR; + } + + /* Write datasets to file + */ + if (create_datasets(file_id, 0, MAX_DSET_COUNT) == FAIL) { + TEST_ERROR; + } + + /* Close to 'flush to disk', and reopen file + */ + if (H5Fclose(file_id) == FAIL) { + TEST_ERROR; + } + file_id = H5I_INVALID_HID; + + /* Reopen file + */ + file_id = H5Fopen(names.rw, H5F_ACC_RDWR, fapl_id); + if (H5I_INVALID_HID == file_id) { + TEST_ERROR; + } + + /* Verify written data integrity + */ + if (verify_datasets(file_id, 0, MAX_DSET_COUNT) == FAIL) { + TEST_ERROR; + } + + /* -------------------- */ + /* Standard cleanup */ + + if (H5Fclose(file_id) == FAIL) { + TEST_ERROR; + } + file_id = H5I_INVALID_HID; + if (fapl_id != H5P_DEFAULT && fapl_id > 0) { + if (H5Pclose(fapl_id) == FAIL) { + TEST_ERROR; + } + fapl_id = H5I_INVALID_HID; + } + + /* -------------------- */ + /* TEST: Verify that the R/W and W/O files are identical */ + + if (h5_compare_file_bytes(names.rw, names.wo) < 0) { + TEST_ERROR; + } + + PASSED(); + return 0; + +error: + H5E_BEGIN_TRY { + (void)H5Fclose(file_id); + if (fapl_id != H5P_DEFAULT && fapl_id > 0) { + (void)H5Pclose(fapl_id); + } + } H5E_END_TRY; + return -1; +} /* end test_chunked_dataset_write() */ + + +/* --------------------------------------------------------------------------- + * Function: test_on_disk_zoo + * + * Purpose: Verify that the mirror can handle the passing of all the + * various on-disk data structures over the wire, as implemented + * in genall5.c:create_zoo(). + * + * TODO: receive target IP from caller? + * + * Return: Success: 0 + * Failure: -1 + * + * Programmer: Jacob Smith + * 2019 + * --------------------------------------------------------------------------- + */ +static int +test_on_disk_zoo(void) +{ + const char grp_name[] = "/only"; + struct mirrortest_filenames names; + hid_t file_id = H5I_INVALID_HID; + hid_t grp_id = H5I_INVALID_HID; + hid_t fapl_id = H5P_DEFAULT; + + TESTING("'Zoo' of on-disk structures"); + + /* Create FAPL for Splitter[sec2|mirror] + */ + fapl_id = create_mirroring_split_fapl("zoo", &names); + if (H5I_INVALID_HID == fapl_id) { + TEST_ERROR; + } + + /* -------------------- */ + /* TEST: Create file */ + file_id = H5Fcreate(names.rw, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id); + if (H5I_INVALID_HID == file_id) { + TEST_ERROR; + } + + grp_id = H5Gcreate2(file_id, grp_name, H5P_DEFAULT, H5P_DEFAULT, + H5P_DEFAULT); + if (grp_id == H5I_INVALID_HID) { + TEST_ERROR; + } + + /* Create datasets in file, close (flush) and reopen, validate. + * Use of ( pass ) a conceit required for using create_ and validate_zoo() + * from cache_common and/or genall5. + */ + + if ( pass ) { + create_zoo(file_id, grp_name, 0); + } + if ( pass ) { + if (H5Fclose(file_id) == FAIL) { + TEST_ERROR; + } + file_id = H5Fopen(names.rw, H5F_ACC_RDWR, fapl_id); + if (H5I_INVALID_HID == file_id) { + TEST_ERROR; + } + } + if ( pass ) { + validate_zoo(file_id, grp_name, 0); /* sanity-check */ + } + if ( !pass ) { + HDprintf(failure_mssg); + TEST_ERROR; + } + + /* -------------------- */ + /* Standard cleanup */ + + if (fapl_id != H5P_DEFAULT && fapl_id >= 0) { + if (H5Pclose(fapl_id) == FAIL) { + TEST_ERROR; + } + } + if (H5Gclose(grp_id) == FAIL) { + TEST_ERROR; + } + if (H5Fclose(file_id) == FAIL) { + TEST_ERROR; + } + + /* -------------------- */ + /* TEST: Verify that the R/W and W/O files are identical */ + + if (h5_compare_file_bytes(names.rw, names.wo) < 0) { + TEST_ERROR; + } + + PASSED(); + return 0; + +error: + H5E_BEGIN_TRY { + (void)H5Fclose(file_id); + (void)H5Gclose(grp_id); + if (fapl_id != H5P_DEFAULT && fapl_id > 0) { + (void)H5Pclose(fapl_id); + } + } H5E_END_TRY; + return -1; +} /* end test_on_disk_zoo() */ + + +/* --------------------------------------------------------------------------- + * Function: test_vanishing_datasets + * + * Purpose: Verify behavior when writing to a file where data is deleted. + * + * Each dataset is populated with the value of its suffix + * (dset5 is all fives). + * + * Opens 0..15 create one new dataset each, '/dset[i]'. + * Opens 3..18 delete '/dset[1-3]' + * + * Should end with no data in file. + * + * Return: Success: 0 + * Failure: -1 + * + * Programmer: Jacob Smith + * 2019 + * --------------------------------------------------------------------------- + */ +static int +test_vanishing_datasets(void) +{ + struct mirrortest_filenames names; + hid_t file_id = H5I_INVALID_HID; + hid_t fapl_id = H5I_INVALID_HID; + hid_t dset_id = H5I_INVALID_HID; + hid_t dspace_id = H5I_INVALID_HID; + hid_t mirror_fapl_id = H5I_INVALID_HID; + hsize_t dims[2] = {DATABUFFER_SIZE, DATABUFFER_SIZE}; + uint32_t buf[DATABUFFER_SIZE][DATABUFFER_SIZE]; /* consider malloc? */ + H5G_info_t group_info; + unsigned int i, j, k; + const unsigned int max_loops = 20; + const unsigned int max_at_one_time = 3; + + TESTING("Vanishing Datasets"); + + /* -------------------- */ + /* Set up recurrent data (FAPL, dataspace) */ + + /* Create FAPL for Splitter[sec2|mirror] + */ + fapl_id = create_mirroring_split_fapl("vanishing", &names); + if (H5I_INVALID_HID == fapl_id) { + TEST_ERROR; + } + + dspace_id = H5Screate_simple(2, dims, NULL); + if (dspace_id < 0) { + TEST_ERROR; + } + + /* create file */ + file_id = H5Fcreate(names.rw, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id); + if (H5I_INVALID_HID == file_id) { + TEST_ERROR; + } + + for (i=0; i < max_loops; i++) { + char namebuf[DSET_NAME_LEN + 1]; + + /* deleting datasets */ + if (i >= max_at_one_time) { + if (HDsnprintf(namebuf, DSET_NAME_LEN, "/dset%02d", + (i - max_at_one_time) ) + > DSET_NAME_LEN) + { + TEST_ERROR; + } + if (H5Ldelete(file_id, namebuf, H5P_DEFAULT) < 0) { + TEST_ERROR; + } + } /* end if deleting a dataset */ + + /* writing datasets */ + if (i < (max_loops - max_at_one_time)) { + if (HDsnprintf(namebuf, DSET_NAME_LEN, "/dset%02d", i) + > DSET_NAME_LEN) + { + TEST_ERROR; + } + dset_id = H5Dcreate2(file_id, namebuf, H5T_STD_U32LE, dspace_id, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (H5I_INVALID_HID == dset_id) { + TEST_ERROR; + } + + for (j=0; j < DATABUFFER_SIZE; j++) { + for (k=0; k < DATABUFFER_SIZE; k++) { + buf[j][k] = (uint32_t)i; + } + } + + if (H5Dwrite(dset_id, H5T_STD_U32LE, H5S_ALL, H5S_ALL, H5P_DEFAULT, + buf) + < 0) + { + TEST_ERROR; + } + + if (H5Dclose(dset_id) < 0) { + TEST_ERROR; + } + dset_id = H5I_INVALID_HID; + } /* end if writing a dataset */ + + } /* end for dataset create-destroy cycles */ + + if (H5Fclose(file_id) < 0) { + TEST_ERROR; + } + file_id = H5I_INVALID_HID; + + /* verify there are no datasets in file */ + file_id = H5Fopen(names.rw, H5F_ACC_RDONLY, H5P_DEFAULT); + if (file_id < 0) { + TEST_ERROR; + } + if (H5Gget_info(file_id, &group_info) < 0) { + TEST_ERROR; + } + if (group_info.nlinks > 0) { + HDfprintf(stderr, "links in rw file: %d\n", group_info.nlinks); + HDfflush(stderr); + TEST_ERROR; + } + if (H5Fclose(file_id) < 0) { + TEST_ERROR; + } + file_id = H5Fopen(names.wo, H5F_ACC_RDONLY, H5P_DEFAULT); + if (file_id < 0) { + TEST_ERROR; + } + if (H5Gget_info(file_id, &group_info) < 0) { + TEST_ERROR; + } + if (group_info.nlinks > 0) { + HDfprintf(stderr, "links in wo file: %d\n", group_info.nlinks); + HDfflush(stderr); + TEST_ERROR; + } + if (H5Fclose(file_id) < 0) { + TEST_ERROR; + } + file_id = H5I_INVALID_HID; + + if (h5_compare_file_bytes(names.rw, names.wo) < 0) + TEST_ERROR; + + /* -------------------- */ + /* Teardown */ + + if (H5Sclose(dspace_id) < 0) { + TEST_ERROR; + } + if (H5Pclose(fapl_id) < 0) { + TEST_ERROR; + } + + PASSED(); + return 0; + +error: + H5E_BEGIN_TRY { + if (mirror_fapl_id != H5I_INVALID_HID) { + H5Pclose(mirror_fapl_id); + } + if (fapl_id != H5I_INVALID_HID) { + H5Pclose(fapl_id); + } + if (file_id != H5I_INVALID_HID) { + H5Fclose(file_id); + } + if (dset_id != H5I_INVALID_HID) { + H5Dclose(dset_id); + } + if (dspace_id != H5I_INVALID_HID) { + H5Sclose(dspace_id); + } + } H5E_END_TRY; + return -1; +} /* test_vanishing_datasets() */ + + +/* --------------------------------------------------------------------------- + * Function: test_concurrent_access + * + * Purpose: Verify that more than one file may be opened at a time. + * + * TODO: receive target IP from caller? + * + * Return: Success: 0 + * Failure: -1 + * + * Programmer: Jacob Smith + * 2020-03-09 + * --------------------------------------------------------------------------- + */ +static int +test_concurrent_access(void) +{ + struct file_bundle { + struct mirrortest_filenames names; + hid_t dset_id; + hid_t fapl_id; + hid_t file_id; + } bundle[CONCURRENT_COUNT]; + hid_t dspace_id = H5I_INVALID_HID; + hid_t dtype_id = H5T_NATIVE_INT; + hsize_t dims[2] = { DATABUFFER_SIZE, DATABUFFER_SIZE }; + int *buf = NULL; + int i = 0; + int j = 0; + + TESTING("Concurrent opened mirrored files"); + + /* blank bundle */ + for (i = 0; i < CONCURRENT_COUNT; i++) { + bundle[i].dset_id = H5I_INVALID_HID; + bundle[i].fapl_id = H5I_INVALID_HID; + bundle[i].file_id = H5I_INVALID_HID; + *bundle[i].names.rw = '\0'; + *bundle[i].names.wo = '\0'; + *bundle[i].names.log = '\0'; + } + + /* Create FAPL for Splitter[sec2|mirror] + */ + for (i = 0; i < CONCURRENT_COUNT; i++) { + char _name[16] = ""; + hid_t _fapl_id = H5I_INVALID_HID; + HDsnprintf(_name, 15, "concurrent%d", i); + _fapl_id = create_mirroring_split_fapl(_name, &bundle[i].names); + if (H5I_INVALID_HID == _fapl_id) { + TEST_ERROR; + } + bundle[i].fapl_id = _fapl_id; + } + + /* Prepare data to be written + */ + buf = (int *)HDmalloc(DATABUFFER_SIZE * DATABUFFER_SIZE * sizeof(int)); + if (NULL == buf) { + TEST_ERROR; + } + for (i = 0; i < DATABUFFER_SIZE; i++) { + for (j = 0; j < DATABUFFER_SIZE; j++) { + int k = i * DATABUFFER_SIZE + j; + buf[k] = k; + } + } + + /* Prepare generic dataspace + */ + dspace_id = H5Screate_simple(2, dims, NULL); + if (H5I_INVALID_HID == dspace_id) { + TEST_ERROR; + } + + /* -------------------- */ + /* TEST: Create file and open elements */ + + for (i = 0; i < CONCURRENT_COUNT; i++) { + hid_t _file_id = H5I_INVALID_HID; + hid_t _dset_id = H5I_INVALID_HID; + + _file_id = H5Fcreate(bundle[i].names.rw, H5F_ACC_TRUNC, H5P_DEFAULT, + bundle[i].fapl_id); + if (H5I_INVALID_HID == _file_id) { + TEST_ERROR; + } + + bundle[i].file_id = _file_id; + + _dset_id = H5Dcreate2(_file_id, "dataset", dtype_id, dspace_id, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (H5I_INVALID_HID == _dset_id) { + TEST_ERROR; + } + bundle[i].dset_id = _dset_id; + } + + /* -------------------- */ + /* TEST: Write to files */ + + for (i = 0; i < CONCURRENT_COUNT; i++) { + if (H5Dwrite(bundle[i].dset_id, dtype_id, H5S_ALL, H5S_ALL, + H5P_DEFAULT, buf) + == FAIL) + { + TEST_ERROR; + } + } + + /* -------------------- */ + /* TEST: Close elements */ + + for (i = 0; i < CONCURRENT_COUNT; i++) { + if (H5Dclose(bundle[i].dset_id) == FAIL) { + TEST_ERROR; + } + if (H5Fclose(bundle[i].file_id) == FAIL) { + TEST_ERROR; + } + if (H5Pclose(bundle[i].fapl_id) == FAIL) { + TEST_ERROR; + } + } + + /* -------------------- */ + /* Standard cleanup */ + + HDfree(buf); + buf = NULL; + if (H5Sclose(dspace_id) == FAIL) { + TEST_ERROR; + } + + /* -------------------- */ + /* TEST: Verify that the R/W and W/O files are identical */ + + for (i = 0; i < CONCURRENT_COUNT; i++) { + if (h5_compare_file_bytes(bundle[i].names.rw, bundle[i].names.wo) < 0) { + TEST_ERROR; + } + } + + PASSED(); + return 0; + +error: + H5E_BEGIN_TRY{ + if (buf) { + HDfree(buf); + } + (void)H5Sclose(dspace_id); + for (i = 0; i < CONCURRENT_COUNT; i++) { + (void)H5Dclose(bundle[i].dset_id); + (void)H5Fclose(bundle[i].file_id); + (void)H5Pclose(bundle[i].fapl_id); + } + } H5E_END_TRY; + return -1; +} /* end test_concurrent_access() */ + + +/* --------------------------------------------------------------------------- + * Function: main + * + * Purpose: Run tests. + * + * Return: Success: 0 + * Failure: 1 + * + * Programmer: Jacob Smith + * 2019 + * --------------------------------------------------------------------------- + */ +int +main(void) +{ + int nerrors = 0; + + h5_reset(); + + g_log_stream = stdout; /* default debug/logging output stream */ + + HDprintf("Testing Mirror VFD functionality.\n"); + + /* -------------------- */ + /* SETUP */ + + /* Create directories for test-generated .h5 files + */ + if (nerrors == 0) { + if ((HDmkdir(MIRROR_RW_DIR, (mode_t)0755) < 0) && (errno != EEXIST)) { + nerrors++; + } + } + if (nerrors == 0) { + if ((HDmkdir(MIRROR_WO_DIR, (mode_t)0755) < 0) && (errno != EEXIST)) { + nerrors++; + } + } + + /* -------------------- */ + /* TESTS */ + /* Tests return negative values; `-=' increments nerrors count */ + + if (nerrors == 0) { + nerrors -= test_fapl_configuration(); + nerrors -= test_xmit_encode_decode(); + nerrors -= test_create_and_close(); + nerrors -= test_basic_dataset_write(); + nerrors -= test_chunked_dataset_write(); + nerrors -= test_on_disk_zoo(); + nerrors -= test_vanishing_datasets(); + nerrors -= test_concurrent_access(); + } + + if (nerrors) { + HDprintf("***** %d Mirror VFD TEST%s FAILED! *****\n", + nerrors, nerrors > 1 ? "S" : ""); + return EXIT_FAILURE; + } + + HDprintf("All Mirror Virtual File Driver tests passed.\n"); + return EXIT_SUCCESS; +} /* end main() */ + +#else /* H5_HAVE_MIRROR_VFD */ + +int +main(void) +{ + h5_reset(); + HDprintf("Testing Mirror VFD functionality.\n"); + HDprintf("SKIPPED - Mirror VFD not built.\n"); + return EXIT_SUCCESS; +} + +#endif /* H5_HAVE_MIRROR_VFD */ + + diff --git a/test/test_mirror.sh.in b/test/test_mirror.sh.in new file mode 100644 index 0000000..3fdc673 --- /dev/null +++ b/test/test_mirror.sh.in @@ -0,0 +1,100 @@ +#! /bin/bash +# +# 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. +# +# Tests for the Mirror VFD feature. +# +# Created: +# Jacob Smith, 2019-12-30 + +############################################################################### +## test parameters +############################################################################### + +nerrors=0 + +SERVER_VERBOSITY="--verbosity=1" +SERVER_PORT="--port=3000" + + +############################################################################### +## Main +############################################################################### + +## TODO: arguments for main port, port range, verbosity? +# Parse options (none accepted at this time) +while [ $# -gt 0 ]; do + case "$1" in + *) # unknown option + echo "$0: Unknown option ($1)" + exit 1 + ;; + esac +done + + + +RUN_DIR=mirror_vfd_test +MIRROR_UTILS=../utils/mirror_vfd # TODO: presupposes from test/ + +mkdir $RUN_DIR + +# Copy program files into dedicated test directory +for FILE in $MIRROR_UTILS/mirror_* ; do + case "$FILE" in + *.o) continue ;; # Don't copy .o files + esac + cp $FILE $RUN_DIR +done +cp mirror_vfd $RUN_DIR + +# With the --disable-shared option, program files are built in their main +# directories; otherwise they are built in dir/.libs with a corresponding +# wrapper script. Copy these libs builds if appropriate. +if [ -f $MIRROR_UTILS/.libs/mirror_server ] ; then + RUN_LIBS=$RUN_DIR/.libs + mkdir $RUN_LIBS + for FILE in $MIRROR_UTILS/.libs/mirror_* ; do + case "$FILE" in + *.o) continue ;; # Don't copy .o files + esac + cp $FILE $RUN_LIBS + done + cp .libs/mirror_vfd $RUN_LIBS +fi + +cd $RUN_DIR + +echo "Launching Mirror Server" +SERVER_ARGS="$SERVER_PORT $SERVER_VERBOSITY" +./mirror_server $SERVER_ARGS & + +./mirror_vfd +nerrors=$? + +echo "Stopping Mirror Server" +./mirror_server_halten_sie $SERVER_PORT + +############################################################################### +## Report and exit +############################################################################### +cd .. +if test $nerrors -eq 0 ; then + echo "Mirror VFD tests passed." + if test -z "$HDF5_NOCLEANUP" ; then + rm -rf $RUN_DIR + fi + exit 0 +else + echo "Mirror VFD tests FAILED." + exit 1 +fi + diff --git a/test/use.h b/test/use.h index 54d9b26..e5eced2 100644 --- a/test/use.h +++ b/test/use.h @@ -16,48 +16,46 @@ #include "h5test.h" /* Macro definitions */ -#define Hgoto_error(val) {ret_value=val; goto done;} -#define Hgoto_done {goto done;} -#define Chunksize_DFT 256 /* chunksize default */ -#define ErrorReportMax 10 /* max number of errors reported */ +#define Hgoto_error(val) {ret_value=val; goto done;} +#define Hgoto_done {goto done;} +#define Chunksize_DFT 256 /* chunksize default */ +#define ErrorReportMax 10 /* max number of errors reported */ /* these two definitions must match each other */ -#define UC_DATATYPE H5T_NATIVE_SHORT /* use case HDF5 data type */ -#define UC_CTYPE short /* use case C data type */ -#define UC_RANK 3 /* use case dataset rank */ +#define UC_DATATYPE H5T_NATIVE_SHORT /* use case HDF5 data type */ +#define UC_CTYPE short /* use case C data type */ +#define UC_RANK 3 /* use case dataset rank */ /* Name of message file that is sent by the writer */ #define WRITER_MESSAGE "USE_WRITER_MESSAGE" /* type declarations */ typedef enum part_t { - UC_READWRITE =0, /* both writer and reader */ - UC_WRITER, /* writer only */ - UC_READER /* reader only */ + UC_READWRITE = 0, /* both writer and reader */ + UC_WRITER, /* writer only */ + UC_READER /* reader only */ } part_t; typedef struct options_t { - hsize_t chunksize; /* chunks are chunksize^2 planes */ - hsize_t chunkplanes; /* number of planes per chunk, default 1 */ + hsize_t chunksize; /* chunks are chunksize^2 planes */ + hsize_t chunkplanes; /* number of planes per chunk, default 1 */ hsize_t chunkdims[UC_RANK]; /* chunk dims is (chunkplan, chunksize, chunksize) */ hsize_t dims[UC_RANK]; /* dataset initial dims */ hsize_t max_dims[UC_RANK]; /* dataset max dims */ - hsize_t nplanes; /* number of planes to write, default proportional to chunksize */ - char *filename; /* use case data filename */ - part_t launch; /* launch writer, reader or both */ - hbool_t use_swmr; /* use swmr open (1) or not */ - int iterations; /* iterations, default 1 */ + hsize_t nplanes; /* number of planes to write, default proportional to chunksize */ + char *filename; /* use case data filename */ + part_t launch; /* launch writer, reader or both */ + hbool_t use_swmr; /* use swmr open (1) or not */ + int iterations; /* iterations, default 1 */ + hid_t fapl_id; /* instance-specific FAPL ID */ + char *progname; /* Program name (used in usage and dset name) */ } options_t; -/* global variables declarations */ -extern options_t UC_opts; /* Use Case Options */ -extern const char *progname_g; /* Program name */ - /* prototype declarations */ -int parse_option(int argc, char * const argv[]); -int setup_parameters(int argc, char * const argv[]); -void show_parameters(void); +int parse_option(int argc, char * const argv[], options_t * opts); +int setup_parameters(int argc, char * const argv[], options_t * opts); +void show_parameters(options_t * opts); void usage(const char *prog); -int create_uc_file(void); -int write_uc_file(hbool_t tosend, hid_t fid); -int read_uc_file(hbool_t towait); +int create_uc_file(options_t * opts); +int write_uc_file(hbool_t tosend, hid_t file_id, options_t * opts); +int read_uc_file(hbool_t towait, options_t * opts); diff --git a/test/use_append_chunk.c b/test/use_append_chunk.c index 6b34f1e..35594cf 100644 --- a/test/use_append_chunk.c +++ b/test/use_append_chunk.c @@ -11,7 +11,7 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Use Case 1.7 Appending a single chunk + * Use Case 1.7 Appending a single chunk * Description: * Appending a single chunk of raw data to a dataset along an unlimited * dimension within a pre-created file and reading the new data back. @@ -24,35 +24,36 @@ * Level: * User Level * Guarantees: - * o Readers will see the modified dimension sizes after the Writer - * finishes HDF5 metadata updates and issues H5Fflush or H5Oflush calls. - * o Readers will see newly appended data after the Writer finishes - * the flush operation. - * + * o Readers will see the modified dimension sizes after the Writer + * finishes HDF5 metadata updates and issues H5Fflush or H5Oflush calls. + * o Readers will see newly appended data after the Writer finishes + * the flush operation. + * * Preconditions: - * o Readers are not allowed to modify the file. o All datasets - * that are modified by the Writer exist when the Writer opens the file. - * o All datasets that are modified by the Writer exist when a Reader - * opens the file. o Data is written by a hyperslab contained in - * one chunk. - * + * o Readers are not allowed to modify the file. + * o All datasets that are modified by the Writer exist when the Writer + * opens the file. + * o All datasets that are modified by the Writer exist when a Reader + * opens the file. + * o Data is written by a hyperslab contained in one chunk. + * * Main Success Scenario: - * 1. An application creates a file with required objects (groups, - * datasets, and attributes). - * 2. The Writer application opens the file and datasets in the file - * and starts adding data along the unlimited dimension using a hyperslab - * selection that corresponds to an HDF5 chunk. - * 3. A Reader opens the file and a dataset in a file, and queries - * the sizes of the dataset; if the extent of the dataset has changed, - * reads the appended data back. - * + * 1. An application creates a file with required objects (groups, + * datasets, and attributes). + * 2. The Writer application opens the file and datasets in the file + * and starts adding data along the unlimited dimension using a hyperslab + * selection that corresponds to an HDF5 chunk. + * 3. A Reader opens the file and a dataset in a file, and queries + * the sizes of the dataset; if the extent of the dataset has changed, + * reads the appended data back. + * * Discussion points: - * 1. Since the new data is written to the file, and metadata update - * operation of adding pointer to the newly written chunk is atomic and - * happens after the chunk is on the disk, only two things may happen - * to the Reader: - * o The Reader will not see new data. - * o The Reader will see all new data written by Writer. + * 1. Since the new data is written to the file, and metadata update + * operation of adding pointer to the newly written chunk is atomic and + * happens after the chunk is on the disk, only two things may happen + * to the Reader: + * o The Reader will not see new data. + * o The Reader will see all new data written by Writer. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Created: Albert Cheng, 2013/5/28 */ @@ -68,46 +69,43 @@ #include "use.h" -/* Global Variable definitions */ -options_t UC_opts; /* Use Case Options */ -const char *progname_g="use_append_chunk"; /* program name */ +#define USE_APPEND_CHUNK_PROGNAME "use_append_chunk" + +static options_t UC_opts; /* Use Case Options */ /* Setup parameters for the use case. * Return: 0 succeed; -1 fail. */ -int setup_parameters(int argc, char * const argv[]) +int +setup_parameters(int argc, char * const argv[], options_t * opts) { /* use case defaults */ - HDmemset(&UC_opts, 0, sizeof(options_t)); - UC_opts.chunksize = Chunksize_DFT; - UC_opts.use_swmr = TRUE; /* use swmr open */ - UC_opts.iterations = 1; - UC_opts.chunkplanes = 1; - - /* parse options */ - if (parse_option(argc, argv) < 0) - return(-1); - - /* set chunk dims */ - UC_opts.chunkdims[0] = UC_opts.chunkplanes; - UC_opts.chunkdims[1] = UC_opts.chunkdims[2] = UC_opts.chunksize; - - /* set dataset initial and max dims */ - UC_opts.dims[0] = 0; - UC_opts.max_dims[0] = H5S_UNLIMITED; - UC_opts.dims[1] = UC_opts.dims[2] = UC_opts.max_dims[1] = UC_opts.max_dims[2] = UC_opts.chunksize; - - /* set nplanes */ - if (UC_opts.nplanes == 0) - UC_opts.nplanes = (hsize_t)UC_opts.chunksize; - - /* show parameters and return */ - show_parameters(); + HDmemset(opts, 0, sizeof(options_t)); + opts->chunksize = Chunksize_DFT; + opts->use_swmr = TRUE; /* use swmr open */ + opts->iterations = 1; + opts->chunkplanes = 1; + opts->progname = USE_APPEND_CHUNK_PROGNAME; + + if (parse_option(argc, argv, opts) < 0) + return(-1); + + opts->chunkdims[0] = opts->chunkplanes; + opts->chunkdims[1] = opts->chunkdims[2] = opts->chunksize; + + opts->dims[0] = 0; + opts->max_dims[0] = H5S_UNLIMITED; + opts->dims[1] = opts->dims[2] = opts->max_dims[1] = opts->max_dims[2] = opts->chunksize; + + if (opts->nplanes == 0) + opts->nplanes = (hsize_t)opts->chunksize; + + show_parameters(opts); return(0); -} +} /* setup_parameters() */ -/* Overall Algorithm: +/* Overall Algorithm: * Parse options from user; * Generate/pre-created test files needed and close it; * fork: child process becomes the reader process; @@ -119,22 +117,20 @@ main(int argc, char *argv[]) { pid_t childpid=0; pid_t mypid, tmppid; - int child_status; + int child_status; int child_wait_option=0; int ret_value = 0; int child_ret_value; hbool_t send_wait = FALSE; hid_t fapl = -1; /* File access property list */ hid_t fid = -1; /* File ID */ - char *name; /* Test file name */ - /* initialization */ - if (setup_parameters(argc, argv) < 0){ + if (setup_parameters(argc, argv, &UC_opts) < 0) { Hgoto_error(1); } /* Determine the need to send/wait message file*/ - if(UC_opts.launch == UC_READWRITE) { + if (UC_opts.launch == UC_READWRITE) { HDunlink(WRITER_MESSAGE); send_wait = TRUE; } @@ -144,38 +140,63 @@ main(int argc, char *argv[]) /* UC_WRITER: create datafile, skip reader, launch writer. */ /* UC_READER: skip create, launch reader, exit. */ /* ==============================================================*/ - /* ============*/ + /* =========== */ /* Create file */ - /* ============*/ - if (UC_opts.launch != UC_READER){ + /* =========== */ + if (UC_opts.launch != UC_READER) { HDprintf("Creating skeleton data file for test...\n"); - if (create_uc_file() < 0){ + if ((UC_opts.fapl_id = h5_fileaccess()) < 0) { + HDfprintf(stderr, "can't create creation FAPL\n"); + Hgoto_error(1); + } + if (H5Pset_libver_bounds(UC_opts.fapl_id, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0) { + HDfprintf(stderr, "can't set creation FAPL libver bounds\n"); + Hgoto_error(1); + } + if (create_uc_file(&UC_opts) < 0) { HDfprintf(stderr, "***encounter error\n"); Hgoto_error(1); - }else + } else { HDprintf("File created.\n"); + } + /* Close FAPL to prevent issues with forking later */ + if (H5Pclose(UC_opts.fapl_id) < 0) { + HDfprintf(stderr, "can't close creation FAPL\n"); + Hgoto_error(1); + } + UC_opts.fapl_id = H5I_INVALID_HID; } - if (UC_opts.launch==UC_READWRITE){ - /* fork process */ - if((childpid = HDfork()) < 0) { + /* ============ */ + /* Fork process */ + /* ============ */ + if (UC_opts.launch == UC_READWRITE) { + if ((childpid = HDfork()) < 0) { HDperror("fork"); Hgoto_error(1); - }; - }; + } + } mypid = HDgetpid(); /* ============= */ /* launch reader */ /* ============= */ - if (UC_opts.launch != UC_WRITER){ + if (UC_opts.launch != UC_WRITER) { /* child process launch the reader */ - if(0 == childpid) { + if (0 == childpid) { HDprintf("%d: launch reader process\n", mypid); - if (read_uc_file(send_wait) < 0){ + if ((UC_opts.fapl_id = h5_fileaccess()) < 0) { + HDfprintf(stderr, "can't create read FAPL\n"); + HDexit(EXIT_FAILURE); + } + if (read_uc_file(send_wait, &UC_opts) < 0) { HDfprintf(stderr, "read_uc_file encountered error\n"); HDexit(EXIT_FAILURE); } + if (H5Pclose(UC_opts.fapl_id) < 0) { + HDfprintf(stderr, "can't close read FAPL\n"); + HDexit(EXIT_FAILURE); + } HDexit(EXIT_SUCCESS); } } @@ -186,65 +207,63 @@ main(int argc, char *argv[]) /* this process continues to launch the writer */ HDprintf("%d: continue as the writer process\n", mypid); - name = UC_opts.filename; - - /* Set file access proeprty list */ - if((fapl = h5_fileaccess()) < 0) + if ((fapl = h5_fileaccess()) < 0) { + HDfprintf(stderr, "can't create write FAPL\n"); Hgoto_error(1); + } - if(UC_opts.use_swmr) - if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0) + if (UC_opts.use_swmr) { + if (H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0) { + HDfprintf(stderr, "can't set write FAPL libver bounds\n"); Hgoto_error(1); + } + } - /* Open the file */ - if((fid = H5Fopen(name, H5F_ACC_RDWR | (UC_opts.use_swmr ? H5F_ACC_SWMR_WRITE : 0), fapl)) < 0) { + if ((fid = H5Fopen(UC_opts.filename, H5F_ACC_RDWR | (UC_opts.use_swmr ? H5F_ACC_SWMR_WRITE : 0), fapl)) < 0) { HDfprintf(stderr, "H5Fopen failed\n"); Hgoto_error(1); } - if(write_uc_file(send_wait, fid) < 0) { + if (write_uc_file(send_wait, fid, &UC_opts) < 0) { HDfprintf(stderr, "write_uc_file encountered error\n"); Hgoto_error(1); } + if (H5Fclose(fid) < 0) { + HDfprintf(stderr, "Failed to close write\n"); + Hgoto_error(1); + } + + if (H5Pclose(fapl) < 0) { + HDfprintf(stderr, "can't close write FAPL\n"); + Hgoto_error(1); + } + /* ================================================ */ /* If readwrite, collect exit code of child process */ /* ================================================ */ - if (UC_opts.launch == UC_READWRITE){ - if ((tmppid = HDwaitpid(childpid, &child_status, child_wait_option)) < 0){ + if (UC_opts.launch == UC_READWRITE) { + if ((tmppid = HDwaitpid(childpid, &child_status, child_wait_option)) < 0) { HDperror("waitpid"); Hgoto_error(1); } - /* Close the file */ - if(H5Fclose(fid) < 0) { - HDfprintf(stderr, "Failed to close file id\n"); - Hgoto_error(1); - } - - /* Close the property list */ - if(H5Pclose(fapl) < 0) { - HDfprintf(stderr, "Failed to close the property list\n"); - Hgoto_error(1); - } - - if (WIFEXITED(child_status)){ - if ((child_ret_value=WEXITSTATUS(child_status)) != 0){ + if (WIFEXITED(child_status)) { + if ((child_ret_value = WEXITSTATUS(child_status)) != 0) { HDprintf("%d: child process exited with non-zero code (%d)\n", mypid, child_ret_value); Hgoto_error(2); } - } else { - HDprintf("%d: child process terminated abnormally\n", mypid); - Hgoto_error(2); - } + } else { + HDprintf("%d: child process terminated abnormally\n", mypid); + Hgoto_error(2); + } } - + done: - /* Print result and exit */ - if (ret_value != 0){ + if (ret_value != 0) { HDprintf("Error(s) encountered\n"); - }else{ + } else { HDprintf("All passed\n"); } diff --git a/test/use_append_chunk_mirror.c b/test/use_append_chunk_mirror.c new file mode 100644 index 0000000..6ee01c0 --- /dev/null +++ b/test/use_append_chunk_mirror.c @@ -0,0 +1,403 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* HACKED VERSION + * Demonstrate SWMR with a mirrored file. + * + * Must be built with SERVER_IP as the IP address of the target system + * with a running mirror server, and SERVER_PORT as the primary server port. + * + * In addition to the local file, 'shinano.h5' will be created on the remote + * system, mirroring the local file. The file location will be local to + * Server's/Writer's invocation directory. + * + * Template for demonstration purposes: + * + * # Launch mirror server on remote machine (in foreground to easily stop) + * REMOTE(1)$ ./mirror_server /path/to/mirror_worker + * + * # Launch chunk writer with plenty of chunks. + * LOCAL(1)$ ./use_append_chunk_mirror -l w -n 10000 + * + * # Wait one second for files to be created. + * + * # Launch chunk readers on both files. + * LOCAL(2)$ ./use_append_chunk_mirror -l r -n 10000 + * REMOTE(2)$ ./use_append_chunk_mirror -l r -n 10000 -f shinano.h5 + * + * # Hard-stop the server. + * REMOTE(1)$ ^C + * # alt, softer shutdown using echo and nc + * echo "SHUTDOWN" | nc localhost 3000 + */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Use Case 1.7 Appending a single chunk + * Description: + * Appending a single chunk of raw data to a dataset along an unlimited + * dimension within a pre-created file and reading the new data back. + * Goal: + * Read data appended by the Writer to a pre-existing dataset in a + * file. The dataset has one or more unlimited dimensions. The data is + * appended by a hyperslab that is contained in one chunk (for example, + * appending 2-dim planes along the slowest changing dimension in the + * 3-dim dataset). + * Level: + * User Level + * Guarantees: + * o Readers will see the modified dimension sizes after the Writer + * finishes HDF5 metadata updates and issues H5Fflush or H5Oflush calls. + * o Readers will see newly appended data after the Writer finishes + * the flush operation. + * + * Preconditions: + * o Readers are not allowed to modify the file. + * o All datasets that are modified by the Writer exist when the Writer + * opens the file. + * o All datasets that are modified by the Writer exist when a Reader + * opens the file. + * o Data is written by a hyperslab contained in one chunk. + * + * Main Success Scenario: + * 1. An application creates a file with required objects (groups, + * datasets, and attributes). + * 2. The Writer application opens the file and datasets in the file + * and starts adding data along the unlimited dimension using a hyperslab + * selection that corresponds to an HDF5 chunk. + * 3. A Reader opens the file and a dataset in a file, and queries + * the sizes of the dataset; if the extent of the dataset has changed, + * reads the appended data back. + * + * Discussion points: + * 1. Since the new data is written to the file, and metadata update + * operation of adding pointer to the newly written chunk is atomic and + * happens after the chunk is on the disk, only two things may happen + * to the Reader: + * o The Reader will not see new data. + * o The Reader will see all new data written by Writer. + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* Created: Jacob Smith, 2019 */ + +#include "use.h" + +/* This test uses many POSIX things that are not available on + * Windows. We're using a check for fork(2) here as a proxy for + * all POSIX/Unix/Linux things until this test can be made + * more platform-independent. + */ +#ifdef H5_HAVE_FORK + +#ifdef H5_HAVE_MIRROR_VFD + +#define THIS_PROGNAME "use_append_chunk_mirror" + +#define CONNECT_WITH_JELLY 0 + +#if CONNECT_WITH_JELLY +#define SERVER_IP "10.10.10.248" /* hard-coded IP address */ +#else +#define SERVER_IP "127.0.0.1" /* localhost */ +#endif /* CONNECT_WITH_JELLY */ +#define SERVER_PORT 3000 /* hard-coded port number */ +#define MIRROR_FILE_NAME "shinano.h5" /* hard-coded duplicate/mirror filename */ + +static options_t UC_opts; /* Use Case Options */ + +/* Setup parameters for the use case. + * Return: 0 succeed; -1 fail. + */ +int +setup_parameters(int argc, char * const argv[], options_t * opts) +{ + /* use case defaults */ + HDmemset(opts, 0, sizeof(options_t)); + opts->chunksize = Chunksize_DFT; + opts->use_swmr = TRUE; + opts->iterations = 1; + opts->chunkplanes = 1; + opts->progname = THIS_PROGNAME; + + if (parse_option(argc, argv, opts) < 0) + return(-1); + + opts->chunkdims[0] = opts->chunkplanes; + opts->chunkdims[1] = opts->chunkdims[2] = opts->chunksize; + + opts->dims[0] = 0; + opts->max_dims[0] = H5S_UNLIMITED; + opts->dims[1] = opts->dims[2] = opts->max_dims[1] = opts->max_dims[2] = opts->chunksize; + + if (opts->nplanes == 0) + opts->nplanes = (hsize_t)opts->chunksize; + + show_parameters(opts); + return(0); +} /* setup_parameters() */ + + +/* Overall Algorithm: + * Parse options from user; + * Generate/pre-created test files needed and close it; + * fork: child process becomes the reader process; + * while parent process continues as the writer process; + * both run till ending conditions are met. + */ +int +main(int argc, char *argv[]) +{ + pid_t childpid=0; + pid_t mypid, tmppid; + int child_status; + int child_wait_option=0; + int ret_value = 0; + int child_ret_value; + hbool_t send_wait = FALSE; + hid_t fid = -1; /* File ID */ + H5FD_mirror_fapl_t mirr_fa; + H5FD_splitter_vfd_config_t split_fa; + hid_t mirr_fapl_id = H5I_INVALID_HID; + + if (setup_parameters(argc, argv, &UC_opts) < 0) { + Hgoto_error(1); + } + + mirr_fa.magic = H5FD_MIRROR_FAPL_MAGIC; + mirr_fa.version = H5FD_MIRROR_CURR_FAPL_T_VERSION; + mirr_fa.handshake_port = SERVER_PORT; + HDstrncpy(mirr_fa.remote_ip, SERVER_IP, H5FD_MIRROR_MAX_IP_LEN); + + + split_fa.wo_fapl_id = H5I_INVALID_HID; + split_fa.rw_fapl_id = H5I_INVALID_HID; + split_fa.magic = H5FD_SPLITTER_MAGIC; + split_fa.version = H5FD_CURR_SPLITTER_VFD_CONFIG_VERSION; + split_fa.log_file_path[0] = '\0'; /* none */ + split_fa.ignore_wo_errs = FALSE; + HDstrncpy(split_fa.wo_path, MIRROR_FILE_NAME, H5FD_SPLITTER_PATH_MAX); + + /* Determine the need to send/wait message file*/ + if (UC_opts.launch == UC_READWRITE) { + HDunlink(WRITER_MESSAGE); + send_wait = TRUE; + } + + /* ==============================================================*/ + /* UC_READWRITE: create datafile, launch both reader and writer. */ + /* UC_WRITER: create datafile, skip reader, launch writer. */ + /* UC_READER: skip create, launch reader, exit. */ + /* ==============================================================*/ + /* =========== */ + /* Create file */ + /* =========== */ + if (UC_opts.launch != UC_READER) { + HDprintf("Creating skeleton data file for test...\n"); + + /* Prepare mirror child driver */ + mirr_fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (mirr_fapl_id == H5I_INVALID_HID) { + HDfprintf(stderr, "can't create creation mirror FAPL\n"); + Hgoto_error(1); + } + if (H5Pset_fapl_mirror(mirr_fapl_id, &mirr_fa) < 0) { + HDfprintf(stderr, "can't set creation mirror FAPL\n"); + H5Eprint2(H5E_DEFAULT, stdout); + Hgoto_error(1); + } + + /* Prepare parent "splitter" driver in UC_opts */ + split_fa.wo_fapl_id = mirr_fapl_id; + split_fa.rw_fapl_id = H5P_DEFAULT; + UC_opts.fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (UC_opts.fapl_id == H5I_INVALID_HID) { + HDfprintf(stderr, "can't create creation FAPL\n"); + Hgoto_error(1); + } + if (H5Pset_fapl_splitter(UC_opts.fapl_id, &split_fa) < 0) { + HDfprintf(stderr, "can't set creation FAPL\n"); + H5Eprint2(H5E_DEFAULT, stdout); + Hgoto_error(1); + } + + if (H5Pset_libver_bounds(UC_opts.fapl_id, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0) { + HDfprintf(stderr, "can't set creation FAPL libver bounds\n"); + Hgoto_error(1); + } + + /* Create file */ + if (create_uc_file(&UC_opts) < 0) { + HDfprintf(stderr, "***encounter error\n"); + Hgoto_error(1); + } else { + HDprintf("File created.\n"); + } + + /* Close FAPLs to prevent issues with forking later */ + if (H5Pclose(UC_opts.fapl_id) < 0) { + HDfprintf(stderr, "can't close creation FAPL\n"); + Hgoto_error(1); + } + UC_opts.fapl_id = H5I_INVALID_HID; + if (H5Pclose(mirr_fapl_id) < 0) { + HDfprintf(stderr, "can't close creation mirror FAPL\n"); + Hgoto_error(1); + } + mirr_fapl_id = H5I_INVALID_HID; + } + + /* ============ */ + /* Fork process */ + /* ============ */ + if (UC_opts.launch == UC_READWRITE) { + if ((childpid = HDfork()) < 0) { + HDperror("fork"); + Hgoto_error(1); + } + } + mypid = HDgetpid(); + + /* ============= */ + /* launch reader */ + /* ============= */ + if (UC_opts.launch != UC_WRITER) { + /* child process -- launch the reader */ + /* reader only opens the one file -- separate reader needed for mirrored file 'shinano.h5' */ + if (0 == childpid) { + HDprintf("%d: launch reader process\n", mypid); + + UC_opts.fapl_id = H5P_DEFAULT; + if (read_uc_file(send_wait, &UC_opts) < 0) { + HDfprintf(stderr, "read_uc_file encountered error (%d)\n", mypid); + HDexit(1); + } + + HDexit(0); + } + } + + /* ============= */ + /* launch writer */ + /* ============= */ + /* this process continues to launch the writer */ + HDprintf("%d: continue as the writer process\n", mypid); + + /* Prepare mirror child driver */ + mirr_fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (mirr_fapl_id == H5I_INVALID_HID) { + HDfprintf(stderr, "can't create creation mirror FAPL\n"); + Hgoto_error(1); + } + if (H5Pset_fapl_mirror(mirr_fapl_id, &mirr_fa) < 0) { + HDfprintf(stderr, "can't set creation mirror FAPL\n"); + H5Eprint2(H5E_DEFAULT, stdout); + Hgoto_error(1); + } + + /* Prepare parent "splitter" driver in UC_opts */ + split_fa.wo_fapl_id = mirr_fapl_id; + split_fa.rw_fapl_id = H5P_DEFAULT; + UC_opts.fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (UC_opts.fapl_id == H5I_INVALID_HID) { + HDfprintf(stderr, "can't create creation FAPL\n"); + Hgoto_error(1); + } + if (H5Pset_fapl_splitter(UC_opts.fapl_id, &split_fa) < 0) { + HDfprintf(stderr, "can't set creation FAPL\n"); + H5Eprint2(H5E_DEFAULT, stdout); + Hgoto_error(1); + } + + if (UC_opts.use_swmr) { + if (H5Pset_libver_bounds(UC_opts.fapl_id, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0) { + HDfprintf(stderr, "can't set write FAPL libver bounds\n"); + Hgoto_error(1); + } + } + + if ((fid = H5Fopen(UC_opts.filename, H5F_ACC_RDWR | (UC_opts.use_swmr ? H5F_ACC_SWMR_WRITE : 0), UC_opts.fapl_id)) < 0) { + HDfprintf(stderr, "H5Fopen failed\n"); + Hgoto_error(1); + } + + if (write_uc_file(send_wait, fid, &UC_opts) < 0) { + HDfprintf(stderr, "write_uc_file encountered error\n"); + Hgoto_error(1); + } + + if (H5Fclose(fid) < 0) { + HDfprintf(stderr, "Failed to close write\n"); + Hgoto_error(1); + } + + if (H5Pclose(UC_opts.fapl_id) < 0) { + HDfprintf(stderr, "can't close write FAPL\n"); + Hgoto_error(1); + } + + if (H5Pclose(mirr_fapl_id) < 0) { + HDfprintf(stderr, "can't close write mirror FAPL\n"); + Hgoto_error(1); + } + + /* ================================================ */ + /* If readwrite, collect exit code of child process */ + /* ================================================ */ + if (UC_opts.launch == UC_READWRITE) { + if ((tmppid = HDwaitpid(childpid, &child_status, child_wait_option)) < 0) { + HDperror("waitpid"); + Hgoto_error(1); + } + + if (WIFEXITED(child_status)) { + if ((child_ret_value = WEXITSTATUS(child_status)) != 0) { + HDprintf("%d: child process exited with non-zero code (%d)\n", + mypid, child_ret_value); + Hgoto_error(2); + } + } else { + HDprintf("%d: child process terminated abnormally\n", mypid); + Hgoto_error(2); + } + } + +done: + if (ret_value != 0) { + HDprintf("Error(s) encountered\n"); + } else { + HDprintf("All passed\n"); + } + + return(ret_value); +} + +#else /* H5_HAVE_MIRROR_VFD */ + +int +main(void) +{ + HDfprintf(stderr, "Mirror VFD is not built. Skipping.\n"); + return EXIT_SUCCESS; +} /* end main() */ + +#endif /* H5_HAVE_MIRROR_VFD */ + +#else /* H5_HAVE_FORK */ + +int +main(void) +{ + HDfprintf(stderr, "Non-POSIX platform. Skipping.\n"); + return EXIT_SUCCESS; +} /* end main() */ + +#endif /* H5_HAVE_FORK */ + diff --git a/test/use_append_mchunks.c b/test/use_append_mchunks.c index b7d45a4..2eb5a1d 100644 --- a/test/use_append_mchunks.c +++ b/test/use_append_mchunks.c @@ -29,14 +29,14 @@ * finishes HDF5 metadata updates and issues H5Fflush or H5Oflush calls. * o Readers will see newly appended data after the Writer finishes * the flush operation. - * + * * Preconditions: * o Readers are not allowed to modify the file. * o All datasets that are modified by the Writer exist when the * Writer opens the file. * o All datasets that are modified by the Writer exist when a Reader * opens the file. - * + * * Main Success Scenario: * 1. An application creates a file with required objects (groups, * datasets, and attributes). @@ -45,7 +45,7 @@ * spans several chunks. * 3. A Reader opens the file and a dataset in a file; if the size of * the unlimited dimension has changed, reads the appended data back. - * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Created: Albert Cheng, 2013/6/1 */ @@ -61,46 +61,45 @@ #include "use.h" -/* Global Variable definitions */ -options_t UC_opts; /* Use Case Options */ -const char *progname_g="use_append_mchunks"; /* program name */ +#define USE_APPEND_MCHUNKS_PROGNAME "use_append_mchunks" + +static options_t UC_opts; /* Use Case Options */ /* Setup parameters for the use case. * Return: 0 succeed; -1 fail. */ -int setup_parameters(int argc, char * const argv[]) +int +setup_parameters(int argc, char * const argv[], options_t * opts) { /* use case defaults */ - HDmemset(&UC_opts, 0, sizeof(options_t)); - UC_opts.chunksize = Chunksize_DFT; - UC_opts.use_swmr = 1; /* use swmr open */ - UC_opts.iterations = 1; - UC_opts.chunkplanes = 1; - - /* parse options */ - if (parse_option(argc, argv) < 0){ - return(-1); + HDmemset(opts, 0, sizeof(options_t)); + opts->chunksize = Chunksize_DFT; + opts->use_swmr = 1; /* use swmr open */ + opts->iterations = 1; + opts->chunkplanes = 1; + opts->progname = USE_APPEND_MCHUNKS_PROGNAME; + opts->fapl_id = H5I_INVALID_HID; + + if (parse_option(argc, argv, opts) < 0) { + return(-1); } - /* set chunk dims */ - UC_opts.chunkdims[0] = (hsize_t)UC_opts.chunkplanes; - UC_opts.chunkdims[1] = UC_opts.chunkdims[2] = (hsize_t)UC_opts.chunksize; - /* set dataset initial and max dims */ - UC_opts.dims[0] = 0; - UC_opts.max_dims[0] = H5S_UNLIMITED; - UC_opts.dims[1] = UC_opts.dims[2] = UC_opts.max_dims[1] = UC_opts.max_dims[2] = 2 * (hsize_t)UC_opts.chunksize; + opts->chunkdims[0] = (hsize_t)opts->chunkplanes; + opts->chunkdims[1] = opts->chunkdims[2] = (hsize_t)opts->chunksize; + + opts->dims[0] = 0; + opts->max_dims[0] = H5S_UNLIMITED; + opts->dims[1] = opts->dims[2] = opts->max_dims[1] = opts->max_dims[2] = 2 * (hsize_t)opts->chunksize; - /* set nplanes */ - if (UC_opts.nplanes == 0) - UC_opts.nplanes = 2 * (hsize_t)UC_opts.chunksize; + if (opts->nplanes == 0) + opts->nplanes = 2 * (hsize_t)opts->chunksize; - /* show parameters and return */ - show_parameters(); + show_parameters(opts); return(0); -} +} /* end setup_parameters() */ -/* Overall Algorithm: +/* Overall Algorithm: * Parse options from user; * Generate/pre-created test files needed and close it; * fork: child process becomes the reader process; @@ -112,22 +111,20 @@ main(int argc, char *argv[]) { pid_t childpid=0; pid_t mypid, tmppid; - int child_status; + int child_status; int child_wait_option=0; int ret_value = 0; int child_ret_value; hbool_t send_wait = 0; hid_t fapl = -1; /* File access property list */ hid_t fid = -1; /* File ID */ - char *name; /* Test file name */ - /* initialization */ - if (setup_parameters(argc, argv) < 0){ + if (setup_parameters(argc, argv, &UC_opts) < 0) { Hgoto_error(1); } /* Determine the need to send/wait message file*/ - if(UC_opts.launch == UC_READWRITE) { + if (UC_opts.launch == UC_READWRITE) { HDunlink(WRITER_MESSAGE); send_wait = 1; } @@ -137,38 +134,63 @@ main(int argc, char *argv[]) /* UC_WRITER: create datafile, skip reader, launch writer. */ /* UC_READER: skip create, launch reader, exit. */ /* ==============================================================*/ - /* ============*/ + /* =========== */ /* Create file */ - /* ============*/ - if (UC_opts.launch != UC_READER){ + /* =========== */ + if (UC_opts.launch != UC_READER) { HDprintf("Creating skeleton data file for test...\n"); - if (create_uc_file() < 0){ - HDfprintf(stderr, "***encounter error\n"); - Hgoto_error(1); - }else - HDprintf("File created.\n"); + if ((UC_opts.fapl_id = h5_fileaccess()) < 0) { + HDfprintf(stderr, "can't create creation FAPL\n"); + Hgoto_error(1); + } + if (H5Pset_libver_bounds(UC_opts.fapl_id, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0) { + HDfprintf(stderr, "can't set creation FAPL libver bounds\n"); + Hgoto_error(1); + } + if (create_uc_file(&UC_opts) < 0) { + HDfprintf(stderr, "***encounter error\n"); + Hgoto_error(1); + } else { + HDprintf("File created.\n"); + } + /* Close FAPL to prevent issues with forking later */ + if (H5Pclose(UC_opts.fapl_id) < 0) { + HDfprintf(stderr, "can't close creation FAPL\n"); + Hgoto_error(1); + } + UC_opts.fapl_id = H5I_INVALID_HID; } - if (UC_opts.launch==UC_READWRITE){ - /* fork process */ - if((childpid = fork()) < 0) { + /* ============ */ + /* Fork process */ + /* ============ */ + if (UC_opts.launch==UC_READWRITE) { + if ((childpid = fork()) < 0) { perror("fork"); Hgoto_error(1); - }; - }; + } + } mypid = getpid(); /* ============= */ /* launch reader */ /* ============= */ - if (UC_opts.launch != UC_WRITER){ + if (UC_opts.launch != UC_WRITER) { /* child process launch the reader */ - if(0 == childpid) { + if (0 == childpid) { HDprintf("%d: launch reader process\n", mypid); - if (read_uc_file(send_wait) < 0){ + if ((UC_opts.fapl_id = h5_fileaccess()) < 0) { + HDfprintf(stderr, "can't create read FAPL\n"); + HDexit(EXIT_FAILURE); + } + if (read_uc_file(send_wait, &UC_opts) < 0) { HDfprintf(stderr, "read_uc_file encountered error\n"); HDexit(EXIT_FAILURE); } + if (H5Pclose(UC_opts.fapl_id) < 0) { + HDfprintf(stderr, "can't close read FAPL\n"); + HDexit(EXIT_FAILURE); + } HDexit(EXIT_SUCCESS); } } @@ -179,51 +201,50 @@ main(int argc, char *argv[]) /* this process continues to launch the writer */ HDprintf("%d: continue as the writer process\n", mypid); - name = UC_opts.filename; - /* Set the file access property list */ - if((fapl = h5_fileaccess()) < 0) + if ((fapl = h5_fileaccess()) < 0) { + HDfprintf(stderr, "can't get write FAPL\n"); Hgoto_error(1); + } - if(UC_opts.use_swmr) - if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0) + if (UC_opts.use_swmr) { + if (H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0) { + HDfprintf(stderr, "can't set write FAPL libver bounds\n"); Hgoto_error(1); + } + } - /* Open the file */ - if((fid = H5Fopen(name, H5F_ACC_RDWR | (UC_opts.use_swmr ? H5F_ACC_SWMR_WRITE : 0), fapl)) < 0) { + if ((fid = H5Fopen(UC_opts.filename, H5F_ACC_RDWR | (UC_opts.use_swmr ? H5F_ACC_SWMR_WRITE : 0), fapl)) < 0) { HDfprintf(stderr, "H5Fopen failed\n"); Hgoto_error(1); } - if(write_uc_file(send_wait, fid) < 0) { + if (write_uc_file(send_wait, fid, &UC_opts) < 0) { HDfprintf(stderr, "write_uc_file encountered error\n"); Hgoto_error(1); } + if (H5Fclose(fid) < 0) { + HDfprintf(stderr, "Failed to close file id\n"); + Hgoto_error(1); + } + + if (H5Pclose(fapl) < 0) { + HDfprintf(stderr, "can't close write FAPL\n"); + Hgoto_error(1); + } /* ================================================ */ /* If readwrite, collect exit code of child process */ /* ================================================ */ - if (UC_opts.launch == UC_READWRITE){ - if ((tmppid = waitpid(childpid, &child_status, child_wait_option)) < 0){ + if (UC_opts.launch == UC_READWRITE) { + if ((tmppid = waitpid(childpid, &child_status, child_wait_option)) < 0) { perror("waitpid"); Hgoto_error(1); } - /* Close the file */ - if(H5Fclose(fid) < 0) { - HDfprintf(stderr, "Failed to close file id\n"); - Hgoto_error(1); - } - - /* Close the property list */ - if(H5Pclose(fapl) < 0) { - HDfprintf(stderr, "Failed to close the property list\n"); - Hgoto_error(1); - } - - if (WIFEXITED(child_status)){ - if ((child_ret_value=WEXITSTATUS(child_status)) != 0){ + if (WIFEXITED(child_status)) { + if ((child_ret_value=WEXITSTATUS(child_status)) != 0) { HDprintf("%d: child process exited with non-zero code (%d)\n", mypid, child_ret_value); Hgoto_error(1); @@ -233,17 +254,16 @@ main(int argc, char *argv[]) Hgoto_error(2); } } - + done: - /* Print result and exit */ - if (ret_value != 0){ + if (ret_value != 0) { HDprintf("Error(s) encountered\n"); - }else{ + } else { HDprintf("All passed\n"); } return(ret_value); -} +} /* end main() */ #else /* H5_HAVE_FORK */ diff --git a/test/use_common.c b/test/use_common.c index 4111b24..a986543 100644 --- a/test/use_common.c +++ b/test/use_common.c @@ -23,8 +23,13 @@ #define H5D_FRIEND /*suppress error about including H5Dpkg */ #define H5D_TESTING -#include "H5Dpkg.h" +#include "H5Dpkg.h" /* TODO : used to verify chunk index type (yes?) -- is there a way to do this sanity-check using the public API instead? */ +/* ---------------------------------------------------------------------------- + * Print a common/shared usage message. + * Receives program name to show default test file name (.h5). + * ---------------------------------------------------------------------------- + */ void usage(const char *prog) { @@ -41,10 +46,13 @@ usage(const char *prog) HDfprintf(stderr, "\n"); } /* end usage() */ -/* Setup Use Case parameters by parsing command line options. -* Setup default values if not set by options. */ +/* ---------------------------------------------------------------------------- + * Setup Use Case parameters by parsing command line options. + * Includes default values for unspecified options. + * ---------------------------------------------------------------------------- + */ int -parse_option(int argc, char * const argv[]) +parse_option(int argc, char * const argv[], options_t * opts) { int ret_value=0; int c; @@ -56,302 +64,307 @@ parse_option(int argc, char * const argv[]) /* suppress getopt from printing error */ opterr = 0; - while (1){ - c = getopt (argc, argv, nagg_options); - if (-1 == c) - break; - switch (c) { - case 'h': - usage(progname_g); - HDexit(EXIT_SUCCESS); - break; - case 'f': /* usecase data file name */ - UC_opts.filename = optarg; - break; - case 'i': /* iterations */ - if ((UC_opts.iterations = HDatoi(optarg)) <= 0) { - HDfprintf(stderr, "bad iterations number %s, must be a positive integer\n", optarg); - usage(progname_g); - Hgoto_error(-1); - }; - break; - case 'l': /* launch reader or writer only */ - switch (*optarg) { - case 'r': /* reader only */ - UC_opts.launch = UC_READER; - break; - case 'w': /* writer only */ - UC_opts.launch = UC_WRITER; - break; + while (1) { + c = getopt(argc, argv, nagg_options); + if (-1 == c) + break; + switch (c) { + case 'h': + usage(opts->progname); + exit(EXIT_SUCCESS); + break; + case 'f': /* usecase data file name */ + opts->filename = optarg; + break; + case 'i': /* iterations */ + if ((opts->iterations = HDatoi(optarg)) <= 0) { + HDfprintf(stderr, "bad iterations number %s, must be a positive integer\n", optarg); + usage(opts->progname); + Hgoto_error(-1); + } + break; + case 'l': /* launch reader or writer only */ + switch (*optarg) { + case 'r': /* reader only */ + opts->launch = UC_READER; + break; + case 'w': /* writer only */ + opts->launch = UC_WRITER; + break; + default: + HDfprintf(stderr, "launch value(%c) should be w or r only.\n", *optarg); + usage(opts->progname); + Hgoto_error(-1); + break; + } /* end switch (reader/writer-only mode toggle) */ + break; + case 'n': /* number of planes to write/read */ + if ((opts->nplanes = HDstrtoul(optarg, NULL, 0)) <= 0) { + HDfprintf(stderr, "bad number of planes %s, must be a positive integer\n", optarg); + usage(opts->progname); + Hgoto_error(-1); + } + break; + case 's': /* use swmr file open mode */ + use_swmr = HDatoi(optarg); + if (use_swmr != 0 && use_swmr != 1) { + HDfprintf(stderr, "swmr value should be 0(no) or 1(yes)\n"); + usage(opts->progname); + Hgoto_error(-1); + } + opts->use_swmr = (hbool_t)use_swmr; + break; + case 'y': /* Number of planes per chunk */ + if ((opts->chunkplanes = HDstrtoul(optarg, NULL, 0)) <= 0) { + HDfprintf(stderr, "bad number of planes per chunk %s, must be a positive integer\n", optarg); + usage(opts->progname); + Hgoto_error(-1); + } + break; + case 'z': /* size of chunk=(z,z) */ + if ((opts->chunksize = HDstrtoull(optarg, NULL, 0)) <= 0) { + HDfprintf(stderr, "bad chunksize %s, must be a positive integer\n", optarg); + usage(opts->progname); + Hgoto_error(-1); + } + break; + case '?': + HDfprintf(stderr, "getopt returned '%c'.\n", c); + Hgoto_error(-1); default: - HDfprintf(stderr, "launch value(%c) should be w or r only.\n", *optarg); - usage(progname_g); - Hgoto_error(-1); - break; - } - break; - case 'n': /* number of planes to write/read */ - if ((UC_opts.nplanes = HDstrtoul(optarg, NULL, 0)) <= 0) { - HDfprintf(stderr, "bad number of planes %s, must be a positive integer\n", optarg); - usage(progname_g); - Hgoto_error(-1); - }; - break; - case 's': /* use swmr file open mode */ - use_swmr = HDatoi(optarg); - if (use_swmr != 0 && use_swmr != 1) { - HDfprintf(stderr, "swmr value should be 0(no) or 1(yes)\n"); - usage(progname_g); + HDfprintf(stderr, "getopt returned unexpected value.\n"); + HDfprintf(stderr, "Unexpected value is %d\n", c); Hgoto_error(-1); - } - UC_opts.use_swmr = (hbool_t)use_swmr; - break; - case 'y': /* Number of planes per chunk */ - if ((UC_opts.chunkplanes = HDstrtoul(optarg, NULL, 0)) <= 0) { - HDfprintf(stderr, "bad number of planes per chunk %s, must be a positive integer\n", optarg); - usage(progname_g); - Hgoto_error(-1); - }; - break; - case 'z': /* size of chunk=(z,z) */ - if ((UC_opts.chunksize = HDstrtoull(optarg, NULL, 0)) <= 0) { - HDfprintf(stderr, "bad chunksize %s, must be a positive integer\n", optarg); - usage(progname_g); - Hgoto_error(-1); - }; - break; - case '?': - HDfprintf(stderr, "getopt returned '%c'.\n", c); - Hgoto_error(-1); - default: - HDfprintf(stderr, "getopt returned unexpected value.\n"); - HDfprintf(stderr, "Unexpected value is %d\n", c); - Hgoto_error(-1); - } - } + } /* end switch (argument symbol) */ + } /* end while (there are still arguments) */ /* set test file name if not given */ - if (!UC_opts.filename){ - /* default data file name is .h5 */ - if ((UC_opts.filename=(char*)HDmalloc(HDstrlen(progname_g)+4))==NULL) { - HDfprintf(stderr, "malloc: failed\n"); - Hgoto_error(-1); - }; - HDstrcpy(UC_opts.filename, progname_g); - HDstrcat(UC_opts.filename, ".h5"); + if (!opts->filename) { + /* default data file name is .h5 */ + if ((opts->filename=(char*)HDmalloc(HDstrlen(opts->progname)+4))==NULL) { + HDfprintf(stderr, "malloc: failed\n"); + Hgoto_error(-1); + } + HDstrcpy(opts->filename, opts->progname); + HDstrcat(opts->filename, ".h5"); } done: - /* All done. */ return(ret_value); -} +} /* end parse_option() */ -/* Show parameters used for this use case */ -void show_parameters(void){ +/* ---------------------------------------------------------------------------- + * Show parameters used for this use case. + * ---------------------------------------------------------------------------- + */ +void +show_parameters(options_t * opts) +{ HDprintf("===Parameters used:===\n"); - printf("chunk dims=(%llu, %llu, %llu)\n", (unsigned long long)UC_opts.chunkdims[0], - (unsigned long long)UC_opts.chunkdims[1], (unsigned long long)UC_opts.chunkdims[2]); - printf("dataset max dims=(%llu, %llu, %llu)\n", (unsigned long long)UC_opts.max_dims[0], - (unsigned long long)UC_opts.max_dims[1], (unsigned long long)UC_opts.max_dims[2]); - HDprintf("number of planes to write=%llu\n", (unsigned long long)UC_opts.nplanes); - HDprintf("using SWMR mode=%s\n", UC_opts.use_swmr ? "yes(1)" : "no(0)"); - HDprintf("data filename=%s\n", UC_opts.filename); + HDprintf("chunk dims=(%llu, %llu, %llu)\n", (unsigned long long)opts->chunkdims[0], + (unsigned long long)opts->chunkdims[1], (unsigned long long)opts->chunkdims[2]); + HDprintf("dataset max dims=(%llu, %llu, %llu)\n", (unsigned long long)opts->max_dims[0], + (unsigned long long)opts->max_dims[1], (unsigned long long)opts->max_dims[2]); + HDprintf("number of planes to write=%llu\n", (unsigned long long)opts->nplanes); + HDprintf("using SWMR mode=%s\n", opts->use_swmr ? "yes(1)" : "no(0)"); + HDprintf("data filename=%s\n", opts->filename); HDprintf("launch part="); - switch (UC_opts.launch){ - case UC_READWRITE: - printf("Reader/Writer\n"); - break; - case UC_WRITER: - printf("Writer\n"); - break; - case UC_READER: - printf("Reader\n"); - break; - default: - /* should not happen */ - printf("Illegal part(%d)\n", UC_opts.launch); - }; - HDprintf("number of iterations=%d (not used yet)\n", UC_opts.iterations); + switch (opts->launch) { + case UC_READWRITE: + HDprintf("Reader/Writer\n"); + break; + case UC_WRITER: + HDprintf("Writer\n"); + break; + case UC_READER: + HDprintf("Reader\n"); + break; + default: + /* should not happen */ + HDprintf("Illegal part(%d)\n", opts->launch); + } + HDprintf("number of iterations=%d (not used yet)\n", opts->iterations); HDprintf("===Parameters shown===\n"); -} +} /* end show_parameters() */ -/* Create the skeleton use case file for testing. +/* ---------------------------------------------------------------------------- + * Create the skeleton use case file for testing. * It has one 3d dataset using chunked storage. * The dataset is (unlimited, chunksize, chunksize). * Dataset type is 2 bytes integer. * It starts out "empty", i.e., first dimension is 0. * * Return: 0 succeed; -1 fail. + * ---------------------------------------------------------------------------- */ -int create_uc_file(void) +int +create_uc_file(options_t * opts) { - hsize_t dims[3]; /* Dataset starting dimensions */ - hid_t fid; /* File ID for new HDF5 file */ - hid_t dcpl; /* Dataset creation property list */ - hid_t sid; /* Dataspace ID */ - hid_t dsid; /* Dataset ID */ - hid_t fapl; /* File access property list */ + hsize_t dims[3]; /* Dataset starting dimensions */ + hid_t fid; /* File ID for new HDF5 file */ + hid_t dcpl; /* Dataset creation property list */ + hid_t sid; /* Dataspace ID */ + hid_t dsid; /* Dataset ID */ H5D_chunk_index_t idx_type; /* Chunk index type */ - /* Create the file */ - if((fapl = h5_fileaccess()) < 0) - return -1; - if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0) - return -1; - if((fid = H5Fcreate(UC_opts.filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0) + if ((fid = H5Fcreate(opts->filename, H5F_ACC_TRUNC, H5P_DEFAULT, opts->fapl_id)) < 0) return -1; /* Set up dimension sizes */ dims[0] = 0; - dims[1] = dims[2] = UC_opts.max_dims[1]; + dims[1] = dims[2] = opts->max_dims[1]; /* Create dataspace for creating datasets */ - if((sid = H5Screate_simple(3, dims, UC_opts.max_dims)) < 0) + if ((sid = H5Screate_simple(3, dims, opts->max_dims)) < 0) return -1; /* Create dataset creation property list */ - if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) + if ((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) return -1; - if(H5Pset_chunk(dcpl, 3, UC_opts.chunkdims) < 0) + if (H5Pset_chunk(dcpl, 3, opts->chunkdims) < 0) return -1; /* create dataset of progname */ - if((dsid = H5Dcreate2(fid, progname_g, UC_DATATYPE, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) - return -1; + if ((dsid = H5Dcreate2(fid, opts->progname, UC_DATATYPE, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) + return -1; /* Check that the chunk index type is not version 1 B-tree. * Version 1 B-trees are not supported under SWMR. */ - if(H5D__layout_idx_type_test(dsid, &idx_type) < 0) + if (H5D__layout_idx_type_test(dsid, &idx_type) < 0) return -1; - if(idx_type == H5D_CHUNK_IDX_BTREE) { + if (idx_type == H5D_CHUNK_IDX_BTREE) { HDfprintf(stderr, "ERROR: Chunk index is version 1 B-tree: aborting.\n"); return -1; } /* Close everything */ - if(H5Dclose(dsid) < 0) - return -1; - if(H5Pclose(fapl) < 0) + if (H5Dclose(dsid) < 0) return -1; - if(H5Pclose(dcpl) < 0) + if (H5Pclose(dcpl) < 0) return -1; - if(H5Sclose(sid) < 0) + if (H5Sclose(sid) < 0) return -1; - if(H5Fclose(fid) < 0) + if (H5Fclose(fid) < 0) return -1; return 0; -} +} /* end create_uc_file() */ -/* Append planes, each of (1,2*chunksize,2*chunksize) to the dataset. +/* ---------------------------------------------------------------------------- + * Append planes, each of (1,2*chunksize,2*chunksize) to the dataset. * In other words, 4 chunks are appended to the dataset at a time. * Fill each plan with the plane number and then write it at the nth plane. * Increase the plane number and repeat till the end of dataset, when it * reaches chunksize long. End product is a (2*chunksize)^3 cube. * * Return: 0 succeed; -1 fail. + * ---------------------------------------------------------------------------- */ -int write_uc_file(hbool_t tosend, hid_t fid) +int +write_uc_file(hbool_t tosend, hid_t file_id, options_t * opts) { - hid_t dsid; /* dataset ID */ - hid_t dcpl; /* Dataset creation property list */ - UC_CTYPE *buffer, *bufptr; /* data buffer */ - hsize_t cz=UC_opts.chunksize; /* Chunk size */ - hid_t f_sid; /* dataset file space id */ - hid_t m_sid; /* memory space id */ - int rank; /* rank */ - hsize_t chunk_dims[3]; /* Chunk dimensions */ - hsize_t dims[3]; /* Dataspace dimensions */ - hsize_t memdims[3]; /* Memory space dimensions */ - hsize_t start[3] = {0,0,0}, count[3]; /* Hyperslab selection values */ - hsize_t i, j, k; - - if(tosend) + hid_t dsid; /* dataset ID */ + hid_t dcpl; /* Dataset creation property list */ + UC_CTYPE *buffer, *bufptr; /* data buffer */ + hsize_t cz=opts->chunksize; /* Chunk size */ + hid_t f_sid; /* dataset file space id */ + hid_t m_sid; /* memory space id */ + int rank; /* rank */ + hsize_t chunk_dims[3]; /* Chunk dimensions */ + hsize_t dims[3]; /* Dataspace dimensions */ + hsize_t memdims[3]; /* Memory space dimensions */ + hsize_t start[3] = {0,0,0}, count[3]; /* Hyperslab selection values */ + hsize_t i, j, k; + + if (TRUE == tosend) { /* Send a message that H5Fopen is complete--releasing the file lock */ h5_send_message(WRITER_MESSAGE, NULL, NULL); + } /* Open the dataset of the program name */ - if((dsid = H5Dopen2(fid, progname_g, H5P_DEFAULT)) < 0){ + if ((dsid = H5Dopen2(file_id, opts->progname, H5P_DEFAULT)) < 0) { HDfprintf(stderr, "H5Dopen2 failed\n"); return -1; } /* Find chunksize used */ - if ((dcpl = H5Dget_create_plist(dsid)) < 0){ + if ((dcpl = H5Dget_create_plist(dsid)) < 0) { HDfprintf(stderr, "H5Dget_create_plist failed\n"); return -1; } - if (H5D_CHUNKED != H5Pget_layout(dcpl)){ + if (H5D_CHUNKED != H5Pget_layout(dcpl)) { HDfprintf(stderr, "storage layout is not chunked\n"); return -1; } - if ((rank = H5Pget_chunk(dcpl, 3, chunk_dims)) != 3){ + if ((rank = H5Pget_chunk(dcpl, 3, chunk_dims)) != 3) { HDfprintf(stderr, "storage rank is not 3\n"); return -1; } /* verify chunk_dims against set paramenters */ - if (chunk_dims[0]!=UC_opts.chunkdims[0] || chunk_dims[1] != cz || chunk_dims[2] != cz){ + if (chunk_dims[0]!=opts->chunkdims[0] || chunk_dims[1] != cz || chunk_dims[2] != cz) { HDfprintf(stderr, "chunk size is not as expected. Got dims=(%llu,%llu,%llu)\n", - (unsigned long long)chunk_dims[0], (unsigned long long)chunk_dims[1], + (unsigned long long)chunk_dims[0], (unsigned long long)chunk_dims[1], (unsigned long long)chunk_dims[2]); return -1; } /* allocate space for data buffer 1 X dims[1] X dims[2] of UC_CTYPE */ memdims[0]=1; - memdims[1] = UC_opts.dims[1]; - memdims[2] = UC_opts.dims[2]; + memdims[1] = opts->dims[1]; + memdims[2] = opts->dims[2]; if ((buffer=(UC_CTYPE*)HDmalloc((size_t)memdims[1]*(size_t)memdims[2]*sizeof(UC_CTYPE)))==NULL) { HDfprintf(stderr, "malloc: failed\n"); return -1; - }; + } /* * Get dataset rank and dimension. */ f_sid = H5Dget_space(dsid); /* Get filespace handle first. */ rank = H5Sget_simple_extent_ndims(f_sid); - if (rank != UC_RANK){ + if (rank != UC_RANK) { HDfprintf(stderr, "rank(%d) of dataset does not match\n", rank); return -1; } - if (H5Sget_simple_extent_dims(f_sid, dims, NULL) < 0){ + if (H5Sget_simple_extent_dims(f_sid, dims, NULL) < 0) { HDfprintf(stderr, "H5Sget_simple_extent_dims got error\n"); return -1; } HDprintf("dataset rank %d, dimensions %llu x %llu x %llu\n", - rank, (unsigned long long)(dims[0]), (unsigned long long)(dims[1]), + rank, (unsigned long long)(dims[0]), (unsigned long long)(dims[1]), (unsigned long long)(dims[2])); /* verify that file space dims are as expected and are consistent with memory space dims */ - if (dims[0] != 0 || dims[1] != memdims[1] || dims[2] != memdims[2]){ + if (dims[0] != 0 || dims[1] != memdims[1] || dims[2] != memdims[2]) { HDfprintf(stderr, "dataset is not empty. Got dims=(%llu,%llu,%llu)\n", - (unsigned long long)dims[0], (unsigned long long)dims[1], + (unsigned long long)dims[0], (unsigned long long)dims[1], (unsigned long long)dims[2]); return -1; } /* setup mem-space for buffer */ - if ((m_sid=H5Screate_simple(rank, memdims, NULL))<0){ + if ((m_sid=H5Screate_simple(rank, memdims, NULL))<0) { HDfprintf(stderr, "H5Screate_simple for memory failed\n"); return -1; - }; + } /* write planes */ count[0]=1; count[1]=dims[1]; count[2]=dims[2]; - for (i=0; inplanes; i++) { /* fill buffer with value i+1 */ bufptr = buffer; - for (j=0; juse_swmr) { + if (H5Odisable_mdc_flushes(dsid) < 0) { HDfprintf(stderr, "H5Odisable_mdc_flushes failed\n"); return -1; } @@ -359,64 +372,65 @@ int write_uc_file(hbool_t tosend, hid_t fid) /* extend the dataset by one for new plane */ dims[0]=i+1; - if(H5Dset_extent(dsid, dims) < 0){ + if (H5Dset_extent(dsid, dims) < 0) { HDfprintf(stderr, "H5Dset_extent failed\n"); return -1; } /* Get the dataset's dataspace */ - if((f_sid = H5Dget_space(dsid)) < 0){ + if ((f_sid = H5Dget_space(dsid)) < 0) { HDfprintf(stderr, "H5Dset_extent failed\n"); return -1; } start[0]=i; /* Choose the next plane to write */ - if(H5Sselect_hyperslab(f_sid, H5S_SELECT_SET, start, NULL, count, NULL) < 0){ + if (H5Sselect_hyperslab(f_sid, H5S_SELECT_SET, start, NULL, count, NULL) < 0) { HDfprintf(stderr, "Failed H5Sselect_hyperslab\n"); return -1; } /* Write plane to the dataset */ - if(H5Dwrite(dsid, UC_DATATYPE, m_sid, f_sid, H5P_DEFAULT, buffer) < 0){ + if (H5Dwrite(dsid, UC_DATATYPE, m_sid, f_sid, H5P_DEFAULT, buffer) < 0) { HDfprintf(stderr, "Failed H5Dwrite\n"); return -1; } /* Uncork the dataset's metadata from the cache, if SWMR is enabled */ - if(UC_opts.use_swmr) - if(H5Oenable_mdc_flushes(dsid) < 0) { + if (opts->use_swmr) { + if (H5Oenable_mdc_flushes(dsid) < 0) { HDfprintf(stderr, "H5Oenable_mdc_flushes failed\n"); return -1; } + } /* flush file to make the just written plane available. */ - if(H5Dflush(dsid) < 0) { + if (H5Dflush(dsid) < 0) { HDfprintf(stderr, "Failed to H5Fflush file\n"); return -1; } - } + } /* end for each plane to write */ /* Done writing. Free/Close all resources including data file */ HDfree(buffer); - if (H5Dclose(dsid) < 0){ + if (H5Dclose(dsid) < 0) { HDfprintf(stderr, "Failed to close datasete\n"); return -1; } - if (H5Sclose(m_sid) < 0){ + if (H5Sclose(m_sid) < 0) { HDfprintf(stderr, "Failed to close memory space\n"); return -1; } - if (H5Sclose(f_sid) < 0){ + if (H5Sclose(f_sid) < 0) { HDfprintf(stderr, "Failed to close file space\n"); return -1; } return 0; -} - +} /* end write_uc_file() */ -/* Read planes from the dataset. +/* ---------------------------------------------------------------------------- + * Read planes from the dataset. * It expects the dataset is being changed (growing). * It checks the unlimited dimension (1st one). When it increases, * it will read in the new planes, one by one, and verify the data correctness. @@ -425,61 +439,51 @@ int write_uc_file(hbool_t tosend, hid_t fid) * that is the expected end of data, the reader exits. * * Return: 0 succeed; -1 fail. + * ---------------------------------------------------------------------------- */ -int read_uc_file(hbool_t towait) +int +read_uc_file(hbool_t towait, options_t * opts) { - hid_t fapl; /* file access property list ID */ - hid_t fid; /* File ID for new HDF5 file */ - hid_t dsid; /* dataset ID */ - char *name; - UC_CTYPE *buffer, *bufptr; /* read data buffer */ - hid_t f_sid; /* dataset file space id */ - hid_t m_sid; /* memory space id */ - int rank; /* rank */ - hsize_t dims[3]; /* Dataspace dimensions */ - hsize_t memdims[3]; /* Memory space dimensions */ - hsize_t nplane=0, nplane_old=0; /* nth plane, last nth plane */ - hsize_t start[3] = {0,0,0}, count[3]; /* Hyperslab selection values */ - hsize_t j, k; - int nreadererr=0; - int nerrs; - int nonewplane; + hid_t fid; /* File ID for new HDF5 file */ + hid_t dsid; /* dataset ID */ + UC_CTYPE *buffer, *bufptr; /* read data buffer */ + hid_t f_sid; /* dataset file space id */ + hid_t m_sid; /* memory space id */ + int rank; /* rank */ + hsize_t dims[3]; /* Dataspace dimensions */ + hsize_t memdims[3]; /* Memory space dimensions */ + hsize_t nplane=0, nplanes_seen=0; /* nth plane, last nth plane */ + hsize_t start[3] = {0,0,0}, count[3]; /* Hyperslab selection values */ + hsize_t j, k; + int nreadererr=0; + int nerrs; + int loops_waiting_for_plane; /* Before reading, wait for the message that H5Fopen is complete--file lock is released */ - if(towait && h5_wait_message(WRITER_MESSAGE) < 0) { + if (towait && h5_wait_message(WRITER_MESSAGE) < 0) { HDfprintf(stderr, "Cannot find writer message file...failed\n"); return -1; } - name = UC_opts.filename; - - /* Open the file */ - if((fapl = h5_fileaccess()) < 0) - return -1; - if((fid = H5Fopen(name, H5F_ACC_RDONLY | (UC_opts.use_swmr ? H5F_ACC_SWMR_READ : 0), fapl)) < 0){ + HDfprintf(stderr, "Opening to read %s\n", opts->filename); + if ((fid = H5Fopen(opts->filename, H5F_ACC_RDONLY | (opts->use_swmr ? H5F_ACC_SWMR_READ : 0), opts->fapl_id)) < 0) { HDfprintf(stderr, "H5Fopen failed\n"); return -1; } - if (H5Pclose(fapl) < 0){ - HDfprintf(stderr, "Failed to property list\n"); - return -1; - } - - /* Open the dataset of the program name */ - if((dsid = H5Dopen2(fid, progname_g, H5P_DEFAULT)) < 0){ + if ((dsid = H5Dopen2(fid, opts->progname, H5P_DEFAULT)) < 0) { HDfprintf(stderr, "H5Dopen2 failed\n"); return -1; } - /* allocate space for data buffer 1 X dims[1] X dims[2] of UC_CTYPE */ - memdims[0]=1; - memdims[1] = UC_opts.dims[1]; - memdims[2] = UC_opts.dims[2]; + /* Allocate space for data buffer 1 X dims[1] X dims[2] of UC_CTYPE */ + memdims[0] = 1; + memdims[1] = opts->dims[1]; + memdims[2] = opts->dims[2]; if ((buffer=(UC_CTYPE*)HDmalloc((size_t)memdims[1]*(size_t)memdims[2]*sizeof(UC_CTYPE)))==NULL) { HDfprintf(stderr, "malloc: failed\n"); return -1; - }; + } /* * Get dataset rank and dimension. @@ -487,11 +491,11 @@ int read_uc_file(hbool_t towait) */ f_sid = H5Dget_space(dsid); /* Get filespace handle first. */ rank = H5Sget_simple_extent_ndims(f_sid); - if (rank != UC_RANK){ + if (rank != UC_RANK) { HDfprintf(stderr, "rank(%d) of dataset does not match\n", rank); return -1; } - if (H5Sget_simple_extent_dims(f_sid, dims, NULL) < 0){ + if (H5Sget_simple_extent_dims(f_sid, dims, NULL) < 0) { HDfprintf(stderr, "H5Sget_simple_extent_dims got error\n"); return -1; } @@ -499,7 +503,7 @@ int read_uc_file(hbool_t towait) rank, (unsigned long long)(dims[0]), (unsigned long long)(dims[1]), (unsigned long long)(dims[2])); /* verify that file space dims are as expected and are consistent with memory space dims */ - if (dims[1] != memdims[1] || dims[2] != memdims[2]){ + if (dims[1] != memdims[1] || dims[2] != memdims[2]) { HDfprintf(stderr, "dataset dimension is not as expected. Got dims=(%llu,%llu,%llu)\n", (unsigned long long)dims[0], (unsigned long long)dims[1], (unsigned long long)dims[2]); @@ -509,120 +513,101 @@ int read_uc_file(hbool_t towait) return -1; } - /* setup mem-space for buffer */ - if ((m_sid=H5Screate_simple(rank, memdims, NULL))<0){ + /* Setup mem-space for buffer */ + if ((m_sid=H5Screate_simple(rank, memdims, NULL)) < 0) { HDfprintf(stderr, "H5Screate_simple for memory failed\n"); return -1; - }; + } - /* Read 1 plane at a time whenever the dataset grows larger - * (along dim[0]) */ - count[0]=1; - count[1]=dims[1]; - count[2]=dims[2]; + /* Read 1 plane at a time whenever the dataset grows larger (along dim[0]) */ + count[0] = 1; + count[1] = dims[1]; + count[2] = dims[2]; /* quit when all nplanes have been read */ - nonewplane=0; - while (nplane_old < UC_opts.nplanes ){ - /* print progress message according to if new planes are availalbe */ - if (nplane_old < dims[0]) { - if (nonewplane){ - /* end the previous message */ - HDprintf("\n"); - nonewplane=0; - } - HDprintf("reading planes %llu to %llu\n", (unsigned long long)nplane_old, + loops_waiting_for_plane=0; + while (nplanes_seen < opts->nplanes) { + /* print progress message according to if new planes are availalbe */ + if (nplanes_seen < dims[0]) { + if (loops_waiting_for_plane) { + /* end the previous message */ + HDprintf("\n"); + loops_waiting_for_plane=0; + } + HDprintf("reading planes %llu to %llu\n", (unsigned long long)nplanes_seen, (unsigned long long)dims[0]); - }else{ - if (nonewplane){ - HDprintf("."); - if (nonewplane>=30){ - HDfprintf(stderr, "waited too long for new plane, quit.\n"); - return -1; - } - }else{ - /* print mesg only the first time; dots still no new plane */ - HDprintf("no new planes to read "); } - nonewplane++; - /* pause for a second */ - HDsleep(1); - } - for (nplane=nplane_old; nplane < dims[0]; nplane++){ - /* read planes between last old nplanes and current extent */ - /* Get the dataset's dataspace */ - if((f_sid = H5Dget_space(dsid)) < 0){ - HDfprintf(stderr, "H5Dget_space failed\n"); - return -1; + else { + if (loops_waiting_for_plane) { + HDprintf("."); + if (loops_waiting_for_plane>=30) { + HDfprintf(stderr, "waited too long for new plane, quit.\n"); + return -1; + } + } + else { + /* print mesg only the first time; dots still no new plane */ + HDprintf("waiting for new planes to read "); + } + loops_waiting_for_plane++; + /* pause for a second */ + HDsleep(1); } - start[0]=nplane; - /* Choose the next plane to read */ - if(H5Sselect_hyperslab(f_sid, H5S_SELECT_SET, start, NULL, count, NULL) < 0){ - HDfprintf(stderr, "H5Sselect_hyperslab failed\n"); - return -1; - } + for (nplane=nplanes_seen; nplane < dims[0]; nplane++) { + /* read planes between last old nplanes and current extent */ + /* Get the dataset's dataspace */ + if ((f_sid = H5Dget_space(dsid)) < 0) { + HDfprintf(stderr, "H5Dget_space failed\n"); + return -1; + } - /* Read the plane from the dataset */ - if(H5Dread(dsid, UC_DATATYPE, m_sid, f_sid, H5P_DEFAULT, buffer) < 0){ - HDfprintf(stderr, "H5Dread failed\n"); - return -1; - } + start[0]=nplane; + /* Choose the next plane to read */ + if (H5Sselect_hyperslab(f_sid, H5S_SELECT_SET, start, NULL, count, NULL) < 0) { + HDfprintf(stderr, "H5Sselect_hyperslab failed\n"); + return -1; + } - /* compare read data with expected data value which is nplane */ - bufptr = buffer; - nerrs=0; - for (j=0; jrw_fapl_id == H5P_DEFAULT) { + if (H5Pget_driver(fetched_info.rw_fapl_id) != H5Pget_driver(H5P_FILE_ACCESS_DEFAULT)) { + SPLITTER_TEST_FAULT("Read-Write driver mismatch (default)\n"); + } + } + else { + if (H5Pget_driver(fetched_info.rw_fapl_id) != H5Pget_driver(info->rw_fapl_id)) { + SPLITTER_TEST_FAULT("Read-Write driver mismatch\n"); + } + } + if (info->wo_fapl_id == H5P_DEFAULT) { + if (H5Pget_driver(fetched_info.wo_fapl_id) != H5Pget_driver(H5P_FILE_ACCESS_DEFAULT)) { + SPLITTER_TEST_FAULT("Write-Only driver mismatch (default)\n"); + } + } + else { + if (H5Pget_driver(fetched_info.wo_fapl_id) != H5Pget_driver(info->wo_fapl_id)) { + SPLITTER_TEST_FAULT("Write-Only driver mismatch\n"); + } + } + if ( (HDstrlen(info->wo_path) != HDstrlen(fetched_info.wo_path)) || + HDstrncmp(info->wo_path, fetched_info.wo_path, H5FD_SPLITTER_PATH_MAX)) + { + HDfprintf(stderr, "MISMATCH: '%s' :: '%s'\n", info->wo_path, fetched_info.wo_path); + HEXPRINT(H5FD_SPLITTER_PATH_MAX, info->wo_path); + HEXPRINT(H5FD_SPLITTER_PATH_MAX, fetched_info.wo_path); + SPLITTER_TEST_FAULT("Write-Only file path mismatch\n"); + } + +done: + return ret_value; +} /* end compare_splitter_config_info() */ + + +/*------------------------------------------------------------------------- + * Function: run_splitter_test + * + * Purpose: Auxiliary function for test_splitter(). + * + * Return: Success: 0 + * Failure: -1 + * + * Description: + * Perform basic open-write-close with the Splitter VFD. + * Prior to operations, removes files from a previous run, + * if they exist. + * After writing, compares read-write and write-only files. + * Includes FAPL sanity testing. + * + *------------------------------------------------------------------------- + */ +static int +run_splitter_test(const struct splitter_dataset_def *data, + hbool_t ignore_wo_errors, + hbool_t provide_logfile_path, + hid_t sub_fapl_ids[2]) +{ + hid_t file_id = H5I_INVALID_HID; + hid_t fapl_id = H5I_INVALID_HID; + hid_t dset_id = H5I_INVALID_HID; + hid_t space_id = H5I_INVALID_HID; + hid_t fapl_id_out = H5I_INVALID_HID; + hid_t fapl_id_cpy = H5I_INVALID_HID; + H5FD_splitter_vfd_config_t vfd_config; + char filename_rw[H5FD_SPLITTER_PATH_MAX + 1]; + FILE *logfile = NULL; + int ret_value = 0; + + vfd_config.magic = H5FD_SPLITTER_MAGIC; + vfd_config.version = H5FD_CURR_SPLITTER_VFD_CONFIG_VERSION; + vfd_config.ignore_wo_errs = ignore_wo_errors; + vfd_config.rw_fapl_id = sub_fapl_ids[0]; + vfd_config.wo_fapl_id = sub_fapl_ids[1]; + + if (splitter_prepare_file_paths(&vfd_config, filename_rw) < 0) { + SPLITTER_TEST_FAULT("can't prepare file paths\n"); + } + + if (provide_logfile_path == FALSE) { + *vfd_config.log_file_path = '\0'; /* reset as empty string */ + } + + /* Create a new fapl to use the SPLITTER file driver */ + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) == H5I_INVALID_HID) { + SPLITTER_TEST_FAULT("can't create FAPL ID\n"); + } + if (H5Pset_fapl_splitter(fapl_id, &vfd_config) < 0) { + SPLITTER_TEST_FAULT("can't set splitter FAPL\n"); + } + if (H5Pget_driver(fapl_id) != H5FD_SPLITTER) { + SPLITTER_TEST_FAULT("set FAPL not SPLITTER\n"); + } + + if (compare_splitter_config_info(fapl_id, &vfd_config) < 0) { + SPLITTER_TEST_FAULT("information mismatch\n"); + } + + /* + * Copy property list, light compare, and close the copy. + * Helps test driver-implemented FAPL-copying and library ID management. + */ + + fapl_id_cpy = H5Pcopy(fapl_id); + if (H5I_INVALID_HID == fapl_id_cpy) { + SPLITTER_TEST_FAULT("can't copy FAPL\n"); + } + if (compare_splitter_config_info(fapl_id_cpy, &vfd_config) < 0) { + SPLITTER_TEST_FAULT("information mismatch\n"); + } + if (H5Pclose(fapl_id_cpy) < 0) { + SPLITTER_TEST_FAULT("can't close fapl copy\n"); + } + + /* + * Proceed with test. Create file. + */ + file_id = H5Fcreate(filename_rw, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id); + if (file_id < 0) { + SPLITTER_TEST_FAULT("can't create file\n"); + } + + /* + * Check driver from file + */ + + fapl_id_out = H5Fget_access_plist(file_id); + if (H5I_INVALID_HID == fapl_id_out) { + SPLITTER_TEST_FAULT("can't get file's FAPL\n"); + + } + if (H5Pget_driver(fapl_id_out) != H5FD_SPLITTER) { + SPLITTER_TEST_FAULT("wrong file FAPL driver\n"); + } + if (compare_splitter_config_info(fapl_id_out, &vfd_config) < 0) { + SPLITTER_TEST_FAULT("information mismatch\n"); + } + if (H5Pclose(fapl_id_out) < 0) { + SPLITTER_TEST_FAULT("can't close file's FAPL\n"); + } + + /* + * Create and write the dataset + */ + + space_id = H5Screate_simple(data->n_dims, data->dims, NULL); + if (space_id < 0) { + SPLITTER_TEST_FAULT("can't create dataspace\n"); + } + dset_id = H5Dcreate2(file_id, data->dset_name, data->mem_type_id, space_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + if (dset_id < 0) { + SPLITTER_TEST_FAULT("can't create dataset\n"); + } + if (H5Dwrite(dset_id, data->mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, data->buf) < 0) { + SPLITTER_TEST_FAULT("can't write data to dataset\n"); + } + + /* Close everything */ + if (H5Dclose(dset_id) < 0) { + SPLITTER_TEST_FAULT("can't close dset\n"); + } + if (H5Sclose(space_id) < 0) { + SPLITTER_TEST_FAULT("can't close space\n"); + } + if (H5Pclose(fapl_id) < 0) { + SPLITTER_TEST_FAULT("can't close fapl\n"); + } + if (H5Fclose(file_id) < 0) { + SPLITTER_TEST_FAULT("can't close file\n"); + } + + /* Verify that the R/W and W/O files are identical */ + if (h5_compare_file_bytes(filename_rw, vfd_config.wo_path) < 0) { + SPLITTER_TEST_FAULT("files are not byte-for-byte equivalent\n"); + } + + /* Verify existence of logfile iff appropriate */ + logfile = fopen(vfd_config.log_file_path, "r"); + if ( (TRUE == provide_logfile_path && NULL == logfile) || + (FALSE == provide_logfile_path && NULL != logfile) ) + { + SPLITTER_TEST_FAULT("no logfile when one was expected\n"); + } + +done: + if (ret_value < 0) { + H5E_BEGIN_TRY { + (void)H5Dclose(dset_id); + (void)H5Sclose(space_id); + (void)H5Pclose(fapl_id_out); + (void)H5Pclose(fapl_id_cpy); + (void)H5Pclose(fapl_id); + (void)H5Fclose(file_id); + } H5E_END_TRY; + } + if (logfile != NULL) { + fclose(logfile); + } + return ret_value; + +} /* end run_splitter_test() */ + + +/*------------------------------------------------------------------------- + * Function: driver_is_splitter_compatible + * + * Purpose: Determine whether the driver set in the FAPL ID is compatible + * with the Splitter VFD -- specificially, Write-Only channel. + * + * Return: Success: 0 + * Failure: -1 + * + * Description: Attempts to put the given FAPL ID as the W/O channel. + * Uses driver's own mechanisms to generate error, and catches + * error. + * + *------------------------------------------------------------------------- + */ +static int +driver_is_splitter_compatible(hid_t fapl_id) +{ + H5FD_splitter_vfd_config_t vfd_config; + hid_t split_fapl_id = H5I_INVALID_HID; + herr_t ret; + int ret_value = 0; + + split_fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == split_fapl_id) { + FAIL_PUTS_ERROR("Can't create contained FAPL"); + } + vfd_config.magic = H5FD_SPLITTER_MAGIC; + vfd_config.version = H5FD_CURR_SPLITTER_VFD_CONFIG_VERSION; + vfd_config.ignore_wo_errs = FALSE; + vfd_config.rw_fapl_id = H5P_DEFAULT; + vfd_config.wo_fapl_id = fapl_id; + HDstrncpy(vfd_config.wo_path, "nonesuch", H5FD_SPLITTER_PATH_MAX); + + H5E_BEGIN_TRY { + ret = H5Pset_fapl_splitter(split_fapl_id, &vfd_config); + } H5E_END_TRY; + if (SUCCEED == ret) { + ret_value = -1; + } + + if (H5Pclose(split_fapl_id) < 0) { + FAIL_PUTS_ERROR("Can't close contained FAPL") + } + split_fapl_id = H5I_INVALID_HID; + + return ret_value; + +error: + H5E_BEGIN_TRY { + (void)H5Pclose(split_fapl_id); + } H5E_END_TRY; + return -1; +} /* end driver_is_splitter_compatible() */ + + +/*------------------------------------------------------------------------- + * Function: splitter_RO_test + * + * Purpose: Verify Splitter VFD with the Read-Only access flag. + * + * Return: Success: 0 + * Failure: -1 + * + * Description: Attempt read-only opening of files with different + * permutations of files already existing on-disk. + * + *------------------------------------------------------------------------- + */ +static int +splitter_RO_test( + const struct splitter_dataset_def *data, + hid_t child_fapl_id) +{ + char filename_rw[H5FD_SPLITTER_PATH_MAX + 1]; + H5FD_splitter_vfd_config_t vfd_config; + hid_t fapl_id = H5I_INVALID_HID; + int ret_value = 0; + hid_t file_id = H5I_INVALID_HID; + + vfd_config.magic = H5FD_SPLITTER_MAGIC; + vfd_config.version = H5FD_CURR_SPLITTER_VFD_CONFIG_VERSION; + vfd_config.ignore_wo_errs = FALSE; + vfd_config.rw_fapl_id = child_fapl_id; + vfd_config.wo_fapl_id = child_fapl_id; + + if (splitter_prepare_file_paths(&vfd_config, filename_rw) < 0) { + SPLITTER_TEST_FAULT("can't prepare splitter file paths\n"); + } + + /* Create a new fapl to use the SPLITTER file driver */ + fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (H5I_INVALID_HID == fapl_id) { + SPLITTER_TEST_FAULT("can't create FAPL ID\n"); + } + if (H5Pset_fapl_splitter(fapl_id, &vfd_config) < 0) { + SPLITTER_TEST_FAULT("can't set splitter FAPL\n"); + } + if (H5Pget_driver(fapl_id) != H5FD_SPLITTER) { + SPLITTER_TEST_FAULT("set FAPL not SPLITTER\n"); + } + + /* Attempt R/O open when both files are nonexistent + * Should fail. + */ + + H5E_BEGIN_TRY { + file_id = H5Fopen(filename_rw, H5F_ACC_RDONLY, fapl_id); + } H5E_END_TRY; + if (file_id >= 0) { + SPLITTER_TEST_FAULT("R/O open on nonexistent files unexpectedly successful\n"); + } + + /* Attempt R/O open when only W/O file exists + * Should fail. + */ + + if (splitter_create_single_file_at(vfd_config.wo_path, vfd_config.wo_fapl_id, data) < 0) { + SPLITTER_TEST_FAULT("can't write W/O file\n"); + } + H5E_BEGIN_TRY { + file_id = H5Fopen(filename_rw, H5F_ACC_RDONLY, fapl_id); + } H5E_END_TRY; + if (file_id >= 0) { + SPLITTER_TEST_FAULT("R/O open with extant W/O file unexpectedly successful\n"); + } + HDremove(vfd_config.wo_path); + + /* Attempt R/O open when only R/W file exists + * Should fail. + */ + + if (splitter_create_single_file_at(filename_rw, vfd_config.rw_fapl_id, data) < 0) { + SPLITTER_TEST_FAULT("can't create R/W file\n"); + } + H5E_BEGIN_TRY { + file_id = H5Fopen(filename_rw, H5F_ACC_RDONLY, fapl_id); + } H5E_END_TRY; + if (file_id >= 0) { + SPLITTER_TEST_FAULT("R/O open with extant R/W file unexpectedly successful\n"); + } + + /* Attempt R/O open when both R/W and W/O files exist + */ + + if (splitter_create_single_file_at(vfd_config.wo_path, vfd_config.wo_fapl_id, data) < 0) { + SPLITTER_TEST_FAULT("can't create W/O file\n"); + } + file_id = H5Fopen(filename_rw, H5F_ACC_RDONLY, fapl_id); + if (file_id < 0) { + SPLITTER_TEST_FAULT("R/O open on two extant files failed\n"); + } + if (splitter_compare_expected_data(file_id, data) < 0) { + SPLITTER_TEST_FAULT("data mismatch in R/W file\n"); + } + if (H5Fclose(file_id) < 0) { + SPLITTER_TEST_FAULT("can't close file(s)\n"); + } + file_id = H5I_INVALID_HID; + + /* Cleanup + */ + + if (H5Pclose(fapl_id) < 0) { + SPLITTER_TEST_FAULT("can't close FAPL ID\n"); + } + fapl_id = H5I_INVALID_HID; + +done: + if (ret_value < 0) { + H5E_BEGIN_TRY { + (void)H5Pclose(fapl_id); + (void)H5Fclose(file_id); + } H5E_END_TRY; + } /* end if error */ + return ret_value; +} /* end splitter_RO_test() */ + + +/*------------------------------------------------------------------------- + * Function: splitter_prepare_file_paths + * + * Purpose: Get file paths ready for use by the Splitter VFD tests. + * + * Return: Success: 0 + * Failure: -1 + * + * Description: + * Use h5_fixname to adjust the splitter-relevant file paths + * from those given in FILENAMES. + * + * REMOVES EXISTING FILES AT THE PATH LOCATIONS PRIOR TO RETURN. + * + * The relevant file paths will be set in filename_rw_out and + * inside the config structure (wo_path, log_file_path). + * + * `filename_rw_out` must be at least H5FD_SPLITTER_PATH_MAX+1 + * characters long. + * + * `vfd_config` must have its child FAPL IDs preset. + * + *------------------------------------------------------------------------- + */ +static int +splitter_prepare_file_paths(H5FD_splitter_vfd_config_t *vfd_config, char *filename_rw_out) +{ + int ret_value = 0; + + if (vfd_config == NULL || vfd_config->magic != H5FD_SPLITTER_MAGIC) { + SPLITTER_TEST_FAULT("invalid splitter config pointer\n"); + } + if (filename_rw_out == NULL) { + SPLITTER_TEST_FAULT("NULL filename_rw pointer\n"); + } + + /* TODO: sanity-check fapl IDs? */ + + /* Build the r/w file, w/o file, and the log file paths. + * Output is stored in the associated string pointers. + */ + h5_fixname(FILENAME[11], vfd_config->rw_fapl_id, filename_rw_out, H5FD_SPLITTER_PATH_MAX); + h5_fixname(FILENAME[12], vfd_config->wo_fapl_id, vfd_config->wo_path, H5FD_SPLITTER_PATH_MAX); + h5_fixname_no_suffix(FILENAME[13], vfd_config->wo_fapl_id, vfd_config->log_file_path, H5FD_SPLITTER_PATH_MAX); + + /* Delete any existing files on disk. + */ + HDremove(filename_rw_out); + HDremove(vfd_config->wo_path); + HDremove(vfd_config->log_file_path); + +done: + return ret_value; +} /* end splitter_prepare_file_paths() */ + + +/*------------------------------------------------------------------------- + * Function: splitter_crate_single_file_at + * + * Purpose: Create a file, optionally w/ dataset. + * + * Return: Success: 0 + * Failure: -1 + * + * Description: + * Create a file at the given location with the given FAPL, + * and write data as defined in `data` in a pre-determined location in the file. + * + * If the dataset definition pointer is NULL, no data is written + * to the file. + * + * Will always overwrite an existing file with the given name/path. + * + *------------------------------------------------------------------------- + */ +static int +splitter_create_single_file_at( + const char *filename, + hid_t fapl_id, + const struct splitter_dataset_def *data) +{ + hid_t file_id = H5I_INVALID_HID; + hid_t space_id = H5I_INVALID_HID; + hid_t dset_id = H5I_INVALID_HID; + int ret_value = 0; + + if (filename == NULL || *filename == '\0') { + SPLITTER_TEST_FAULT("filename is invalid\n"); + } + /* TODO: sanity-check fapl id? */ + + file_id = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id); + if (file_id < 0) { + SPLITTER_TEST_FAULT("can't create file\n"); + } + + if (data) { + /* TODO: sanity-check data, if it exists? */ + space_id = H5Screate_simple(data->n_dims, data->dims, NULL); + if (space_id < 0) { + SPLITTER_TEST_FAULT("can't create dataspace\n"); + } + + dset_id = H5Dcreate2( + file_id, + data->dset_name, + data->mem_type_id, + space_id, + H5P_DEFAULT, + H5P_DEFAULT, + H5P_DEFAULT); + if (dset_id < 0) { + SPLITTER_TEST_FAULT("can't create dataset\n"); + } + + if (H5Dwrite(dset_id, data->mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, data->buf) < 0) { + SPLITTER_TEST_FAULT("can't write data to dataset\n"); + } + + if (H5Dclose(dset_id) < 0) { + SPLITTER_TEST_FAULT("can't close dset\n"); + } + if (H5Sclose(space_id) < 0) { + SPLITTER_TEST_FAULT("can't close space\n"); + } + } /* end if data definition is provided */ + + if (H5Fclose(file_id) < 0) { + SPLITTER_TEST_FAULT("can't close file\n"); + } + +done: + if (ret_value < 0) { + H5E_BEGIN_TRY { + (void)H5Dclose(dset_id); + (void)H5Sclose(space_id); + (void)H5Fclose(file_id); + } H5E_END_TRY; + } /* end if error */ + return ret_value; +} /* end splitter_create_single_file_at() */ + + +/*------------------------------------------------------------------------- + * Function: splitter_compare_expected_data + * + * Purpose: Compare data within a predermined dataset. + * + * Return: Success: 0 + * Failure: -1 + * + * Description: Read data from the file at a predetermined location, and + * compare its contents byte-for-byte with that expected in + * the `data` definition structure. + * + *------------------------------------------------------------------------- + */ +static int +splitter_compare_expected_data(hid_t file_id, + const struct splitter_dataset_def *data) +{ + hid_t dset_id = H5I_INVALID_HID; + int buf[SPLITTER_SIZE][SPLITTER_SIZE]; + int expected[SPLITTER_SIZE][SPLITTER_SIZE]; + size_t i = 0; + size_t j = 0; + int ret_value = 0; + + if (sizeof((void *)buf) != sizeof(data->buf)) { + SPLITTER_TEST_FAULT("invariant size of expected data does not match that received!\n"); + } + HDmemcpy(expected, data->buf, sizeof(expected)); + + dset_id = H5Dopen2(file_id, data->dset_name, H5P_DEFAULT); + if (dset_id < 0) { + SPLITTER_TEST_FAULT("can't open dataset\n"); + } + + if (H5Dread(dset_id, data->mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, (void *)buf) < 0) { + SPLITTER_TEST_FAULT("can't read dataset\n"); + } + + for (i=0; i < SPLITTER_SIZE; i++) { + for (j=0; j < SPLITTER_SIZE; j++) { + if (buf[i][j] != expected[i][j]) { + SPLITTER_TEST_FAULT("mismatch in expected data\n"); + } + } + } + + if (H5Dclose(dset_id) < 0) { + SPLITTER_TEST_FAULT("can't close dataset\n"); + } + +done: + if (ret_value < 0) { + H5E_BEGIN_TRY { + (void)H5Dclose(dset_id); + } H5E_END_TRY; + } + return ret_value; +} /* end splitter_compare_expected_data() */ + + +/*------------------------------------------------------------------------- + * Function: splitter_tentative_open_test() + * + * Purpose: Verifies Splitter behavior with "tentative" H5F_open. + * + * Return: Success: 0 + * Failure: -1 + * + * Description: + * H5F_open() has a two-stage opening process when given a + * Read/Write access flag -- first it performs a "tentative + * open", where it checks to see whether files already exist + * on the system, done in such a way as to not "alter its state" + * (i.e., truncate). + * This can cause problems with the Splitter VFD, as the + * file on the R/W channel might exist already, but that on the + * W/O channel will not, and vice-versa. + * + * This test exists to verify that in any event, files will be + * created as required. + * + *------------------------------------------------------------------------- + */ +static int +splitter_tentative_open_test(hid_t child_fapl_id) +{ + char filename_rw[H5FD_SPLITTER_PATH_MAX + 1]; + H5FD_splitter_vfd_config_t vfd_config; + hid_t fapl_id = H5I_INVALID_HID; + hid_t file_id = H5I_INVALID_HID; + int buf[SPLITTER_SIZE][SPLITTER_SIZE]; /* for comparison */ + hsize_t dims[2] = { SPLITTER_SIZE, SPLITTER_SIZE }; /* for comparison */ + int i = 0; /* for comparison */ + int j = 0; /* for comparison */ + struct splitter_dataset_def data; /* for comparison */ + int ret_value = 0; + + /* pre-fill data buffer to write */ + for (i=0; i < SPLITTER_SIZE; i++) { + for (j=0; j < SPLITTER_SIZE; j++) { + buf[i][j] = i*100+j; + } + } + + /* Dataset info */ + data.buf = (void *)buf; + data.mem_type_id = H5T_NATIVE_INT; + data.dims = dims; + data.n_dims = 2; + data.dset_name = SPLITTER_DATASET_NAME; + + vfd_config.magic = H5FD_SPLITTER_MAGIC; + vfd_config.version = H5FD_CURR_SPLITTER_VFD_CONFIG_VERSION; + vfd_config.ignore_wo_errs = FALSE; + vfd_config.rw_fapl_id = child_fapl_id; + vfd_config.wo_fapl_id = child_fapl_id; + + if (splitter_prepare_file_paths(&vfd_config, filename_rw) < 0) { + SPLITTER_TEST_FAULT("can't prepare splitter file paths\n"); + } + + /* Create a new fapl to use the SPLITTER file driver */ + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) == H5I_INVALID_HID) { + SPLITTER_TEST_FAULT("can't create FAPL ID\n"); + } + if (H5Pset_fapl_splitter(fapl_id, &vfd_config) < 0) { + SPLITTER_TEST_FAULT("can't set splitter FAPL\n"); + } + if (H5Pget_driver(fapl_id) != H5FD_SPLITTER) { + SPLITTER_TEST_FAULT("set FAPL not SPLITTER\n"); + } + + /* H5Fopen() with RDWR access. + * Neither file exist already + * Should fail. + */ + + H5E_BEGIN_TRY { + file_id = H5Fopen(filename_rw, H5F_ACC_RDWR, fapl_id); + } H5E_END_TRY; + if (file_id != H5I_INVALID_HID) { + SPLITTER_TEST_FAULT("open with both nonexistent files unexpectedly succeeded\n"); + } + if (file_exists(filename_rw, child_fapl_id)) { + SPLITTER_TEST_FAULT("R/W file unexpectedly created\n"); + } + if (file_exists(vfd_config.wo_path, child_fapl_id)) { + SPLITTER_TEST_FAULT("W/O file unexpectedly created\n"); + } + + /* H5Fopen() with RDWR access. + * W/O file exists already. + * Should fail. + */ + + if (splitter_create_single_file_at(vfd_config.wo_path, child_fapl_id, &data) < 0) { + SPLITTER_TEST_FAULT("can't write W/O file\n"); + } + H5E_BEGIN_TRY { + file_id = H5Fopen(filename_rw, H5F_ACC_RDWR, fapl_id); + } H5E_END_TRY; + if (file_id != H5I_INVALID_HID) { + SPLITTER_TEST_FAULT("open with nonexistent R/W file unexpectedly succeeded\n"); + } + if (file_exists(filename_rw, child_fapl_id)) { + SPLITTER_TEST_FAULT("R/W file unexpectedly created\n"); + } + if (!file_exists(vfd_config.wo_path, child_fapl_id)) { + SPLITTER_TEST_FAULT("W/O file mysteriously disappeared\n"); + } + HDremove(vfd_config.wo_path); + if (file_exists(vfd_config.wo_path, child_fapl_id)) { + SPLITTER_TEST_FAULT("failed to remove W/O file\n"); + } + + /* H5Fopen() with RDWR access. + * R/W file exists already. + * Should fail. + */ + + if (splitter_create_single_file_at(filename_rw, child_fapl_id, &data) < 0) { + SPLITTER_TEST_FAULT("can't write R/W file\n"); + } + H5E_BEGIN_TRY { + file_id = H5Fopen(filename_rw, H5F_ACC_RDWR, fapl_id); + } H5E_END_TRY; + if (file_id != H5I_INVALID_HID) { + SPLITTER_TEST_FAULT("open with nonexistent W/O unexpectedly succeeded\n"); + } + if (!file_exists(filename_rw, child_fapl_id)) { + SPLITTER_TEST_FAULT("R/W file mysteriously disappeared\n"); + } + if (file_exists(vfd_config.wo_path, child_fapl_id)) { + SPLITTER_TEST_FAULT("W/O file unexpectedly created\n"); + } + + /* H5Fopen() with RDWR access. + * Both files already exist. + */ + + if (splitter_create_single_file_at(vfd_config.wo_path, child_fapl_id, &data) < 0) { + SPLITTER_TEST_FAULT("can't write W/O file\n"); + } + file_id = H5Fopen(filename_rw, H5F_ACC_RDWR, fapl_id); + if (file_id == H5I_INVALID_HID) { + SPLITTER_TEST_FAULT("file-open failed with both present\n"); + } + /* Open successful; close file then inspect presence again */ + if (H5Fclose(file_id) < 0) { + SPLITTER_TEST_FAULT("can't close file ID\n"); + } + if (!file_exists(filename_rw, child_fapl_id)) { + SPLITTER_TEST_FAULT("R/W file mysteriously disappared\n"); + } + if (!file_exists(vfd_config.wo_path, child_fapl_id)) { + SPLITTER_TEST_FAULT("W/O file mysteriously disappeared\n"); + } + if (h5_compare_file_bytes(filename_rw, vfd_config.wo_path) < 0) { + SPLITTER_TEST_FAULT("files are not byte-for-byte equivalent\n"); + } + + /* H5Fcreate() with TRUNC access. + * Both files already exist. + */ + + file_id = H5Fcreate(filename_rw, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id); + if (file_id == H5I_INVALID_HID) { + SPLITTER_TEST_FAULT("file-open failed with both present\n"); + } + /* Open successful; close file then inspect presence again */ + if (H5Fclose(file_id) < 0) { + SPLITTER_TEST_FAULT("can't close file ID\n"); + } + if (!file_exists(filename_rw, child_fapl_id)) { + SPLITTER_TEST_FAULT("R/W file mysteriously disappared\n"); + } + if (!file_exists(vfd_config.wo_path, child_fapl_id)) { + SPLITTER_TEST_FAULT("W/O file mysteriously disappeared\n"); + } + if (h5_compare_file_bytes(filename_rw, vfd_config.wo_path) < 0) { + SPLITTER_TEST_FAULT("files are not byte-for-byte equivalent\n"); + } + + /* H5Fcreate() with TRUNC access. + * R/W already exists. + */ + + HDremove(filename_rw); + HDremove(vfd_config.wo_path); + if (splitter_create_single_file_at(filename_rw, child_fapl_id, &data) < 0) { + SPLITTER_TEST_FAULT("can't write R/W file\n"); + } + + if (file_exists(vfd_config.wo_path, child_fapl_id)) { + SPLITTER_TEST_FAULT("failed to remove W/O file\n"); + } + file_id = H5Fcreate(filename_rw, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id); + if (file_id == H5I_INVALID_HID) { + SPLITTER_TEST_FAULT("file-open failed with both present\n"); + } + /* Open successful; close file then inspect presence again */ + if (H5Fclose(file_id) < 0) { + SPLITTER_TEST_FAULT("can't close file ID\n"); + } + if (!file_exists(filename_rw, child_fapl_id)) { + SPLITTER_TEST_FAULT("R/W file mysteriously disappared\n"); + } + if (!file_exists(vfd_config.wo_path, child_fapl_id)) { + SPLITTER_TEST_FAULT("W/O file mysteriously disappeared\n"); + } + if (h5_compare_file_bytes(filename_rw, vfd_config.wo_path) < 0) { + SPLITTER_TEST_FAULT("files are not byte-for-byte equivalent\n"); + } + + /* H5Fcreate() with TRUNC access. + * W/O already exists. + */ + + HDremove(filename_rw); + HDremove(vfd_config.wo_path); + if (splitter_create_single_file_at(vfd_config.wo_path, child_fapl_id, &data) < 0) { + SPLITTER_TEST_FAULT("can't write R/W file\n"); + } + + if (file_exists(filename_rw, child_fapl_id)) { + SPLITTER_TEST_FAULT("failed to remove R/W file\n"); + } + file_id = H5Fcreate(filename_rw, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id); + if (file_id == H5I_INVALID_HID) { + SPLITTER_TEST_FAULT("file-open failed with both present\n"); + } + /* Open successful; close file then inspect presence again */ + if (H5Fclose(file_id) < 0) { + SPLITTER_TEST_FAULT("can't close file ID\n"); + } + if (!file_exists(filename_rw, child_fapl_id)) { + SPLITTER_TEST_FAULT("R/W file mysteriously disappared\n"); + } + if (!file_exists(vfd_config.wo_path, child_fapl_id)) { + SPLITTER_TEST_FAULT("W/O file mysteriously disappeared\n"); + } + if (h5_compare_file_bytes(filename_rw, vfd_config.wo_path) < 0) { + SPLITTER_TEST_FAULT("files are not byte-for-byte equivalent\n"); + } + + /* H5Fcreate with both files absent is tested elsewhere */ + + /* Cleanup + */ + + if (H5Pclose(fapl_id) < 0) { + SPLITTER_TEST_FAULT("can't close splitter FAPL ID\n"); + } + +done: + if (ret_value < 0) { + H5E_BEGIN_TRY { + (void)H5Pclose(fapl_id); + (void)H5Fclose(file_id); + } H5E_END_TRY; + } /* end if error */ + return ret_value; +} /* end splitter_tentative_open_test() */ + + +/*------------------------------------------------------------------------- + * Function: file_exists() + * + * Purpose: Determine whether a file exists on-system + * + * Return: Non-zero (1) if it exists (H5Fopen successful), + * zero (0) if absent (cannot be opened). + * + * Description: Attempt H5Fopen with the given FAPL ID and RDONLY access flag. + * + *------------------------------------------------------------------------- + */ +static int +file_exists(const char *filename, hid_t fapl_id) +{ + hid_t file_id = H5I_INVALID_HID; + int ret_value = 0; + + H5E_BEGIN_TRY { + file_id = H5Fopen(filename, H5F_ACC_RDONLY, fapl_id); + } H5E_END_TRY; + if (file_id != H5I_INVALID_HID) { + ret_value = 1; + if (H5Fclose(file_id) < 0) { + FAIL_PUTS_ERROR("can't close file ID\n"); + } + } + + return ret_value; + +error: + H5E_BEGIN_TRY { + (void)H5Fclose(file_id); + } H5E_END_TRY; + return ret_value; +} /* end file_exists() */ + + +/*------------------------------------------------------------------------- + * Function: test_splitter + * + * Purpose: Tests the Splitter VFD + * + * Return: Success: 0 + * Failure: -1 + * + * Description: + * This test function uses the Splitter VFD to produce a r/w + * file and a w/o file. It will verify that the two files + * are identical. + * + *------------------------------------------------------------------------- + */ +static herr_t +test_splitter(void) +{ + int buf[SPLITTER_SIZE][SPLITTER_SIZE]; + hsize_t dims[2] = { SPLITTER_SIZE, SPLITTER_SIZE }; + hid_t child_fapl_id = H5I_INVALID_HID; + int i = 0; + int j = 0; + struct splitter_dataset_def data; + + TESTING("SPLITTER file driver"); + + /* pre-fill data buffer to write */ + for (i=0; i < SPLITTER_SIZE; i++) { + for (j=0; j < SPLITTER_SIZE; j++) { + buf[i][j] = i*100+j; + } + } + + /* Dataset info */ + data.buf = (void *)buf; + data.mem_type_id = H5T_NATIVE_INT; + data.dims = dims; + data.n_dims = 2; + data.dset_name = SPLITTER_DATASET_NAME; + + /* Stand-in for manual FAPL creation + * Enables verification with arbitrary VFDs via `make check-vfd` + */ + child_fapl_id = h5_fileaccess(); + if (child_fapl_id < 0) { + TEST_ERROR; + } + + if (!driver_is_splitter_compatible(child_fapl_id)) { + SKIPPED(); + HDprintf(" given driver is not Splitter W/O compatible.\n"); + return 0; + } + + /* Test Read-Only access, including when a file on the W/O channel + * does not exist. + */ + if (splitter_RO_test(&data, child_fapl_id) < 0) { + TEST_ERROR; + } + + /* Test opening of files when the W/O channel does not exist. + */ + if (splitter_tentative_open_test(child_fapl_id) < 0) { + TEST_ERROR; + } + + + /* Test file creation, utilizing different child FAPLs (default vs. + * specified), logfile, and Write Channel error ignoring behavior. + */ + for (i=0; i < 4; i++) { + hbool_t ignore_wo_errors = (i & 1) ? TRUE : FALSE; + hbool_t provide_logfile_path = (i & 2) ? TRUE : FALSE; + hid_t child_fapl_ids[2] = { H5P_DEFAULT, H5P_DEFAULT }; + + /* Test child driver definition/default combination */ + for (j=0; j < 4; j++) { + + child_fapl_ids[0] = (j & 1) ? child_fapl_id : H5P_DEFAULT; + child_fapl_ids[1] = (j & 2) ? child_fapl_id : H5P_DEFAULT; + + if (run_splitter_test(&data, ignore_wo_errors, provide_logfile_path, child_fapl_ids) < 0) { + TEST_ERROR; + } + + } /* end for child fapl definition/pairing */ + + } /* end for behavior-flag loops */ + +/* TODO: SWMR open? */ +/* Concurrent opens with both drivers using the Splitter */ + + + if (H5Pclose(child_fapl_id) == FAIL) { + TEST_ERROR; + } + + PASSED(); + return 0; + +error: + if (child_fapl_id != H5I_INVALID_HID) { + (void)H5Pclose(child_fapl_id); + } + return -1; +} /* end test_splitter() */ + +#undef SPLITTER_TEST_FAULT + + /*------------------------------------------------------------------------- * Function: main * @@ -2187,9 +3276,6 @@ error: * Return: Success: 0 * Failure: 1 * - * Programmer: Raymond Lu - * Tuesday, Sept 24, 2002 - * *------------------------------------------------------------------------- */ int @@ -2213,6 +3299,7 @@ main(void) nerrors += test_stdio() < 0 ? 1 : 0; nerrors += test_windows() < 0 ? 1 : 0; nerrors += test_ros3() < 0 ? 1 : 0; + nerrors += test_splitter() < 0 ? 1 : 0; if(nerrors) { HDprintf("***** %d Virtual File Driver TEST%s FAILED! *****\n", diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt new file mode 100644 index 0000000..2d5626e --- /dev/null +++ b/utils/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required (VERSION 3.10) +project (HDF5_UTILS C) + +add_subdirectory (mirror_vfd) diff --git a/utils/COPYING b/utils/COPYING new file mode 100644 index 0000000..4a23112 --- /dev/null +++ b/utils/COPYING @@ -0,0 +1,12 @@ + + Copyright by The HDF Group. + All rights reserved. + + The files and subdirectories in this directory are 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. + diff --git a/utils/Makefile.am b/utils/Makefile.am new file mode 100644 index 0000000..b4409ac --- /dev/null +++ b/utils/Makefile.am @@ -0,0 +1,26 @@ +# +# 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. +## +## Makefile.am +## Run automake to generate a Makefile.in from this file. +## +# +# Utils HDF5 Makefile(.in) +# + +include $(top_srcdir)/config/commence.am + +CONFIG=ordered + +# All subdirectories +SUBDIRS=mirror_vfd + +include $(top_srcdir)/config/conclude.am diff --git a/utils/mirror_vfd/CMakeLists.txt b/utils/mirror_vfd/CMakeLists.txt new file mode 100644 index 0000000..405c420 --- /dev/null +++ b/utils/mirror_vfd/CMakeLists.txt @@ -0,0 +1,64 @@ +cmake_minimum_required (VERSION 3.10) +project (HDF5_UTILS_MIRRORVFD C) + +#----------------------------------------------------------------------------- +# Add the mirror_server executable +#----------------------------------------------------------------------------- + +set (mirror_server_SOURCES + ${HDF5_UTILS_MIRRORVFD_SOURCE_DIR}/mirror_remote.c + ${HDF5_UTILS_MIRRORVFD_SOURCE_DIR}/mirror_server.c + ${HDF5_UTILS_MIRRORVFD_SOURCE_DIR}/mirror_writer.c) +add_executable (mirror_server ${mirror_server_SOURCES}) +target_include_directories (mirror_server PRIVATE "${HDF5_UITLS_DIR};${HDF5_SRC_DIR};${HDF5_BINARY_DIR};$<$:${MPI_C_INCLUDE_DIRS}>") +if (NOT BUILD_SHARED_LIBS) + TARGET_C_PROPERTIES (mirror_server STATIC) + target_link_libraries (mirror_server PRIVATE ${HDF5_TOOLS_LIB_TARGET} ${HDF5_LIB_TARGET}) +else () + TARGET_C_PROPERTIES (mirror_server SHARED) + target_link_libraries (mirror_server PRIVATE ${HDF5_TOOLS_LIBSH_TARGET} ${HDF5_LIBSH_TARGET}) +endif () +set_target_properties (mirror_server PROPERTIES FOLDER utils) +set_global_variable (HDF5_UTILS_TO_EXPORT "${HDF5_UTILS_TO_EXPORT};mirror_server") +set (H5_DEP_EXECUTABLES ${H5_DEP_EXECUTABLES} mirror_server) + +#----------------------------------------------------------------------------- +# Add the mirror_server_halten_sie executable +#----------------------------------------------------------------------------- + +set (mirror_server_halt_SOURCES ${HDF5_UTILS_MIRRORVFD_SOURCE_DIR}/mirror_server_halten_sie.c) +add_executable (mirror_server_halt ${mirror_server_halt_SOURCES}) +target_include_directories (mirror_server_halt PRIVATE "${HDF5_UITLS_DIR};${HDF5_SRC_DIR};${HDF5_BINARY_DIR};$<$:${MPI_C_INCLUDE_DIRS}>") +if (NOT BUILD_SHARED_LIBS) + TARGET_C_PROPERTIES (mirror_server_halt STATIC) + target_link_libraries (mirror_server_halt PRIVATE ${HDF5_TOOLS_LIB_TARGET} ${HDF5_LIB_TARGET}) +else () + TARGET_C_PROPERTIES (mirror_server_halt SHARED) + target_link_libraries (mirror_server_halt PRIVATE ${HDF5_TOOLS_LIBSH_TARGET} ${HDF5_LIBSH_TARGET}) +endif () +set_target_properties (mirror_server_halt PROPERTIES FOLDER utils) +set_global_variable (HDF5_UTILS_TO_EXPORT "${HDF5_UTILS_TO_EXPORT};mirror_server_halt") +set (H5_DEP_EXECUTABLES ${H5_DEP_EXECUTABLES} mirror_server_halt) + +############################################################################## +############################################################################## +### I N S T A L L A T I O N ### +############################################################################## +############################################################################## + +#----------------------------------------------------------------------------- +# Rules for Installation of tools using make Install target +#----------------------------------------------------------------------------- +if (HDF5_EXPORTED_TARGETS) + foreach (exec ${H5_DEP_EXECUTABLES}) + INSTALL_PROGRAM_PDB (${exec} ${HDF5_INSTALL_BIN_DIR} utilsapplications) + endforeach () + + install ( + TARGETS + ${H5_DEP_EXECUTABLES} + EXPORT + ${HDF5_EXPORTED_TARGETS} + RUNTIME DESTINATION ${HDF5_INSTALL_BIN_DIR} COMPONENT utilsapplications + ) +endif () diff --git a/utils/mirror_vfd/Makefile.am b/utils/mirror_vfd/Makefile.am new file mode 100644 index 0000000..448e4cd --- /dev/null +++ b/utils/mirror_vfd/Makefile.am @@ -0,0 +1,30 @@ +# +# 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. +## +## Makefile.am +## Run automake to generate a Makefile.in from this file. +# +# HDF5 Library Makefile(.in) +# + +include $(top_srcdir)/config/commence.am + +AM_CPPFLAGS+=-I$(top_srcdir)/src + +bin_PROGRAMS = mirror_server mirror_server_halten_sie + +mirror_server_SOURCES = mirror_server.c mirror_writer.c mirror_remote.c +#mirror_writer_SOURCES = mirror_writer.c mirror_remote.c + +# All programs depend on the hdf5 library +LDADD=$(LIBHDF5) + +include $(top_srcdir)/config/conclude.am diff --git a/utils/mirror_vfd/mirror_remote.c b/utils/mirror_vfd/mirror_remote.c new file mode 100644 index 0000000..81a3625 --- /dev/null +++ b/utils/mirror_vfd/mirror_remote.c @@ -0,0 +1,225 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* Common operations for "remote" processes for the Mirror VFD. + * + * Jacob Smith, 2020-03-06 + */ + +#include "mirror_remote.h" + +#ifdef H5_HAVE_MIRROR_VFD + + +/* --------------------------------------------------------------------------- + * Function: mirror_log + * + * Purpose: Write message to the logging stream/file. + * If logging info pointer is NULL, uses logging defaults. + * ---------------------------------------------------------------------------- + */ +void +mirror_log(struct mirror_log_info *info, + unsigned int level, + const char *format, + ...) +{ + FILE *stream = MIRROR_LOG_DEFAULT_STREAM; + unsigned int verbosity = MIRROR_LOG_DEFAULT_VERBOSITY; + hbool_t custom = FALSE; + + if (info != NULL && info->magic == MIRROR_LOG_INFO_MAGIC) { + stream = info->stream; + verbosity = info->verbosity; + custom = TRUE; + } + + if (level == V_NONE) { + return; + } + else + if (level <= verbosity) { + if (custom == TRUE && info->prefix[0] != '\0') { + HDfprintf(stream, "%s", info->prefix); + } + + switch (level) { + case (V_ERR) : + HDfprintf(stream, "ERROR "); + break; + case (V_WARN) : + HDfprintf(stream, "WARNING "); + break; + default: + break; + } + + if (format != NULL) { + va_list args; + HDva_start(args, format); + HDvfprintf(stream, format, args); + HDva_end(args); + } + + HDfprintf(stream, "\n"); + HDfflush(stream); + } /* end if sufficiently verbose to print */ +} /* end mirror_log() */ + + +/* --------------------------------------------------------------------------- + * Function: session_log_bytes + * + * Purpose: "Pretty-print" raw binary data to logging stream/file. + * If info pointer is NULL, uses logging defaults. + * ---------------------------------------------------------------------------- + */ +void +mirror_log_bytes(struct mirror_log_info *info, + unsigned int level, + size_t n_bytes, + const unsigned char *buf) +{ + FILE *stream = MIRROR_LOG_DEFAULT_STREAM; + unsigned int verbosity = MIRROR_LOG_DEFAULT_VERBOSITY; + + if (buf == NULL) { + return; + } + + if (info != NULL && info->magic == MIRROR_LOG_INFO_MAGIC) { + stream = info->stream; + verbosity = info->verbosity; + } + + if (level <= verbosity) { + size_t bytes_written = 0; + const unsigned char *b = NULL; + + /* print whole lines */ + while ((n_bytes - bytes_written) >= 32) { + b = buf + bytes_written; /* point to region in buffer */ + HDfprintf(stream, + "%04zX %02X%02X%02X%02X %02X%02X%02X%02X" \ + " %02X%02X%02X%02X %02X%02X%02X%02X" \ + " %02X%02X%02X%02X %02X%02X%02X%02X" \ + " %02X%02X%02X%02X %02X%02X%02X%02X\n", + bytes_written, + b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], + b[8], b[9], b[10],b[11], b[12],b[13],b[14],b[15], + b[16],b[17],b[18],b[19], b[20],b[21],b[22],b[23], + b[24],b[25],b[26],b[27], b[28],b[29],b[30],b[31]); + bytes_written += 32; + } + + /* start partial line */ + if (n_bytes > bytes_written) { + HDfprintf(stream, "%04zX ", bytes_written); + } + + /* partial line blocks */ + while ((n_bytes - bytes_written) >= 4) { + HDfprintf(stream, " %02X%02X%02X%02X", + buf[bytes_written], buf[bytes_written+1], + buf[bytes_written+2], buf[bytes_written+3]); + bytes_written += 4; + } + + /* block separator before partial block */ + if (n_bytes > bytes_written) { + HDfprintf(stream, " "); + } + + /* partial block individual bytes */ + while (n_bytes > bytes_written) { + HDfprintf(stream, "%02X", buf[bytes_written++]); + } + + /* end partial line */ + HDfprintf(stream, "\n"); + } /* end if suitably verbose to log */ +} /* end mirror_log_bytes() */ + + +/* --------------------------------------------------------------------------- + * Function: mirror_log_init + * + * Purpose: Prepare a loginfo_t structure for use. + * + * Return: Success: Pointer to newly-ceated info. + * Failure: NULL. Either unable to allocate or cannot open file. + * ---------------------------------------------------------------------------- + */ +loginfo_t * +mirror_log_init(char *path, char *prefix, unsigned int verbosity) +{ + loginfo_t *info = NULL; + + info = (loginfo_t *)HDmalloc(sizeof(loginfo_t)); + if (info != NULL) { + info->magic = MIRROR_LOG_INFO_MAGIC; + info->verbosity = verbosity; + info->stream = MIRROR_LOG_DEFAULT_STREAM; + info->prefix[0] = '\0'; + + if (prefix && *prefix) { + HDstrncpy(info->prefix, prefix, MIRROR_LOG_PREFIX_MAX); + } + + if (path && *path) { + FILE *f = NULL; + f = HDfopen(path, "w"); + if (NULL == f) { + HDfprintf(MIRROR_LOG_DEFAULT_STREAM, + "WARN custom logging path could not be opened: %s\n", + path); + info->magic += 1; + HDfree(info); + } + else { + info->stream = f; + } + } + + } /* end if able to allocate */ + + return info; +} /* end mirror_log_init() */ + + +/* --------------------------------------------------------------------------- + * Function: mirror_log_term + * + * Purpose: Shut down and clean up a loginfo_t structure. + * + * Return: Success: SUCCEED. Resources released. + * Failure: FAIL. Indeterminite state. + * ---------------------------------------------------------------------------- + */ +herr_t +mirror_log_term(loginfo_t *info) +{ + if (info == NULL || info->magic != MIRROR_LOG_INFO_MAGIC) { + return FAIL; + } + if (info->stream != stderr || info->stream != stdout) { + if (HDfclose(info->stream) < 0) { + return FAIL; + } + } + info->magic += 1; + HDfree(info); + return SUCCEED; +} /* end mirror_log_term() */ + +#endif /* H5_HAVE_MIRROR_VFD */ + diff --git a/utils/mirror_vfd/mirror_remote.h b/utils/mirror_vfd/mirror_remote.h new file mode 100644 index 0000000..9132d51 --- /dev/null +++ b/utils/mirror_vfd/mirror_remote.h @@ -0,0 +1,51 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* Common definitions for "remote" processes for the Mirror VFD. + * + * Jacob Smith, 2020-03-06 + */ + +#include "hdf5.h" +#include "H5private.h" + +#ifdef H5_HAVE_MIRROR_VFD + +#define V_NONE 0 +#define V_ERR 1 +#define V_WARN 2 +#define V_INFO 3 +#define V_ALL 4 + +#define MIRROR_LOG_DEFAULT_STREAM stdout +#define MIRROR_LOG_DEFAULT_VERBOSITY V_WARN +#define MIRROR_LOG_PREFIX_MAX 79 +#define MIRROR_LOG_INFO_MAGIC 0x569D589A + +typedef struct mirror_log_info { + uint32_t magic; + FILE *stream; + unsigned int verbosity; + char prefix[MIRROR_LOG_PREFIX_MAX+1]; +} loginfo_t; + +void mirror_log(loginfo_t *info, unsigned int level, + const char *format, ...); +void mirror_log_bytes(loginfo_t *info, unsigned int level, + size_t n_bytes, const unsigned char *buf); +loginfo_t *mirror_log_init(char *path, char *prefix, unsigned int verbosity); +int mirror_log_term(loginfo_t *loginfo); + +herr_t run_writer(int socketfd, H5FD_mirror_xmit_open_t *xmit_open); + +#endif /* H5_HAVE_MIRROR_VFD */ + diff --git a/utils/mirror_vfd/mirror_server.c b/utils/mirror_vfd/mirror_server.c new file mode 100644 index 0000000..314f067 --- /dev/null +++ b/utils/mirror_vfd/mirror_server.c @@ -0,0 +1,645 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * "Server" application to associate a Mirror VFD Driver with a Writer. + * + * Server waits on a dedicated port for a Driver to attempt to connect. + * When connection is made, reads a message from the Driver. + * If message is "SHUTDOWN", Server closes connection and terminates. + * Else, if it receives an encoded OPEN xmit (from Driver), the Server forks + * itself; the child becomes a dedicated Writer and maintains connection with + * the Driver instance, and the parent process remains a Server and returns + * to listening for incoming requests. + * Else, the message is not recognized and is ignored. + * + * + * + * mirror_server [args] + * + * Primary server for coordinating mirror VFD connections with the remote + * process. + * + * args: + * --help, -h Print help message and exit. + * --port=N Positive integer for primary listening port. + * --verbosity=N Debugging verbosity + * 0: none + * 1: errors + * 2: details + * 3: all + * --logpath=S File path to direct debugging output, if any. + * Default of none prints output to stdout. + * + */ + +#include "mirror_remote.h" + +#ifdef H5_HAVE_MIRROR_VFD + +#define MAXBUF 2048 /* max buffer length. */ +#define LISTENQ 80 /* max pending mirrorS requests */ +#define DEFAULT_PORT 3000 /* default listening port */ +#define MAX_PORT_LOOPS 20 /* max iteratations through port range */ +#define PORT_LOOP_RETRY_DELAY 1 /* seconds to wait between port scans */ + +/* semi-unique "magic" numbers to sanity-check structure pointers */ +#define OP_ARGS_MAGIC 0xCF074379u +#define SERVER_RUN_MAGIC 0x741B459Au + + +/* --------------------------------------------------------------------------- + * Structure: struct op_args + * + * Purpose: Convenience structure for holding arguments from command-line. + * + * `magic` (uint32_t) + * Semi-unique number to help validate a pointer to this struct type. + * Must be OP_ARGS_MAGIC to be considered valid. + * + * `help` (int) + * Flag that the help argument was present in the command line. + * + * `main_port` (int) + * Flag that the help argument was present in the command line. + * + * `verbosity` (int) + * Number between 0 (none) and 4 (all) that controls how much detail + * the program prints as a course of logging. + * + * `log_prepend_serv` (int) + * Flag that the logging messages should have 'S- ' at the start of each + * line. + * + * `log_prepend_type` (int) + * Flag that the logging messages should have the assocaited verbosity + * level present in the line (e.g., "WARN", "ERROR", or "INFO"). + * + * `log_path` (char *) + * Path string from the command line, giving the absolute path + * for the file for logging output. Can be empty. + * + * `writer_log_path` (char *) + * Path string from the command line, giving the absolute path + * for the file for writer's logging output. Can be empty. + * + * --------------------------------------------------------------------------- + */ +struct op_args { + uint32_t magic; + int help; + int main_port; + int verbosity; + int log_prepend_serv; + int log_prepend_type; + char log_path[PATH_MAX+1]; + char writer_log_path[PATH_MAX+1]; +}; + + +/* --------------------------------------------------------------------------- + * Structure: struct server_run + * + * Purpose: Convenience structure for holding information about a server + * in operation. + * + * `magic` (uint32_t) + * Semi-unique number to help validate a pointer to this struct type. + * Must be SERVER_RUN_MAGIC to be considered valid. + * + * `log_stream` (FILE *) + * File handle where logging output is directed. + * By default, is stdout. + * + * `opts` (struct opt_args) + * Contained structure, holds the server's configuration. + * + * `listenfd` (int) + * File descriptor of the listening socket. + * + * --------------------------------------------------------------------------- + */ +struct server_run { + uint32_t magic; + struct op_args opts; + struct mirror_log_info *loginfo; + int listenfd; +}; + + +/* --------------------------------------------------------------------------- + * Function: usage + * + * Purpose: Print the usage message to stdout. + * --------------------------------------------------------------------------- + */ +static void +usage(void) +{ + HDfprintf(stdout, + "mirror_server [options]\n" \ + "\n" \ + "Application for providing Mirror Writer process to " \ + " Mirror VFD on file-open.\n" \ + "Listens on a dedicated socket; forks as a Writer upon receipt" \ + " of a valid OPEN xmit.\n" \ + "\n" \ + "Options:\n" \ + "--help [-h] : Print this help message and quit.\n" \ + "--logpath=PATH : File path for logging output " \ + "(default none, to stdout).\n" \ + "--port=PORT : Primary port (default %d).\n" \ + "--verbosity=NUM : Debug printing level " \ + "0..4, (default %d).\n", + DEFAULT_PORT, + MIRROR_LOG_DEFAULT_VERBOSITY); +} /* end usage() */ + + +/* --------------------------------------------------------------------------- + * Function: parse_args + * + * Purpose: Read command line options and store results in args_out + * structure. Fails in event of unrecognized option. + * + * Return: 0 on success, -1 on failure. + * --------------------------------------------------------------------------- + */ +static int +parse_args(int argc, char **argv, struct op_args *args_out) +{ + int i; + + /* preset default values + */ + args_out->main_port = DEFAULT_PORT; + args_out->help = 0; + args_out->log_prepend_serv = 1; + args_out->log_prepend_type = 1; + args_out->verbosity = MIRROR_LOG_DEFAULT_VERBOSITY; + /* preset empty strings */ + HDbzero(args_out->log_path, PATH_MAX+1); + HDbzero(args_out->writer_log_path, PATH_MAX+1); + + if (argv == NULL || *argv == NULL) { + mirror_log(NULL, V_ERR, "invalid argv pointer"); + return -1; + } + + /* Loop over arguments after program name and writer_path */ + for (i=2; i < argc; i++) { + if (!HDstrncmp(argv[i], "-h", 3) || !HDstrncmp(argv[i], "--help", 7)) { + mirror_log(NULL, V_INFO, "found help argument"); + args_out->help = 1; + return 0; + } /* end if help */ + else + if (!HDstrncmp(argv[i], "--port=", 7)) { + mirror_log(NULL, V_INFO, "parsing 'main_port' (%s)", argv[i]+7); + args_out->main_port = HDatoi(argv[i]+7); + } /* end if port */ + else + if (!HDstrncmp(argv[i], "--verbosity=", 12)) { + mirror_log(NULL, V_INFO, "parsing 'verbosity' (%s)", argv[i]+12); + args_out->verbosity = HDatoi(argv[i]+12); + } /* end if verbosity */ + else + if (!HDstrncmp(argv[i], "--logpath=", 10)) { + mirror_log(NULL, V_INFO, "parsing 'logpath' (%s)", argv[i]+10); + HDstrncpy(args_out->log_path, argv[i]+10, PATH_MAX); + } /* end if logpath */ + else { + mirror_log(NULL, V_ERR, "unrecognized argument: %s", argv[i]); + return -1; + } /* end if unrecognized argument */ + } /* end for each arg after the path to writer "receiver process" */ + + mirror_log(NULL, V_INFO, "all args parsed"); + + return 0; +} /* end parse_args() */ + + +/* --------------------------------------------------------------------------- + * Function: prepare_listening_socket + * + * Purpose: Configure and open a socket. + * In event of error, attempts to undo its processes. + * + * Return: Success: non-negative (the file descriptor of the socket) + * Failure: -1 + * --------------------------------------------------------------------------- + */ +static int +prepare_listening_socket(struct server_run *run) +{ + struct sockaddr_in server_addr; + int _true = 1; /* needed for setsockopt() */ + int ret_value = -1; + int ret = 0; /* for checking return value of function calls */ + + if (run == NULL || run->magic != SERVER_RUN_MAGIC) { + mirror_log(NULL, V_ERR, "invalid server_run pointer"); + return -1; + } + + mirror_log(run->loginfo, V_INFO, "preparing socket"); + + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = HDhtonl(INADDR_ANY); + server_addr.sin_port = HDhtons((uint16_t)run->opts.main_port); + + mirror_log(run->loginfo, V_INFO, "socket()"); + ret_value = HDsocket(AF_INET, SOCK_STREAM, 0); + if (ret_value < 0) { + mirror_log(run->loginfo, V_ERR, "listening socket:%d", ret_value); + goto error; + } + + mirror_log(run->loginfo, V_ALL, "setsockopt()"); + HDsetsockopt(ret_value, SOL_SOCKET, SO_REUSEADDR, &_true, sizeof(int)); + + mirror_log(run->loginfo, V_INFO, "bind()"); + ret = HDbind(ret_value, (struct sockaddr *)&server_addr, + sizeof(server_addr)); + if (ret < 0) { + mirror_log(run->loginfo, V_ERR, "bind() %s", HDstrerror(errno)); + goto error; + } + + mirror_log(run->loginfo, V_INFO, "listen()"); + ret = HDlisten(ret_value, LISTENQ); + if (ret < 0) { + mirror_log(run->loginfo, V_ERR, "H5FD server listen:%d", ret); + goto error; + } + + return ret_value; + +error: + if (ret_value >= 0) { + HDshutdown(ret_value, SHUT_RDWR); + HDclose(ret_value); + } + return -1; +} /* end prepare_listening_socket() */ + + +/* --------------------------------------------------------------------------- + * Function: init_server_run + * + * Purpose: Set up server_run struct with default and specified values. + * + * Return: Zero (0) if successful, -1 if an error occurred. + * --------------------------------------------------------------------------- + */ +static struct server_run * +init_server_run(int argc, char **argv) +{ + struct server_run *run; + + run = (struct server_run *)HDmalloc(sizeof(struct server_run)); + if (run == NULL) { + mirror_log(NULL, V_ERR, "can't allocate server_run struct"); + return NULL; + } + + run->magic = (uint32_t)SERVER_RUN_MAGIC; + run->opts.magic = (uint32_t)OP_ARGS_MAGIC; + run->listenfd = -1; + + if (parse_args(argc, argv, &(run->opts)) < 0) { + mirror_log(NULL, V_ERR, "can't parse arguments"); + usage(); + goto error; + } + + if (run->opts.help) { + usage(); + return run; /* early exit */ + } + + run->loginfo = mirror_log_init(run->opts.log_path, "s- ", + run->opts.verbosity); + + run->listenfd = prepare_listening_socket(run); + if (run->listenfd < 0) { + mirror_log(NULL, V_ERR, "can't prepare listening socket"); + goto error; + } + + return run; + +error: + if (run != NULL) { + HDfree(run); + } + return NULL; + +} /* end init_server_run() */ + + +/* --------------------------------------------------------------------------- + * Function: term_server_run + * + * Purpose: Close opened items in a sever_run and release the pointer. + * + * Return: Zero (0) if successful, -1 if an error occurred. + * --------------------------------------------------------------------------- + */ +static int +term_server_run(struct server_run *run) +{ + if (run == NULL || run->magic != SERVER_RUN_MAGIC) { + mirror_log(NULL, V_ERR, "invalid server_run pointer"); + return -1; + } + + mirror_log(run->loginfo, V_INFO, "shutting down"); + + if (run->listenfd >= 0) { + HDshutdown(run->listenfd, SHUT_RDWR); /* TODO: error-checking? */ + HDclose(run->listenfd); /* TODO: error-checking? */ + run->listenfd = -1; + } + + if (mirror_log_term(run->loginfo) < 0) { + mirror_log(NULL, V_ERR, "can't close logging stream"); + return -1; /* doesn't solve the problem, but informs of error */ + } + run->loginfo = NULL; + + (run->magic)++; + (run->opts.magic)++; + HDfree(run); + return 0; +} /* end term_server_run() */ + + +/* --------------------------------------------------------------------------- + * Function: accept_connection + * + * Purpose: Main working loop; process requests as they are received. + * Does nothing if the run option help is set. + * + * Return: -1 on error, else a non-negative file descriptor of the socket. + * --------------------------------------------------------------------------- + */ +static int +accept_connection(struct server_run *run) +{ + struct sockaddr_in client_addr; /**/ + socklen_t clilen; /**/ + struct hostent *host_port = NULL; /**/ + char *hostaddrp; /**/ + int connfd = -1; /* connection file descriptor */ + + if (run == NULL || run->magic != SERVER_RUN_MAGIC) { + mirror_log(NULL, V_ERR, "invalid server_run pointer"); + return -1; + } + + /*------------------------------*/ + /* accept a connection on a socket */ + clilen = sizeof(client_addr); + connfd = HDaccept(run->listenfd, (struct sockaddr *)&client_addr, &clilen); + if (connfd < 0) { + mirror_log(run->loginfo, V_ERR, "accept:%d", connfd); + goto error; + } + mirror_log(run->loginfo, V_INFO, "connection achieved"); + + /*------------------------------*/ + /* get client address information */ + host_port = HDgethostbyaddr( + (const char *)&client_addr.sin_addr.s_addr, + sizeof(client_addr.sin_addr.s_addr), + AF_INET); + if (host_port == NULL) { + mirror_log(run->loginfo, V_ERR, "gethostbyaddr()"); + goto error; + } + + /* function has the string space statically scoped -- OK until next call */ + hostaddrp = HDinet_ntoa(client_addr.sin_addr); + /* TODO? proper error-checking */ + + mirror_log(run->loginfo, V_INFO, + "server connected with %s (%s)", + host_port->h_name, + hostaddrp); + + return connfd; + +error: + if (connfd >= 0) { + close(connfd); + } + return -1; +} /* end accept_connection() */ + + +/* --------------------------------------------------------------------------- + * Function: wait_for_child + * + * Purpose: Signal handler to reap zombie processes. + * --------------------------------------------------------------------------- + */ +static void +wait_for_child(int sig) +{ + while (HDwaitpid(-1, NULL, WNOHANG) > 0); +} /* end wait_for_child() */ + + +/* --------------------------------------------------------------------------- + * Function: handle_requests + * + * Purpose: Main working loop; process requests as they are received. + * Does nothing if the run option `help` is set. + * + * Return: -1 on error, else 0 for successful operation. + * --------------------------------------------------------------------------- + */ +static int +handle_requests(struct server_run *run) +{ + int connfd = -1; /**/ + char mybuf[H5FD_MIRROR_XMIT_OPEN_SIZE]; /**/ + int ret; /* general-purpose error-checking */ + int pid; /* process ID of fork */ + struct sigaction sa; + int ret_value = 0; + + if (run == NULL || run->magic != SERVER_RUN_MAGIC) { + mirror_log(NULL, V_ERR, "invalid server_run pointer"); + return -1; + } + + if (run->opts.help) { + return 0; + } + + if (run->listenfd < 0) { + mirror_log(NULL, V_ERR, "invalid listening socket"); + return -1; + } + + /* Set up the signal handler */ + sa.sa_handler = wait_for_child; + HDsigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + if (HDsigaction(SIGCHLD, &sa, NULL) == -1) { + perror("sigaction"); + return 1; + } + + /* Keep listening for attempts to connect. + */ + + while (1) { /* infinite loop, exited via break or goto */ + mirror_log(run->loginfo, V_INFO, "server waiting for connections..."); + + connfd = -1; + + connfd = accept_connection(run); + if (connfd < 0) { + mirror_log(run->loginfo, V_ERR, "unable to receive connection"); + goto error; + } + + /* Read handshake from port connection. + */ + + ret = (int)HDread(connfd, &mybuf, H5FD_MIRROR_XMIT_OPEN_SIZE); + if (-1 == ret) { + mirror_log(run->loginfo, V_ERR, "read:%d", ret); + goto error; + } + mirror_log(run->loginfo, V_INFO, "received %d bytes", ret); + mirror_log(run->loginfo, V_ALL, "```"); + mirror_log_bytes(run->loginfo, V_ALL, ret, + (const unsigned char *)mybuf); + mirror_log(run->loginfo, V_ALL, "```"); + + /* Respond to handshake message. + */ + + if (!HDstrncmp("SHUTDOWN", mybuf, 8)) { + /* Stop operation if told to stop */ + mirror_log(run->loginfo, V_INFO, "received SHUTDOWN!", ret); + HDclose(connfd); + connfd = -1; + goto done; + } /* end if explicit "SHUTDOWN" directive */ + else + if (H5FD_MIRROR_XMIT_OPEN_SIZE == ret) { + H5FD_mirror_xmit_open_t xopen; + + mirror_log(run->loginfo, V_INFO, + "probable OPEN xmit received"); + + H5FD_mirror_xmit_decode_open(&xopen, (const unsigned char *)mybuf); + if (FALSE == H5FD_mirror_xmit_is_open(&xopen)) { + mirror_log(run->loginfo, V_WARN, + "expected OPEN xmit was malformed"); + HDclose(connfd); + continue; + } + + mirror_log(run->loginfo, V_INFO, + "probable OPEN xmit confirmed"); + + pid = HDfork(); + if (pid < 0) { /* fork error */ + mirror_log(run->loginfo, V_ERR, "cannot fork"); + goto error; + } /* end if fork error */ + else + if (pid == 0) { /* child process (writer side of fork) */ + mirror_log(run->loginfo, V_INFO, + "executing writer"); + if (run_writer(connfd, &xopen) < 0) { + HDprintf("can't run writer\n"); + } + else { + HDprintf("writer OK\n"); + } + HDclose(connfd); + + HDexit(EXIT_SUCCESS); + } /* end if writer side of fork */ + else { /* parent process (server side of fork) */ + mirror_log(run->loginfo, V_INFO, "tidying up from handshake"); + HDclose(connfd); + } /* end if server side of fork */ + + } /* end else-if valid request for service */ + else { + /* Ignore unrecognized messages */ + HDclose(connfd); + continue; + } /* end else (not a valid message, to be ignored) */ + + } /* end while listening for new connections */ + +done: + if (connfd >= 0) { + mirror_log(run->loginfo, V_WARN, "connfd still open upon cleanup"); + HDclose(connfd); + } + + return ret_value; + +error: + if (connfd >= 0) { + HDclose(connfd); + } + return -1; +} /* end handle_requests() */ + + +/* ------------------------------------------------------------------------- */ +int +main(int argc, char **argv) +{ + struct server_run *run; + + run = init_server_run(argc, argv); + if (NULL == run) { + mirror_log(NULL, V_ERR, "can't initialize run"); + HDexit(EXIT_FAILURE); + } + + if (handle_requests(run) < 0) { + mirror_log(run->loginfo, V_ERR, "problem handling requests"); + } + + if (term_server_run(run) < 0) { + mirror_log(NULL, V_ERR, "problem closing server run"); + HDexit(EXIT_FAILURE); + } + + HDexit(EXIT_SUCCESS); +} /* end main() */ + +#else /* H5_HAVE_MIRROR_VFD */ + +int +main(int argc, char **argv) +{ + HDprintf("Mirror VFD was not built -- cannot launch server.\n"); + HDexit(EXIT_FAILURE); +} + +#endif /* H5_HAVE_MIRROR_VFD */ + diff --git a/utils/mirror_vfd/mirror_server_halten_sie.c b/utils/mirror_vfd/mirror_server_halten_sie.c new file mode 100644 index 0000000..ccd5824 --- /dev/null +++ b/utils/mirror_vfd/mirror_server_halten_sie.c @@ -0,0 +1,214 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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: Stop the mirror server + * Exists for cross-platform, optionally remote shutdown. + */ + +#include "H5private.h" /* System compatability call-wrapper macros */ + +#ifdef H5_HAVE_MIRROR_VFD + +#define MSHS_OPTS_MAGIC 0x613B1C15u /* sanity-checking constant */ +#define MSHS_IP_STR_SIZE 20 +#define MSHS_DEFAULT_IP "127.0.0.1" +#define MSHS_DEFAULT_PORTNO 3000 + + +/* ---------------------------------------------------------------------------- + * Structure: struct mshs_opts + * + * Purpose: Convenience structure to hold options as parsed from the + * command line. + * + * `magic` (uint32_t) + * Semi-unique constant to help verify pointer integrity. + * + * `help` (int) + * Flag that the help argument was present. + * + * `portno` (int) + * Port number, as received from arguments. + * + * `ip` (char *) + * IP address string as received from arguments. + * + * ---------------------------------------------------------------------------- + */ +struct mshs_opts { + uint32_t magic; + int help; + int portno; + char ip[MSHS_IP_STR_SIZE + 1]; +}; + + +/* ---------------------------------------------------------------------------- + * Function: usage + * + * Purpose: Print usage message to stdout. + * ---------------------------------------------------------------------------- + */ +static void +usage(void) +{ + HDprintf("mirror_server_halten_sie [options]\n" \ + "System-independent Mirror Server shutdown program.\n" \ + "Sends shutdown message to Mirror Server at given IP:port\n" \ + "\n" \ + "Options:\n" \ + " -h | --help Print this usage message and exit.\n" \ + " --ip=ADDR IP Address of remote server (defaut %s)\n" \ + " --port=PORT Handshake port of remote server (default %d)\n", + MSHS_DEFAULT_IP, + MSHS_DEFAULT_PORTNO); +} /* end usage() */ + + +/* ---------------------------------------------------------------------------- + * Function: parse_args + * + * Purpose: Parse command-line arguments, populating the options struct + * pointer as appropriate. + * Default values will be set for unspecified options. + * + * Return: 0 on success, negative (-1) if error. + * ---------------------------------------------------------------------------- + */ +static int +parse_args(int argc, char **argv, struct mshs_opts *opts) +{ + int i = 0; + + opts->magic = MSHS_OPTS_MAGIC; + opts->help = 0; + opts->portno = MSHS_DEFAULT_PORTNO; + HDstrncpy(opts->ip, MSHS_DEFAULT_IP, MSHS_IP_STR_SIZE); + + for (i=1; i < argc; i++) { /* start with first possible option argument */ + if (!HDstrncmp(argv[i], "-h", 3) || !HDstrncmp(argv[i], "--help", 7)) { + opts->help = 1; + } + else + if (!HDstrncmp(argv[i], "--ip=", 5)) { + HDstrncpy(opts->ip, argv[i]+5, MSHS_IP_STR_SIZE); + } + else + if (!HDstrncmp(argv[i], "--port=", 7)) { + opts->portno = HDatoi(argv[i]+7); + } + else { + HDprintf("Unrecognized option: '%s'\n", argv[i]); + usage(); + opts->magic++; /* invalidate for sanity */ + return -1; + } + } /* end for each argument from command line */ + + /* auto-replace 'localhost' with numeric IP */ + if (!HDstrncmp(opts->ip, "localhost", 10)) { /* include null terminator */ + HDstrncpy(opts->ip, "127.0.0.1", MSHS_IP_STR_SIZE); + } + + return 0; +} /* end parse_args() */ + + +/* ---------------------------------------------------------------------------- + * Function: send_shutdown + * + * Purpose: Create socket and send shutdown signal to remote server. + * + * Return: 0 on success, negative (-1) if error. + * ---------------------------------------------------------------------------- + */ +static int +send_shutdown(struct mshs_opts *opts) +{ + int live_socket; + struct sockaddr_in target_addr; + + if (opts->magic != MSHS_OPTS_MAGIC) { + HDprintf("invalid options structure\n"); + return -1; + } + + live_socket = HDsocket(AF_INET, SOCK_STREAM, 0); + if (live_socket < 0) { + HDprintf("ERROR socket()\n"); + return -1; + } + + target_addr.sin_family = AF_INET; + target_addr.sin_port = HDhtons((uint16_t)opts->portno); + target_addr.sin_addr.s_addr = HDinet_addr(opts->ip); + HDmemset(target_addr.sin_zero, '\0', sizeof(target_addr.sin_zero)); + + if (HDconnect(live_socket, (struct sockaddr *)&target_addr, + (socklen_t)sizeof(target_addr)) + < 0) + { + HDprintf("ERROR connect() (%d)\n%s\n", errno, HDstrerror(errno)); + return -1; + } + + if (HDwrite(live_socket, "SHUTDOWN", 9) == -1) { + HDprintf("ERROR write() (%d)\n%s\n", errno, HDstrerror(errno)); + return -1; + } + + if (HDclose(live_socket) < 0) { + HDprintf("ERROR close() can't close socket\n"); + return -1; + } + + return 0; +} /* end send_shutdown() */ + + +/* ------------------------------------------------------------------------- */ +int +main(int argc, char **argv) +{ + struct mshs_opts opts; + + if (parse_args(argc, argv, &opts) < 0) { + HDprintf("Unable to parse arguments\n"); + return EXIT_FAILURE; + } + + if (opts.help) { + usage(); + return EXIT_SUCCESS; + } + + if (send_shutdown(&opts) < 0) { + HDprintf("Unable to send shutdown command\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} /* end main() */ + +#else /* H5_HAVE_MIRROR_VFD */ + + +/* ------------------------------------------------------------------------- */ +int +main(int argc, char **argv) +{ + HDprintf("Mirror VFD not built -- unable to perform shutdown.\n"); + return EXIT_FAILURE; +} + +#endif /* H5_HAVE_MIRROR_VFD */ diff --git a/utils/mirror_vfd/mirror_writer.c b/utils/mirror_vfd/mirror_writer.c new file mode 100644 index 0000000..9cd9b4c --- /dev/null +++ b/utils/mirror_vfd/mirror_writer.c @@ -0,0 +1,1064 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Remote writer process for the mirror (socket) VFD. + * + * Writer is started with arguments for slaved port. + * Awaits a connection on the socket. + * Handles instructions from the master 'Driver' process. + * + * Current implementation uses Sec2 as the underlying driver when opening a + * file. This is reflected in the source (H5FDmirror.c) of the Mirror driver. + */ + +#include "mirror_remote.h" + +#ifdef H5_HAVE_MIRROR_VFD + +#define HEXDUMP_XMITS 1 /* Toggle whether to print xmit bytes-blob */ + /* in detailed logging */ +#define HEXDUMP_WRITEDATA 0 /* Toggle whether to print bytes to write */ + /* in detailed logging */ +#define LISTENQ 80 /* max pending Driver requests */ + +#define MW_SESSION_MAGIC 0x88F36B32u +#define MW_SOCK_COMM_MAGIC 0xDF10A157u +#define MW_OPTS_MAGIC 0x3BA8B462u + + +/* --------------------------------------------------------------------------- + * Structure: struct mirror_session + * + * Bundle of information used to manage the operation of this remote Writer + * in a "session" with the Driver process. + * + * magic (uint32_t) + * Semi-unique "magic" number used to sanity-check a structure for + * validity. MUST equal MW_SESSION_MAGIC to be valid. + * + * sockfd (int) + * File descriptor to the socket. + * Used for receiving bytes from and writing bytes to the Driver + * across the network. + * If not NULL, should be a valid descriptor. + * + * token (uint32t) + * Number used to help sanity-check received transmission from the Writer. + * Each Driver/Writer pairing should have a semi-unique "token" to help + * guard against commands from the wrong entity. + * + * xmit_count (uint32_t) + * Record of trasmissions received from the Driver. While the transmission + * protocol should be trustworthy, this serves as an additional guard. + * Starts a 0 and should be incremented for each one-way transmission. + * + * file (H5FD_t *) + * Virtual File handle for the hdf5 file. + * Set on file open if H5Fopen() is successful. If NULL, it is invalid. + * + * log_verbosity (unsigned int) + * The verbosity level for logging. Should be set to one of the values + * defined at the top of this file. + * + * log_stream (FILE *) + * File pointer to which logging output is written. Starts (and ends) + * with a default stream, such as stdout, but can be overridden at + * runtime. + * + * reply (H5FD_mirror_xmit_reply_t) + * Structure space for persistent reply data. + * Should be initialized with basic header info (magic, version, op), + * then with session info (token, xmit count), and finally with specific + * reply info (update xmit_count, status code, and message) before + * transmission. + * + * ---------------------------------------------------------------------------- + */ +struct mirror_session { + uint32_t magic; + int sockfd; + uint32_t token; + uint32_t xmit_count; + H5FD_t *file; + loginfo_t *loginfo; + H5FD_mirror_xmit_reply_t reply; +}; + + +/* --------------------------------------------------------------------------- + * Structure: struct sock_comm + * + * Structure for placing the data read and pre-processed from Driver in an + * organized fashion. Useful for pre-processing a received xmit. + * + * magic (uint32_t) + * Semi-unique number to sanity-check structure pointer and validity. + * Must equal MW_SOCK_COMM_MAGIC to be valid. + * + * recd_die (int) + * "Boolean" flag indicating that an explicit shutdown/kill/die command + * was received. Potentially useful for debugging and or "manual" + * operation of the program. + * 0 indicates normal operation, non-0 (1) indicates to die. + * + * xmit_recd (H5FD_mirror_xmit_t *) + * Structure pointer for the "xmit header" as decoded from the raw + * binary stream read from the socket. + * + * raw (char *) + * Pointer to a raw byte array, for storing data as read from the + * socket. Bytes buffer is decoded into xmit_t header and derivative + * structures. + * + * raw_size (size_t) + * Give the size of the `raw` buffer. + * + * --------------------------------------------------------------------------- + */ +struct sock_comm { + uint32_t magic; + int recd_die; + H5FD_mirror_xmit_t *xmit_recd; + char *raw; + size_t raw_size; +}; + + +/* --------------------------------------------------------------------------- + * Structure: struct mirror_writer_opts + * + * Container for default values and options as parsed from the command line. + * Currently rather vestigal, but may be expanded and/or moved to be set by + * Server and passed around as an argument. + * + * magic (uint32_t) + * Semi-unique number to sanity-check structure pointer and validity. + * Must equal MW_OPTS_MAGIC to be valid. + * + * logpath (char *) + * String pointer. Allocated at runtime. + * Specifies file location for logging output. + * May be NULL -- uses default output (e.g., stdout). + * + * ---------------------------------------------------------------------------- + */ +struct mirror_writer_opts { + uint32_t magic; + char *logpath; +}; + +static int do_open(struct mirror_session *session, + const H5FD_mirror_xmit_open_t *xmit_open); + + +/* --------------------------------------------------------------------------- + * Function: session_init + * + * Purpose: Populate mirror_session structure with default and + * options-drived values. + * + * Return: An allocated mirror_session structure pointer on success, + * else NULL. + * ---------------------------------------------------------------------------- + */ +static struct mirror_session * +session_init(struct mirror_writer_opts *opts) +{ + struct mirror_session *session = NULL; + + mirror_log(NULL, V_INFO, "session_init()"); + + if (NULL == opts || opts->magic != MW_OPTS_MAGIC) { + mirror_log(NULL, V_ERR, "invalid opts pointer"); + goto error; + } + + session = (struct mirror_session *)HDmalloc(sizeof(struct mirror_session)); + if (session == NULL) { + mirror_log(NULL, V_ERR, "can't allocate session structure"); + goto error; + } + + session->magic = MW_SESSION_MAGIC; + session->sockfd = -1; + session->xmit_count = 0; + session->token = 0; + session->file = NULL; + + session->reply.pub.magic = H5FD_MIRROR_XMIT_MAGIC; + session->reply.pub.version = H5FD_MIRROR_XMIT_CURR_VERSION; + session->reply.pub.op = H5FD_MIRROR_OP_REPLY; + session->reply.pub.session_token = 0; + HDbzero(session->reply.message, H5FD_MIRROR_STATUS_MESSAGE_MAX); + + /* Options-derived population + */ + + session->loginfo = mirror_log_init(opts->logpath, "W- ", + MIRROR_LOG_DEFAULT_VERBOSITY); + + return session; + +error: + if (session) { + HDfree(session); + } + return NULL; +} /* end session_init() */ + + +/* --------------------------------------------------------------------------- + * Function: session_stop + * + * Purpose: Stop and clean up a session. + * Only do this as part of program termination or aborting startup. + * + * Return: 0 on success, or negative sum of errors encountered. + * ---------------------------------------------------------------------------- + */ +static int +session_stop(struct mirror_session *session) +{ + int ret_value = 0; + + HDassert(session && (session->magic == MW_SESSION_MAGIC)); + + mirror_log(session->loginfo, V_INFO, "session_stop()"); + + /* Close HDF5 file if it is still open (probably in error) */ + if (session->file) { + mirror_log(session->loginfo, V_WARN, "HDF5 file still open at cleanup"); + if (H5FDclose(session->file) < 0) { + mirror_log(session->loginfo, V_ERR, "H5FDclose() during cleanup!"); + ret_value--; + } + } + + /* Socket will be closed by parent side of server fork after exit */ + + /* Close custom logging stream */ + if (mirror_log_term(session->loginfo) < 0) { + mirror_log(NULL, V_ERR, "Problem closing logging stream"); + ret_value--; + } + session->loginfo = NULL; + + /* Invalidate and release structure */ + session->magic++; + HDfree(session); + + return ret_value; +} /* end session_stop() */ + + +/* --------------------------------------------------------------------------- + * Function: session_start + * + * Purpose: Initiate session, open files. + * + * Return: Success: A valid mirror_session pointer which must later be + * cleaned up with session_stop(). + * Failure: NULL, after cleaning up after itself. + * --------------------------------------------------------------------------- + */ +static struct mirror_session * +session_start(int socketfd, const H5FD_mirror_xmit_open_t *xmit_open) +{ + struct mirror_session *session = NULL; + struct mirror_writer_opts opts; +#if 0 /* TODO: behaviro option */ + char logpath[H5FD_MIRROR_XMIT_FILEPATH_MAX] = ""; +#endif + + mirror_log(NULL, V_INFO, "session_start()"); + + if (FALSE == H5FD_mirror_xmit_is_open(xmit_open)) { + mirror_log(NULL, V_ERR, "invalid OPEN xmit"); + return NULL; + } + + opts.magic = MW_OPTS_MAGIC; +#if 0 /* TODO: behavior option */ + HDsnprintf(logpath, H5FD_MIRROR_XMIT_FILEPATH_MAX, "%s.log", + xmit_open->filename); + opts.logpath = logpath; +#else + opts.logpath = NULL; +#endif + + session = session_init(&opts); + if (NULL == session) { + mirror_log(NULL, V_ERR, "can't instantiate session"); + goto error; + } + + session->sockfd = socketfd; + + if (do_open(session, xmit_open) < 0) { + mirror_log(NULL, V_ERR, "unable to open file"); + goto error; + } + + return session; + +error: + if (session != NULL) { + if (session_stop(session) < 0) { + mirror_log(NULL, V_WARN, "Can't abort session init"); + } + session = NULL; + } + return NULL; +} + + +/* --------------------------------------------------------------------------- + * Function: _xmit_reply + * + * Purpose: Common operations to send a reply xmit through the session. + * + * Return: 0 on success, -1 if error. + * ---------------------------------------------------------------------------- + */ +static int +_xmit_reply(struct mirror_session *session) +{ + unsigned char xmit_buf[H5FD_MIRROR_XMIT_REPLY_SIZE]; + H5FD_mirror_xmit_reply_t *reply = &(session->reply); + + HDassert(session && (session->magic == MW_SESSION_MAGIC)); + + mirror_log(session->loginfo, V_ALL, "_xmit_reply()"); + + reply->pub.xmit_count = session->xmit_count++; + if (H5FD_mirror_xmit_encode_reply(xmit_buf, + (const H5FD_mirror_xmit_reply_t *)reply) + != H5FD_MIRROR_XMIT_REPLY_SIZE) + { + mirror_log(session->loginfo, V_ERR, "can't encode reply"); + return -1; + } + + mirror_log(session->loginfo, V_ALL, "reply xmit data\n```"); + mirror_log_bytes(session->loginfo, V_ALL, H5FD_MIRROR_XMIT_REPLY_SIZE, + (const unsigned char *)xmit_buf); + mirror_log(session->loginfo, V_ALL, "```"); + + if (HDwrite(session->sockfd, xmit_buf, H5FD_MIRROR_XMIT_REPLY_SIZE) < 0) { + mirror_log(session->loginfo, V_ERR, "can't write reply to Driver"); + return -1; + } + + return 0; +} /* end _xmit_reply() */ + + +/* --------------------------------------------------------------------------- + * Function: reply_ok + * + * Purpose: Send an OK reply through the session. + * + * Return: 0 on success, -1 if error. + * --------------------------------------------------------------------------- + */ +static int +reply_ok(struct mirror_session *session) +{ + H5FD_mirror_xmit_reply_t *reply = &(session->reply); + + HDassert(session && (session->magic == MW_SESSION_MAGIC)); + + mirror_log(session->loginfo, V_ALL, "reply_ok()"); + + reply->status = H5FD_MIRROR_STATUS_OK; + HDbzero(reply->message, H5FD_MIRROR_STATUS_MESSAGE_MAX); + return _xmit_reply(session); +} /* end reply_ok() */ + + +/* --------------------------------------------------------------------------- + * Function: reply_error + * + * Purpose: Send an ERROR reply with message through the session. + * Message may be cut short if it would overflow the available + * buffer in the xmit. + * + * Return: 0 on success, -1 if error. + * --------------------------------------------------------------------------- + */ +static int +reply_error(struct mirror_session *session, + const char *msg) +{ + H5FD_mirror_xmit_reply_t *reply = &(session->reply); + + HDassert(session && (session->magic == MW_SESSION_MAGIC)); + + mirror_log(session->loginfo, V_ALL, "reply_error(%s)", msg); + + reply->status = H5FD_MIRROR_STATUS_ERROR; + HDsnprintf(reply->message, H5FD_MIRROR_STATUS_MESSAGE_MAX-1, "%s", msg); + return _xmit_reply(session); +} /* end reply_error() */ + + +/* --------------------------------------------------------------------------- + * Function: do_close + * + * Purpose: Handle an CLOSE operation. + * + * Return: 0 on success, -1 if error. + * --------------------------------------------------------------------------- + */ +static int +do_close(struct mirror_session *session) +{ + + HDassert(session && (session->magic == MW_SESSION_MAGIC)); + + mirror_log(session->loginfo, V_INFO, "do_close()"); + + if (NULL == session->file) { + mirror_log(session->loginfo, V_ERR, "no file to close!"); + reply_error(session, "no file to close"); + return -1; + } + + if (H5FDclose(session->file) < 0) { + mirror_log(session->loginfo, V_ERR, "H5FDclose()"); + reply_error(session, "H5FDclose()"); + return -1; + } + session->file = NULL; + + if (reply_ok(session) < 0) { + mirror_log(session->loginfo, V_ERR, "can't reply"); + reply_error(session, "ok reply failed; session contaminated"); + return -1; + } + + return 0; +} /* end do_close() */ + + +/* --------------------------------------------------------------------------- + * Function: do_lock + * + * Purpose: Handle a LOCK operation. + * + * Return: 0 on success, -1 if error. + * --------------------------------------------------------------------------- + */ +static int +do_lock(struct mirror_session *session, + const unsigned char *xmit_buf) +{ + size_t decode_ret = 0; + H5FD_mirror_xmit_lock_t xmit_lock; + + HDassert(session && (session->magic == MW_SESSION_MAGIC) && xmit_buf); + + mirror_log(session->loginfo, V_INFO, "do_lock()"); + + decode_ret = H5FD_mirror_xmit_decode_lock(&xmit_lock, xmit_buf); + if (H5FD_MIRROR_XMIT_LOCK_SIZE != decode_ret) { + mirror_log(session->loginfo, V_ERR, "can't decode set-eoa xmit"); + reply_error(session, "remote xmit_eoa_t decoding size failure"); + return -1; + } + + if (!H5FD_mirror_xmit_is_lock(&xmit_lock)) { + mirror_log(session->loginfo, V_ERR, "not a set-eoa xmit"); + reply_error(session, "remote xmit_eoa_t decode failure"); + return -1; + } + mirror_log(session->loginfo, V_INFO, "lock rw: (%d)", xmit_lock.rw); + + if (H5FDlock(session->file, (hbool_t)xmit_lock.rw) < 0) { + mirror_log(session->loginfo, V_ERR, "H5FDlock()"); + reply_error(session, "remote H5FDlock() failure"); + return -1; + } + + if (reply_ok(session) < 0) { + mirror_log(session->loginfo, V_ERR, "can't reply"); + reply_error(session, "ok reply failed; session contaminated"); + return -1; + } + + return 0; +} /* end do_lock() */ + + +/* --------------------------------------------------------------------------- + * Function: do_open + * + * Purpose: Handle an OPEN operation. + * + * Return: 0 on success, -1 if error. + * --------------------------------------------------------------------------- + */ +static int +do_open(struct mirror_session *session, + const H5FD_mirror_xmit_open_t *xmit_open) +{ + hid_t fapl_id = H5I_INVALID_HID; + unsigned _flags = 0; + haddr_t _maxaddr = HADDR_UNDEF; + + HDassert(session && (session->magic == MW_SESSION_MAGIC) && xmit_open && + TRUE == H5FD_mirror_xmit_is_open(xmit_open)); + + mirror_log(session->loginfo, V_INFO, "do_open()"); + + if (0 != xmit_open->pub.xmit_count) { + mirror_log(session->loginfo, V_ERR, "open with xmit count not zero!"); + reply_error(session, "initial transmission count not zero"); + goto error; + } + if (0 != session->token) { + mirror_log(session->loginfo, V_ERR, "open with token already set!"); + reply_error(session, "initial session token not zero"); + goto error; + } + + session->xmit_count = 1; + session->token = xmit_open->pub.session_token; + session->reply.pub.session_token = session->token; + + _flags = (unsigned)xmit_open->flags; + _maxaddr = (haddr_t)xmit_open->maxaddr; + + /* Check whether the native size_t on the remote machine (Driver) is larger + * than that on the local machine; if so, issue a warning. + * The blob is always an 8-byte bitfield -- check its contents. + */ + if (xmit_open->size_t_blob > (uint64_t)((size_t)(-1))) { + mirror_log(session->loginfo, V_WARN, + "Driver size_t is larger than our own"); + } + + mirror_log(session->loginfo, V_INFO, + "to open file %s (flags %d) (maxaddr %d)", + xmit_open->filename, _flags, _maxaddr); + + /* Explicitly use Sec2 as the underlying driver for now. + */ + fapl_id = H5Pcreate(H5P_FILE_ACCESS); + if (fapl_id < 0) { + mirror_log(session->loginfo, V_ERR, "can't create FAPL"); + reply_error(session, "H5Pcreate() failure"); + goto error; + } + if (H5Pset_fapl_sec2(fapl_id) < 0) { + mirror_log(session->loginfo, V_ERR, "can't set FAPL as Sec2"); + reply_error(session, "H5Pset_fapl_sec2() failure"); + goto error; + } + + session->file = H5FDopen(xmit_open->filename, _flags, fapl_id, _maxaddr); + if (NULL == session->file) { + mirror_log(session->loginfo, V_ERR, "H5FDopen()"); + reply_error(session, "remote H5FDopen() failure"); + goto error; + } + + /* FAPL is set and in use; clean up */ + if (H5Pclose(fapl_id) < 0) { + mirror_log(session->loginfo, V_ERR, "can't set FAPL as Sec2"); + reply_error(session, "H5Pset_fapl_sec2() failure"); + goto error; + } + + if (reply_ok(session) < 0) { + mirror_log(session->loginfo, V_ERR, "can't reply"); + reply_error(session, "ok reply failed; session contaminated"); + return -1; + } + + return 0; + +error: + if (fapl_id > 0) { + H5E_BEGIN_TRY { + (void)H5Pclose(fapl_id); + } H5E_END_TRY; + } + return -1; +} /* end do_open() */ + + +/* --------------------------------------------------------------------------- + * Function: do_set_eoa + * + * Purpose: Handle a SET_EOA operation. + * + * Return: 0 on success, -1 if error. + * --------------------------------------------------------------------------- + */ +static int +do_set_eoa(struct mirror_session *session, + const unsigned char *xmit_buf) +{ + size_t decode_ret = 0; + H5FD_mirror_xmit_eoa_t xmit_seoa; + + HDassert(session && (session->magic == MW_SESSION_MAGIC) && xmit_buf); + + mirror_log(session->loginfo, V_INFO, "do_set_eoa()"); + + decode_ret = H5FD_mirror_xmit_decode_set_eoa(&xmit_seoa, xmit_buf); + if (H5FD_MIRROR_XMIT_EOA_SIZE != decode_ret) { + mirror_log(session->loginfo, V_ERR, "can't decode set-eoa xmit"); + reply_error(session, "remote xmit_eoa_t decoding size failure"); + return -1; + } + + if (!H5FD_mirror_xmit_is_set_eoa(&xmit_seoa)) { + mirror_log(session->loginfo, V_ERR, "not a set-eoa xmit"); + reply_error(session, "remote xmit_eoa_t decode failure"); + return -1; + } + + mirror_log(session->loginfo, V_INFO, "set EOA addr %d", + xmit_seoa.eoa_addr); + + if (H5FDset_eoa(session->file, (H5FD_mem_t)xmit_seoa.type, + (haddr_t)xmit_seoa.eoa_addr) + < 0) + { + mirror_log(session->loginfo, V_ERR, "H5FDset_eoa()"); + reply_error(session, "remote H5FDset_eoa() failure"); + return -1; + } + + if (reply_ok(session) < 0) { + mirror_log(session->loginfo, V_ERR, "can't reply"); + reply_error(session, "ok reply failed; session contaminated"); + return -1; + } + + return 0; +} /* end do_set_eoa() */ + + +/* --------------------------------------------------------------------------- + * Function: do_truncate + * + * Purpose: Handle a TRUNCATE operation. + * + * Return: 0 on success, -1 if error. + * --------------------------------------------------------------------------- + */ +static int +do_truncate(struct mirror_session *session) +{ + + HDassert(session && (session->magic == MW_SESSION_MAGIC)); + + mirror_log(session->loginfo, V_INFO, "do_truncate()"); + + /* default DXPL ID (0), 0 for "FALSE" closing -- both probably unused */ + if (H5FDtruncate(session->file, 0, 0) < 0) { + mirror_log(session->loginfo, V_ERR, "H5FDtruncate()"); + reply_error(session, "remote H5FDtruncate() failure"); + return -1; + } + + if (reply_ok(session) < 0) { + mirror_log(session->loginfo, V_ERR, "can't reply"); + reply_error(session, "ok reply failed; session contaminated"); + return -1; + } + + return 0; +} /* end do_truncate() */ + + +/* --------------------------------------------------------------------------- + * Function: do_unlock + * + * Purpose: Handle an UNLOCK operation. + * + * Return: 0 on success, -1 if error. + * --------------------------------------------------------------------------- + */ +static int +do_unlock(struct mirror_session *session) +{ + HDassert(session && (session->magic == MW_SESSION_MAGIC)); + + mirror_log(session->loginfo, V_INFO, "do_unlock()"); + + if (H5FDunlock(session->file) < 0) { + mirror_log(session->loginfo, V_ERR, "H5FDunlock()"); + reply_error(session, "remote H5FDunlock() failure"); + return -1; + } + + if (reply_ok(session) < 0) { + mirror_log(session->loginfo, V_ERR, "can't reply"); + reply_error(session, "ok reply failed; session contaminated"); + return -1; + } + + return 0; +} /* end do_unlock() */ + + +/* --------------------------------------------------------------------------- + * Function: do_write + * + * Purpose: Handle a WRITE operation. + * Receives command, replies; receives & writes data, replies. + * + * It is known that this results in suboptimal performance, + * but handling both small and very, very large write buffers + * with a single "over the wire" exchange + * poses design challenges not worth tackling as of March 2020. + * + * Return: 0 on success, -1 if error. + * --------------------------------------------------------------------------- + */ +static int +do_write(struct mirror_session *session, + const unsigned char *xmit_buf) +{ + size_t decode_ret = 0; + haddr_t addr = 0; + haddr_t sum_bytes_written = 0; + H5FD_mem_t type = 0; + char *buf = NULL; + ssize_t nbytes_in_packet = 0; + H5FD_mirror_xmit_write_t xmit_write; + + HDassert(session && (session->magic == MW_SESSION_MAGIC) && xmit_buf); + + mirror_log(session->loginfo, V_INFO, "do_write()"); + + if (NULL == session->file) { + mirror_log(session->loginfo, V_ERR, "no open file!"); + reply_error(session, "no file open on remote"); + return -1; + } + + decode_ret = H5FD_mirror_xmit_decode_write(&xmit_write, xmit_buf); + if (H5FD_MIRROR_XMIT_WRITE_SIZE != decode_ret) { + mirror_log(session->loginfo, V_ERR, "can't decode write xmit"); + reply_error(session, "remote xmit_write_t decoding size failure"); + return -1; + } + + if (!H5FD_mirror_xmit_is_write(&xmit_write)) { + mirror_log(session->loginfo, V_ERR, "not a write xmit"); + reply_error(session, "remote xmit_write_t decode failure"); + return -1; + } + + addr = (haddr_t)xmit_write.offset; + type = (H5FD_mem_t)xmit_write.type; + + /* Allocate the buffer once -- re-use between loops. + */ + buf = (char *)HDmalloc(sizeof(char) * H5FD_MIRROR_DATA_BUFFER_MAX); + if (NULL == buf) { + mirror_log(session->loginfo, V_ERR, "can't allocate databuffer"); + reply_error(session, "can't allocate buffer for receiving data"); + return -1; + } + + /* got write signal; ready for data */ + if (reply_ok(session) < 0) { + mirror_log(session->loginfo, V_ERR, "can't reply"); + reply_error(session, "ok reply failed; session contaminated"); + return -1; + } + + mirror_log(session->loginfo, V_INFO, "to write %zu bytes at %zu", + xmit_write.size, + addr); + + /* The given write may be: + * 1. larger than the allowed single buffer size + * 2. larger than the native size_t of this system + * + * Handle all cases by looping, ingesting as much of the stream as possible + * and writing that part to the file. + */ + sum_bytes_written = 0; + do { + nbytes_in_packet = HDread(session->sockfd, buf, + H5FD_MIRROR_DATA_BUFFER_MAX); + if (-1 == nbytes_in_packet) { + mirror_log(session->loginfo, V_ERR, "can't read into databuffer"); + reply_error(session, "can't read data buffer"); + return -1; + } + + mirror_log(session->loginfo, V_INFO, "received %zd bytes", + nbytes_in_packet); + if (HEXDUMP_WRITEDATA) { + mirror_log(session->loginfo, V_ALL, "DATA:\n```"); + mirror_log_bytes(session->loginfo, V_ALL, nbytes_in_packet, + (const unsigned char *)buf); + mirror_log(session->loginfo, V_ALL, "```"); + } + + mirror_log(session->loginfo, V_INFO, "writing %zd bytes at %zu", + nbytes_in_packet, + (addr + sum_bytes_written)); + + if (H5FDwrite(session->file, type, H5P_DEFAULT, + (addr + sum_bytes_written), (size_t)nbytes_in_packet, buf) + < 0) + { + mirror_log(session->loginfo, V_ERR, "H5FDwrite()"); + reply_error(session, "remote H5FDwrite() failure"); + return -1; + } + + sum_bytes_written += (haddr_t)nbytes_in_packet; + + } while (sum_bytes_written < xmit_write.size); /* end while ingesting */ + + HDfree(buf); + + /* signal that we're done here and a-ok */ + if (reply_ok(session) < 0) { + mirror_log(session->loginfo, V_ERR, "can't reply"); + reply_error(session, "ok reply failed; session contaminated"); + return -1; + } + + return 0; +} /* end do_write() */ + + +/* --------------------------------------------------------------------------- + * Function: receive_communique + * + * Purpose: Accept bytes from the socket, check for emergency shutdown, and + * sanity-check received bytes. + * The raw bytes read are stored in the sock_comm structure at + * comm->raw. + * The raw bytes are decoded and a xmit_t (header) struct pointer + * in comm is populated at comm->xmit_recd. + * + * Return: 0 on success, -1 if error. + * --------------------------------------------------------------------------- + */ +static int +receive_communique( + struct mirror_session *session, + struct sock_comm *comm) +{ + ssize_t read_ret = 0; + size_t decode_ret; + H5FD_mirror_xmit_t *X = comm->xmit_recd; + + HDassert((session != NULL) && \ + (session->magic == MW_SESSION_MAGIC) && \ + (comm != NULL) && \ + (comm->magic == MW_SOCK_COMM_MAGIC) && \ + (comm->xmit_recd != NULL) && \ + (comm->raw != NULL) && \ + (comm->raw_size >= H5FD_MIRROR_XMIT_BUFFER_MAX) ); + + mirror_log(session->loginfo, V_INFO, "receive_communique()"); + + HDbzero(comm->raw, comm->raw_size); + comm->recd_die = 0; + + mirror_log(session->loginfo, V_INFO, "ready to receive"); /* TODO */ + + read_ret = HDread(session->sockfd, comm->raw, H5FD_MIRROR_XMIT_BUFFER_MAX); + if (-1 == read_ret) { + mirror_log(session->loginfo, V_ERR, "read:%zd", read_ret); + goto error; + } + + mirror_log(session->loginfo, V_INFO, "received %zd bytes", read_ret); + if (HEXDUMP_XMITS) { + mirror_log(session->loginfo, V_ALL, "```", read_ret); + mirror_log_bytes(session->loginfo, V_ALL, (size_t)read_ret, + (const unsigned char *)comm->raw); + mirror_log(session->loginfo, V_ALL, "```"); + } /* end if hexdump transmissions received */ + + /* old-fashioned manual kill (for debugging) */ + if (!HDstrncmp("GOODBYE", comm->raw, 7)) { + mirror_log(session->loginfo, V_INFO, "received GOODBYE"); + comm->recd_die = 1; + goto done; + } + + decode_ret = H5FD_mirror_xmit_decode_header(X, + (const unsigned char *)comm->raw); + if (H5FD_MIRROR_XMIT_HEADER_SIZE != decode_ret) { + mirror_log(session->loginfo, V_ERR, + "header decode size mismatch: expected (%z), got (%z)", + H5FD_MIRROR_XMIT_HEADER_SIZE, decode_ret); + /* Try to tell Driver that it should stop */ + reply_error(session, "xmit size mismatch"); + goto error; + } + + if (!H5FD_mirror_xmit_is_xmit(X)) { + mirror_log(session->loginfo, V_ERR, "bad magic: 0x%X", X->magic); + /* Try to tell Driver that it should stop */ + reply_error(session, "bad magic"); + goto error; + } + + if (session->xmit_count != X->xmit_count) { + mirror_log(session->loginfo, V_ERR, + "xmit_count mismatch exp:%d recd:%d", + session->xmit_count, X->xmit_count); + /* Try to tell Driver that it should stop */ + reply_error(session, "xmit_count mismatch"); + goto error; + } + + if ( (session->token > 0) && (session->token != X->session_token) ) { + mirror_log(session->loginfo, V_ERR, "wrong session"); + /* Try to tell Driver that it should stop */ + reply_error(session, "wrong session"); + goto error; + } + + session->xmit_count++; + +done: + return 0; + +error: + return -1; +} /* end receive_communique() */ + + +/* --------------------------------------------------------------------------- + * Function: process_instructions + * + * Purpose: Receive and handle all instructions from Driver. + * + * Return: 0 on success, -1 if error. + * --------------------------------------------------------------------------- + */ +static int +process_instructions(struct mirror_session *session) +{ + struct sock_comm comm; + char xmit_buf[H5FD_MIRROR_XMIT_BUFFER_MAX]; /* raw bytes */ + H5FD_mirror_xmit_t xmit_recd; /* for decoded xmit header */ + + HDassert(session && (session->magic == MW_SESSION_MAGIC)); + + mirror_log(session->loginfo, V_INFO, "process_instructions()"); + + comm.magic = MW_SOCK_COMM_MAGIC; + comm.recd_die = 0; /* Flag for program to terminate */ + comm.xmit_recd = &xmit_recd; + comm.raw = xmit_buf; + comm.raw_size = sizeof(xmit_buf); + + while (1) { /* sill-listening infinite loop */ + + /* Use convenience structure for raw/decoded info in/out */ + if (receive_communique(session, &comm) < 0) { + mirror_log(session->loginfo, V_ERR, "problem reading socket"); + return -1; + } + + if (comm.recd_die) { + goto done; + } + + switch(xmit_recd.op) { + case H5FD_MIRROR_OP_CLOSE: + if (do_close(session) < 0) { + return -1; + } + goto done; + case H5FD_MIRROR_OP_LOCK: + if (do_lock(session, (const unsigned char *)xmit_buf) < 0) { + return -1; + } + break; + case H5FD_MIRROR_OP_OPEN: + mirror_log(session->loginfo, V_ERR, "OPEN xmit during session"); + reply_error(session, "illegal OPEN xmit during session"); + return -1; + case H5FD_MIRROR_OP_SET_EOA: + if (do_set_eoa(session, (const unsigned char *)xmit_buf) < 0) { + return -1; + } + break; + case H5FD_MIRROR_OP_TRUNCATE: + if (do_truncate(session) < 0) { + return -1; + } + break; + case H5FD_MIRROR_OP_UNLOCK: + if (do_unlock(session) < 0) { + return -1; + } + break; + case H5FD_MIRROR_OP_WRITE: + if (do_write(session, (const unsigned char *)xmit_buf) < 0) { + return -1; + } + break; + default: + mirror_log(session->loginfo, V_ERR, "unrecognized transmission"); + reply_error(session, "unrecognized transmission"); + return -1; + } /* end switch (xmit_recd.op) */ + + } /* end while still listening */ + +done: + comm.magic = 0; /* invalidate structure, on principle */ + xmit_recd.magic = 0; /* invalidate structure, on principle */ + return 0; +} /* end process_instructions() */ + + +/* ------------------------------------------------------------------------- */ +herr_t +run_writer(int socketfd, H5FD_mirror_xmit_open_t *xmit_open) +{ + struct mirror_session *session = NULL; + int ret_value = SUCCEED; + + session = session_start(socketfd, xmit_open); + if (NULL == session) { + mirror_log(NULL, V_ERR, "Can't start session -- aborting"); + ret_value = FAIL; + } + else { + if (process_instructions(session) < 0) { + mirror_log(session->loginfo, V_ERR, + "problem processing instructions"); + ret_value = FAIL; + } + if (session_stop(session) < 0) { + mirror_log(NULL, V_ERR, "Can't stop session -- going down hard"); + ret_value = FAIL; + } + } + + return ret_value; +} /* end run_writer */ + +#endif /* H5_HAVE_MIRROR_VFD */ + -- cgit v0.12