summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDana Robinson <derobins@hdfgroup.org>2020-08-14 23:15:44 (GMT)
committerDana Robinson <derobins@hdfgroup.org>2020-08-14 23:15:44 (GMT)
commit5a61eb24af840b45105f71da4ea4edc7062e24d9 (patch)
treec58b9b93e840613e3939fd9fdafc642e99c6fc1f
parent7bca2f04a45de38dde4981c5359ca007c36ceba8 (diff)
downloadhdf5-5a61eb24af840b45105f71da4ea4edc7062e24d9.zip
hdf5-5a61eb24af840b45105f71da4ea4edc7062e24d9.tar.gz
hdf5-5a61eb24af840b45105f71da4ea4edc7062e24d9.tar.bz2
Brings Mirror VFD to 1.10 from develop
-rw-r--r--CMakeLists.txt15
-rw-r--r--MANIFEST26
-rw-r--r--Makefile.am12
-rwxr-xr-xbin/trace1
-rw-r--r--config/cmake/ConfigureChecks.cmake17
-rw-r--r--config/cmake/H5pubconf.h.in12
-rw-r--r--config/cmake/libhdf5.settings.cmake.in1
-rw-r--r--configure.ac48
-rw-r--r--release_docs/RELEASE.txt6
-rw-r--r--src/H5FDmirror.c2017
-rw-r--r--src/H5FDmirror.h79
-rw-r--r--src/H5FDmirror_priv.h323
-rw-r--r--src/H5FDsec2.c6
-rw-r--r--src/H5private.h61
-rw-r--r--src/Makefile.am7
-rw-r--r--src/hdf5.h1
-rw-r--r--src/libhdf5.settings.in1
-rw-r--r--test/CMakeLists.txt34
-rw-r--r--test/CMakeTests.cmake2
-rw-r--r--test/Makefile.am17
-rw-r--r--test/mirror_vfd.c2763
-rw-r--r--test/test_mirror.sh.in100
-rw-r--r--test/use_append_chunk_mirror.c403
-rw-r--r--utils/CMakeLists.txt4
-rw-r--r--utils/COPYING12
-rw-r--r--utils/Makefile.am26
-rw-r--r--utils/mirror_vfd/CMakeLists.txt64
-rw-r--r--utils/mirror_vfd/Makefile.am30
-rw-r--r--utils/mirror_vfd/mirror_remote.c225
-rw-r--r--utils/mirror_vfd/mirror_remote.h53
-rw-r--r--utils/mirror_vfd/mirror_server.c666
-rw-r--r--utils/mirror_vfd/mirror_server_stop.c214
-rw-r--r--utils/mirror_vfd/mirror_writer.c1103
33 files changed, 8333 insertions, 16 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ef4caca..5d1295f 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)
@@ -886,6 +891,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 9e89e57..60bb838 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -682,6 +682,9 @@
./src/H5FDint.c
./src/H5FDlog.c
./src/H5FDlog.h
+./src/H5FDmirror.c
+./src/H5FDmirror.h
+./src/H5FDmirror_priv.h
./src/H5FDmodule.h
./src/H5FDmpi.c
./src/H5FDmpi.h
@@ -1003,6 +1006,7 @@
./test/cache_image.c
./test/cache_logging.c
./test/cache_tagging.c
+./test/chunk_info.c
./test/cmpd_dset.c
./test/cork.c
./test/corrupt_stab_msg.h5
@@ -1068,6 +1072,7 @@
./test/gen_deflate.c
./test/gen_file_image.c
./test/gen_filespace.c
+./test/gen_filters.c
./test/gen_mergemsg.c
./test/gen_new_array.c
./test/gen_new_fill.c
@@ -1106,6 +1111,7 @@
./test/corrupted_name_len.h5
./test/mergemsg.h5
./test/mf.c
+./test/mirror_vfd.c
./test/mount.c
./test/mtime.c
./test/multi_file_v16-r.h5
@@ -1163,6 +1169,7 @@
./test/testhdf5.c
./test/testhdf5.h
./test/testlibinfo.sh.in
+./test/test_mirror.sh.in
./test/test_usecases.sh.in
./test/testmeta.c
./test/testswmr.sh.in
@@ -1204,19 +1211,18 @@
./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
./test/use.h
-./test/vfd.c
-./test/gen_filters.c
-./test/chunk_info.c
./test/vds.c
./test/vds_env.c
./test/vds_swmr.h
./test/vds_swmr_gen.c
./test/vds_swmr_reader.c
./test/vds_swmr_writer.c
+./test/vfd.c
./test/testfiles/err_compat_1
./test/testfiles/err_compat_2
@@ -2730,6 +2736,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_stop.c
+./utils/mirror_vfd/mirror_writer.c
+
# high level libraries
./hl/COPYING
./hl/Makefile.am
@@ -3458,6 +3476,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 19522f7..d31f7c7 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,10 +190,10 @@ 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 \
- if test $$d != .; then \
- (cd $$d && $(MAKE) $(AM_MAKEFLAGS) $@) || exit 1; \
- fi; \
+ for d in src utils test; do \
+ if test $$d != .; then \
+ (cd $$d && $(MAKE) $(AM_MAKEFLAGS) $@) || exit 1; \
+ fi; \
done
# Automake wants to make config.status depend on configure. This
diff --git a/bin/trace b/bin/trace
index 6b5dd17..42b89b5 100755
--- a/bin/trace
+++ b/bin/trace
@@ -127,6 +127,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 4188e45..ab121e9 100644
--- a/config/cmake/ConfigureChecks.cmake
+++ b/config/cmake/ConfigureChecks.cmake
@@ -196,6 +196,23 @@ 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
+# ----------------------------------------------------------------------
+option (HDF5_ENABLE_MIRROR_VFD "Build the Mirror Virtual File Driver" OFF)
+if (H5FD_ENABLE_MIRROR_VFD)
+ 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 AND
+ ${HDF_PREFIX}_HAVE_FORK)
+ set (${HDF_PREFIX}_HAVE_MIRROR_VFD 1)
+ else()
+ message(STATUS "The socket-based Mirror VFD was requested but cannot be built. System prerequisites are not met.")
+ endif()
+endif()
+
#-----------------------------------------------------------------------------
# Check if C has __float128 extension
#-----------------------------------------------------------------------------
diff --git a/config/cmake/H5pubconf.h.in b/config/cmake/H5pubconf.h.in
index 05903d4..7d4b28e 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 <arpa/inet.h> 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@
@@ -270,6 +273,9 @@
/* Define to 1 if you have the <memory.h> 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@
@@ -282,6 +288,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 <netdb.h> header file. */
+#cmakedefine H5_HAVE_NETDB_H @H5_HAVE_NETDB_H@
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#cmakedefine H5_HAVE_NETINET_IN_H @H5_HAVE_NETINET_IN_H@
+
/* Define to 1 if you have the <openssl/evp.h> 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 7dd72b4..ebcbd61 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/configure.ac b/configure.ac
index 5e5db4a..93cb2e3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1147,6 +1147,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_SUBST([H5_IS_DARWIN])
@@ -2928,6 +2929,50 @@ 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=no
+
+AC_ARG_ENABLE([mirror-vfd],
+ [AS_HELP_STRING([--enable-mirror-vfd],
+ [Build the socket-based Mirror virtual file driver (VFD).
+ [default=no]])],
+ [MIRROR_VFD=$enableval], [MIRROR_VFD=no])
+
+if test "X$MIRROR_VFD" = "Xyes"; then
+
+ 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_CHECK_FUNC([fork], [], [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 cannot be built.
+ Missing one or more of: arpa/inet.h, netinet/in.h,
+ netdb.h, sys/socket.h, fork().])
+ fi
+else
+ AC_MSG_CHECKING([if the Mirror virtual file driver (VFD) is enabled])
+ AC_MSG_RESULT([no])
+ MIRROR_VFD=no
+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])
@@ -3796,9 +3841,12 @@ 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
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 909401e..ef85584 100644
--- a/release_docs/RELEASE.txt
+++ b/release_docs/RELEASE.txt
@@ -303,6 +303,12 @@ 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
diff --git a/src/H5FDmirror.c b/src/H5FDmirror.c
new file mode 100644
index 0000000..6bee4a7
--- /dev/null
+++ b/src/H5FDmirror.c
@@ -0,0 +1,2017 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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 "H5FDmirror_priv.h" /* Private header for the mirror VFD */
+#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))
+
+#ifndef BSWAP_64
+#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))
+#endif /* BSWAP_64 */
+
+/* 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)); \
+ HDfflush(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 transmission buffers */
+H5FL_BLK_DEFINE_STATIC(xmit);
+
+/* Declare a free list to manage the H5FD_mirror_t struct */
+H5FL_DEFINE_STATIC(H5FD_mirror_t);
+
+/* Declare a free list to manage the H5FD_mirror_xmit_open_t struct */
+H5FL_DEFINE_STATIC(H5FD_mirror_xmit_open_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(FUNC);
+
+ 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(FUNC);
+
+ 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_STATIC_NOERR
+
+ /* Reset VFL ID */
+ H5FD_MIRROR_g = 0;
+
+ LOG_OP_CALL(FUNC);
+
+ 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(__func__);
+
+ 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(__func__);
+
+ 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(__func__);
+
+ 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(__func__);
+
+ 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(__func__);
+
+ 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(__func__);
+
+ 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(__func__);
+
+ 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(__func__);
+
+ 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(__func__);
+
+ 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(__func__);
+
+ 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(__func__);
+
+ 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(__func__);
+
+ 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(__func__);
+
+ 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(__func__);
+
+ 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(__func__);
+
+ 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(__func__);
+
+ 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(__func__);
+
+ HDassert(dest && x);
+
+ /* clear entire structure, but especially its filepath string area */
+ HDmemset(dest, 0, 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(__func__);
+
+ HDassert(dest && x);
+
+ /* clear entire structure, but especially its message string area */
+ HDmemset(dest, 0, 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(__func__);
+
+ 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(__func__);
+
+ 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.
+ * ---------------------------------------------------------------------------
+ */
+H5_ATTR_PURE hbool_t
+H5FD_mirror_xmit_is_close(const H5FD_mirror_xmit_t *xmit)
+{
+ LOG_OP_CALL(__func__);
+
+ 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.
+ * ---------------------------------------------------------------------------
+ */
+H5_ATTR_PURE hbool_t
+H5FD_mirror_xmit_is_lock(const H5FD_mirror_xmit_lock_t *xmit)
+{
+ LOG_OP_CALL(__func__);
+
+ 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.
+ * ---------------------------------------------------------------------------
+ */
+H5_ATTR_PURE hbool_t
+H5FD_mirror_xmit_is_open(const H5FD_mirror_xmit_open_t *xmit)
+{
+ LOG_OP_CALL(__func__);
+
+ 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.
+ * ---------------------------------------------------------------------------
+ */
+H5_ATTR_PURE hbool_t
+H5FD_mirror_xmit_is_set_eoa(const H5FD_mirror_xmit_eoa_t *xmit)
+{
+ LOG_OP_CALL(__func__);
+
+ 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.
+ * ---------------------------------------------------------------------------
+ */
+H5_ATTR_PURE hbool_t
+H5FD_mirror_xmit_is_reply(const H5FD_mirror_xmit_reply_t *xmit)
+{
+ LOG_OP_CALL(__func__);
+
+ 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.
+ * ---------------------------------------------------------------------------
+ */
+H5_ATTR_PURE hbool_t
+H5FD_mirror_xmit_is_write(const H5FD_mirror_xmit_write_t *xmit)
+{
+ LOG_OP_CALL(__func__);
+
+ 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.
+ * ---------------------------------------------------------------------------
+ */
+H5_ATTR_PURE hbool_t
+H5FD_mirror_xmit_is_xmit(const H5FD_mirror_xmit_t *xmit)
+{
+ LOG_OP_CALL(__func__);
+
+ 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.
+ * ----------------------------------------------------------------------------
+ */
+static herr_t
+H5FD__mirror_verify_reply(H5FD_mirror_t *file)
+{
+ unsigned char *xmit_buf = NULL;
+ struct H5FD_mirror_xmit_reply_t reply;
+ ssize_t read_ret = 0;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC
+
+ LOG_OP_CALL(FUNC);
+
+ HDassert(file && file->sock_fd);
+
+ xmit_buf = H5FL_BLK_MALLOC(xmit, H5FD_MIRROR_XMIT_BUFFER_MAX);
+ if(NULL == xmit_buf)
+ HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "unable to allocate xmit buffer");
+
+ 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, 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:
+ if(xmit_buf)
+ xmit_buf = H5FL_BLK_FREE(xmit, xmit_buf);
+
+ 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_STATIC
+
+ LOG_OP_CALL(FUNC);
+
+ fa = (H5FD_mirror_fapl_t *)H5MM_calloc(sizeof(H5FD_mirror_fapl_t));
+ if(NULL == fa)
+ HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, 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_STATIC
+
+ LOG_OP_CALL(FUNC);
+
+ new_fa = (H5FD_mirror_fapl_t *)H5MM_malloc(sizeof(H5FD_mirror_fapl_t));
+ if(new_fa == NULL)
+ HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, 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_STATIC_NOERR
+
+ LOG_OP_CALL(FUNC);
+
+ /* 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(FUNC);
+
+ 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(FUNC);
+
+ 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)
+{
+ int live_socket = -1;
+ struct sockaddr_in target_addr;
+ socklen_t addr_size;
+ unsigned char *xmit_buf = NULL;
+ H5FD_mirror_fapl_t fa;
+ H5FD_mirror_t *file = NULL;
+ H5FD_mirror_xmit_open_t *open_xmit = NULL;
+ H5FD_t *ret_value = NULL;
+
+ FUNC_ENTER_STATIC
+
+ LOG_OP_CALL(FUNC);
+
+ /* --------------- */
+ /* 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_VFL, H5E_CANTALLOC, 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? */
+
+ open_xmit = (H5FD_mirror_xmit_open_t *)H5FL_CALLOC(H5FD_mirror_xmit_open_t);
+ if(NULL == open_xmit)
+ HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate open_xmit struct");
+
+ 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);
+
+ xmit_buf = H5FL_BLK_MALLOC(xmit, H5FD_MIRROR_XMIT_BUFFER_MAX);
+ if(NULL == xmit_buf)
+ HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate xmit buffer");
+
+ if(H5FD_mirror_xmit_encode_open(xmit_buf, 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");
+ }
+
+ if(open_xmit)
+ open_xmit = H5FL_FREE(H5FD_mirror_xmit_open_t, open_xmit);
+ if(xmit_buf)
+ xmit_buf = H5FL_BLK_FREE(xmit, xmit_buf);
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* 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 = NULL;
+ int xmit_encoded = 0; /* monitor point of failure */
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC
+
+ LOG_OP_CALL(FUNC);
+
+ /* Sanity check */
+ HDassert(file);
+ HDassert(file->sock_fd >= 0);
+
+ file->xmit.xmit_count = (file->xmit_i)++;
+ file->xmit.op = H5FD_MIRROR_OP_CLOSE;
+
+ xmit_buf = H5FL_BLK_MALLOC(xmit, H5FD_MIRROR_XMIT_BUFFER_MAX);
+ if(NULL == xmit_buf)
+ HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "unable to allocate xmit buffer");
+
+ if(H5FD_mirror_xmit_encode_header(xmit_buf, &(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 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 error */
+
+ file = H5FL_FREE(H5FD_mirror_t, file); /* always release resources */
+
+ if(xmit_buf)
+ xmit_buf = H5FL_BLK_FREE(xmit, xmit_buf);
+
+ 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_STATIC_NOERR;
+
+ LOG_OP_CALL(FUNC);
+
+ /* 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 = 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_STATIC_NOERR
+
+ LOG_OP_CALL(FUNC);
+
+ 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 = NULL;
+ H5FD_mirror_t *file = (H5FD_mirror_t *)_file;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC
+
+ LOG_OP_CALL(FUNC);
+
+ 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;
+
+ xmit_buf = H5FL_BLK_MALLOC(xmit, H5FD_MIRROR_XMIT_BUFFER_MAX);
+ if(NULL == xmit_buf)
+ HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "unable to allocate xmit buffer");
+
+ if(H5FD_mirror_xmit_encode_set_eoa(xmit_buf, &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:
+ if(xmit_buf)
+ xmit_buf = H5FL_BLK_FREE(xmit, xmit_buf);
+
+ 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_STATIC_NOERR
+
+ LOG_OP_CALL(FUNC);
+
+ HDassert(file);
+
+ FUNC_LEAVE_NOAPI(file->eof)
+} /* end H5FD__mirror_get_eof() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD__mirror_read
+ *
+ * Purpose: Required to register the driver, but 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)
+{
+ FUNC_ENTER_STATIC_NOERR
+
+ LOG_OP_CALL(FUNC);
+
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* 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 = NULL;
+ H5FD_mirror_t *file = (H5FD_mirror_t *)_file;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC
+
+ LOG_OP_CALL(FUNC);
+
+ 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;
+
+ xmit_buf = H5FL_BLK_MALLOC(xmit, H5FD_MIRROR_XMIT_BUFFER_MAX);
+ if(NULL == xmit_buf)
+ HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "unable to allocate xmit buffer");
+
+ /* Notify Writer of incoming data to write. */
+ if(H5FD_mirror_xmit_encode_write(xmit_buf, &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:
+ if(xmit_buf)
+ xmit_buf = H5FL_BLK_FREE(xmit, xmit_buf);
+
+ 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 = NULL;
+ H5FD_mirror_t *file = (H5FD_mirror_t *)_file;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC
+
+ LOG_OP_CALL(FUNC);
+
+ file->xmit.xmit_count = (file->xmit_i)++;
+ file->xmit.op = H5FD_MIRROR_OP_TRUNCATE;
+
+ xmit_buf = H5FL_BLK_MALLOC(xmit, H5FD_MIRROR_XMIT_BUFFER_MAX);
+ if(NULL == xmit_buf)
+ HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "unable to allocate xmit buffer");
+
+ 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:
+ if(xmit_buf)
+ xmit_buf = H5FL_BLK_FREE(xmit, xmit_buf);
+
+ 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 = NULL;
+ H5FD_mirror_t *file = (H5FD_mirror_t *)_file;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC
+
+ LOG_OP_CALL(FUNC);
+
+ 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;
+
+ xmit_buf = H5FL_BLK_MALLOC(xmit, H5FD_MIRROR_XMIT_BUFFER_MAX);
+ if(NULL == xmit_buf)
+ HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "unable to allocate xmit buffer");
+
+ if(H5FD_mirror_xmit_encode_lock(xmit_buf, &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:
+ if(xmit_buf)
+ xmit_buf = H5FL_BLK_FREE(xmit, xmit_buf);
+
+ 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 = NULL;
+ H5FD_mirror_t *file = (H5FD_mirror_t *)_file;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC
+
+ LOG_OP_CALL(FUNC);
+
+ file->xmit.xmit_count = (file->xmit_i)++;
+ file->xmit.op = H5FD_MIRROR_OP_UNLOCK;
+
+ xmit_buf = H5FL_BLK_MALLOC(xmit, H5FD_MIRROR_XMIT_BUFFER_MAX);
+ if(NULL == xmit_buf)
+ HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "unable to allocate xmit buffer");
+
+ 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:
+ if(xmit_buf)
+ xmit_buf = H5FL_BLK_FREE(xmit, xmit_buf);
+
+ 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..7d15c1b
--- /dev/null
+++ b/src/H5FDmirror.h
@@ -0,0 +1,79 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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);
+
+#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/H5FDmirror_priv.h b/src/H5FDmirror_priv.h
new file mode 100644
index 0000000..dc15441
--- /dev/null
+++ b/src/H5FDmirror_priv.h
@@ -0,0 +1,323 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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_priv_H
+#define H5FDmirror_priv_H
+
+#ifdef H5_HAVE_MIRROR_VFD
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+ * 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
+
+#endif /* H5_HAVE_MIRROR_VFD */
+
+#endif /* H5FDmirror_priv_H */
+
diff --git a/src/H5FDsec2.c b/src/H5FDsec2.c
index cbb707a..ce11c05 100644
--- a/src/H5FDsec2.c
+++ b/src/H5FDsec2.c
@@ -545,6 +545,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/H5private.h b/src/H5private.h
index 78746c3..6c481c9 100644
--- a/src/H5private.h
+++ b/src/H5private.h
@@ -362,6 +362,22 @@
#endif /* __cplusplus */
/*
+ * Networking headers used by the mirror VFD and related tests and utilities.
+ */
+#ifdef H5_HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+#ifdef H5_HAVE_NETDB_H
+# include <netdb.h>
+#endif
+#ifdef H5_HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#ifdef H5_HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#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
@@ -665,6 +681,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 */
@@ -711,6 +730,9 @@ 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 */
@@ -753,6 +775,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 */
@@ -997,6 +1022,9 @@ 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 /* HDgethostname */
@@ -1036,6 +1064,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 */
@@ -1090,6 +1130,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 */
@@ -1171,6 +1214,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 */
@@ -1302,12 +1351,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 */
@@ -1353,6 +1411,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 a077a19..05a5856 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -127,6 +127,11 @@ if DIRECT_VFD_CONDITIONAL
libhdf5_la_SOURCES += H5FDdirect.c
endif
+# Only compile the mirror VFD if necessary
+if MIRROR_VFD_CONDITIONAL
+ libhdf5_la_SOURCES += H5FDmirror.c
+endif
+
# Only compile the read-only S3 VFD if necessary
if ROS3_VFD_CONDITIONAL
libhdf5_la_SOURCES += H5FDros3.c H5FDs3comms.c
@@ -138,7 +143,7 @@ include_HEADERS = hdf5.h H5api_adpt.h H5overflow.h H5pubconf.h H5public.h H5vers
H5Cpublic.h H5Dpublic.h \
H5Epubgen.h H5Epublic.h H5Fpublic.h \
H5FDpublic.h H5FDcore.h H5FDdirect.h H5FDfamily.h H5FDhdfs.h \
- H5FDlog.h H5FDmpi.h H5FDmpio.h H5FDmulti.h H5FDros3.h \
+ 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 \
H5MMpublic.h H5Opublic.h H5Ppublic.h \
diff --git a/src/hdf5.h b/src/hdf5.h
index 9a0ea40..b717c6d 100644
--- a/src/hdf5.h
+++ b/src/hdf5.h
@@ -44,6 +44,7 @@
#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 */
diff --git a/src/libhdf5.settings.in b/src/libhdf5.settings.in
index b1c1b2b..128e107 100644
--- a/src/libhdf5.settings.in
+++ b/src/libhdf5.settings.in
@@ -79,6 +79,7 @@ Parallel Filtered Dataset Writes: @PARALLEL_FILTERED_WRITES@
I/O filters (external): @EXTERNAL_FILTERS@
MPE: @MPE@
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 8e553d0..d4c33b9 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -173,6 +173,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
@@ -206,7 +211,7 @@ set (H5_TESTS
page_buffer
dtypes
dsets
- chunk_info
+ chunk_info # compression lib link
cmpd_dset
filter_fail
extend
@@ -235,6 +240,7 @@ set (H5_TESTS
ros3
s3comms
hdfs
+ mirror_vfd
ntypes
dangle
dtransform
@@ -272,6 +278,7 @@ set (H5_TESTS_MULTIPLE
cache_image
ttsafe
thread_id # special link
+ mirror_vfd
)
# Only build single source tests here
foreach (h5_test ${H5_TESTS})
@@ -284,6 +291,19 @@ endforeach ()
### M U L T I P L E S O U R C E T E S T S ###
##############################################################################
######### Also special handling of link libs #############
+#-- Adding test for chunk_info
+add_executable (chunk_info ${HDF5_TEST_SOURCE_DIR}/chunk_info.c)
+target_compile_options(chunk_info PRIVATE "${HDF5_CMAKE_C_FLAGS}")
+target_include_directories (chunk_info PRIVATE "${HDF5_SRC_DIR};${HDF5_BINARY_DIR};${HDF5_TEST_BINARY_DIR};$<$<BOOL:${HDF5_ENABLE_PARALLEL}>:${MPI_C_INCLUDE_DIRS}>")
+if (NOT BUILD_SHARED_LIBS)
+ TARGET_C_PROPERTIES (chunk_info STATIC)
+ target_link_libraries (chunk_info PRIVATE ${HDF5_TEST_LIB_TARGET} ${LINK_COMP_LIBS})
+else ()
+ TARGET_C_PROPERTIES (chunk_info SHARED)
+ target_link_libraries (chunk_info PRIVATE ${HDF5_TEST_LIBSH_TARGET} ${LINK_COMP_LIBS})
+endif ()
+set_target_properties (chunk_info PROPERTIES FOLDER test)
+
#-- Adding test for direct_chunk
add_executable (direct_chunk ${HDF5_TEST_SOURCE_DIR}/direct_chunk.c)
target_compile_options(direct_chunk PRIVATE "${HDF5_CMAKE_C_FLAGS}")
@@ -362,6 +382,18 @@ else ()
endif ()
set_target_properties (thread_id 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};$<$<BOOL:${HDF5_ENABLE_PARALLEL}>:${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 949b926..4e14067 100644
--- a/test/CMakeTests.cmake
+++ b/test/CMakeTests.cmake
@@ -448,6 +448,8 @@ set (test_CLEANFILES
direct_chunk.h5
splitter*.h5
splitter.log
+ mirror_rw/*
+ mirror_wo/*
)
# Remove any output file left over from previous test run
diff --git a/test/Makefile.am b/test/Makefile.am
index 1c759dc..874e35d 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
endif
@@ -137,6 +141,7 @@ LDADD=libh5test.la $(LIBHDF5)
ttsafe_SOURCES=ttsafe.c ttsafe_dcreate.c ttsafe_error.c ttsafe_cancel.c \
ttsafe_acreate.c ttsafe_attr_vlen.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
@@ -202,7 +207,7 @@ CHECK_CLEANFILES+=accum.h5 cmpd_dset.h5 compact_dataset.h5 dataset.h5 dset_offse
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 \
- splitter*.h5 splitter.log
+ splitter*.h5 splitter.log mirror_rw mirror_ro
# Sources for testhdf5 executable
testhdf5_SOURCES=testhdf5.c tarray.c tattr.c tchecksum.c tconfig.c tfile.c \
@@ -212,11 +217,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
+ testexternal_env.sh testswmr.sh testvds_env.sh testvdsswmr.sh test_usecases.sh testflushrefresh.sh \
+ testabort_fail.sh test_mirror.sh
include $(top_srcdir)/config/conclude.am
diff --git a/test/mirror_vfd.c b/test/mirror_vfd.c
new file mode 100644
index 0000000..35c3f90
--- /dev/null
+++ b/test/mirror_vfd.c
@@ -0,0 +1,2763 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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
+
+#include "H5FDmirror_priv.h" /* Private header for the 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);
+
+static void mybzero(void *dest, size_t size);
+
+
+/* ----------------------------------------------------------------------------
+ * Function: mybzero
+ *
+ * Purpose: Have bzero simplicity and abstraction in (possible) absence of
+ * it being available.
+ *
+ * Programmer: Jacob Smith
+ * 2020-03-30
+ * ----------------------------------------------------------------------------
+ */
+static void
+mybzero(void *dest, size_t size)
+{
+ size_t i = 0;
+ char *s = NULL;
+ HDassert(dest != NULL);
+ s = (char *)dest;
+ for (i = 0; i < size; i++) {
+ *(s+i) = 0;
+ }
+} /* end mybzero() */
+
+
+/* ----------------------------------------------------------------------------
+ * 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()
+ * <h5fixname_prefix> / <dirname> / <_basename> <h5prefix?>
+ *
+ * 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
+ */
+ mybzero(buf, 8);
+ mybzero(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
+ */
+ mybzero(buf, 8);
+ mybzero(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
+ */
+ mybzero(buf, 8);
+ mybzero(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
+ */
+ mybzero(buf, 8);
+ mybzero(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
+ */
+ mybzero(buf, 8);
+ mybzero(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
+ */
+ mybzero(buf, 8);
+ mybzero(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
+ */
+ mybzero(buf, 8);
+ mybzero(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
+ */
+ mybzero(buf, 8);
+ mybzero(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
+ */
+ mybzero(buf, 8);
+ mybzero(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
+ */
+ mybzero(buf, 16);
+ mybzero(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
+ */
+ mybzero(buf, 16);
+ mybzero(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
+ */
+ mybzero(buf, 16);
+ mybzero(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..9635c38
--- /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_stop $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_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/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..1926352
--- /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};$<$<BOOL:${HDF5_ENABLE_PARALLEL}>:${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_stop executable
+#-----------------------------------------------------------------------------
+
+set (mirror_server_stop_SOURCES ${HDF5_UTILS_MIRRORVFD_SOURCE_DIR}/mirror_server_stop.c)
+add_executable (mirror_server_stop ${mirror_server_stop_SOURCES})
+target_include_directories (mirror_server_stop PRIVATE "${HDF5_UITLS_DIR};${HDF5_SRC_DIR};${HDF5_BINARY_DIR};$<$<BOOL:${HDF5_ENABLE_PARALLEL}>:${MPI_C_INCLUDE_DIRS}>")
+if (NOT BUILD_SHARED_LIBS)
+ TARGET_C_PROPERTIES (mirror_server_stop STATIC)
+ target_link_libraries (mirror_server_stop PRIVATE ${HDF5_TOOLS_LIB_TARGET} ${HDF5_LIB_TARGET})
+else ()
+ TARGET_C_PROPERTIES (mirror_server_stop SHARED)
+ target_link_libraries (mirror_server_stop PRIVATE ${HDF5_TOOLS_LIBSH_TARGET} ${HDF5_LIBSH_TARGET})
+endif ()
+set_target_properties (mirror_server_stop PROPERTIES FOLDER utils)
+set_global_variable (HDF5_UTILS_TO_EXPORT "${HDF5_UTILS_TO_EXPORT};mirror_server_stop")
+set (H5_DEP_EXECUTABLES ${H5_DEP_EXECUTABLES} mirror_server_stop)
+
+##############################################################################
+##############################################################################
+### 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..96d3104
--- /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_stop
+
+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..67e95a5
--- /dev/null
+++ b/utils/mirror_vfd/mirror_remote.h
@@ -0,0 +1,53 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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
+
+#include "H5FDmirror_priv.h" /* Private header for the 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..db7cf04
--- /dev/null
+++ b/utils/mirror_vfd/mirror_server.c
@@ -0,0 +1,666 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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: mybzero
+ *
+ * Purpose: Introduce bzero without neededing it on the system.
+ *
+ * Programmer: Jacob Smith
+ * 2020-03-30
+ * ---------------------------------------------------------------------------
+ */
+static void mybzero(void *dest, size_t size)
+{
+ size_t i = 0;
+ char *s = NULL;
+ HDassert(dest);
+ s = (char *)dest;
+ for (i = 0; i < size; i++) {
+ *(s+i) = 0;
+ }
+} /* end mybzero() */
+
+
+/* ---------------------------------------------------------------------------
+ * 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 */
+ mybzero(args_out->log_path, PATH_MAX+1);
+ mybzero(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(void)
+{
+ 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_stop.c b/utils/mirror_vfd/mirror_server_stop.c
new file mode 100644
index 0000000..f2414e0
--- /dev/null
+++ b/utils/mirror_vfd/mirror_server_stop.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");
+ HDexit(EXIT_FAILURE);
+ }
+
+ if (opts.help) {
+ usage();
+ HDexit(EXIT_FAILURE);
+ }
+
+ if (send_shutdown(&opts) < 0) {
+ HDprintf("Unable to send shutdown command\n");
+ HDexit(EXIT_FAILURE);
+ }
+
+ HDexit(EXIT_SUCCESS);
+} /* end main() */
+
+#else /* H5_HAVE_MIRROR_VFD */
+
+
+/* ------------------------------------------------------------------------- */
+int
+main(void)
+{
+ HDprintf("Mirror VFD not built -- unable to perform shutdown.\n");
+ HDexit(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..e1ab1b2
--- /dev/null
+++ b/utils/mirror_vfd/mirror_writer.c
@@ -0,0 +1,1103 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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 void mybzero(void *dest, size_t size);
+
+static int do_open(struct mirror_session *session,
+ const H5FD_mirror_xmit_open_t *xmit_open);
+
+
+/* ---------------------------------------------------------------------------
+ * Function: mybzero
+ *
+ * Purpose: Introduce bzero without neededing it on the system.
+ *
+ * Programmer: Jacob Smith
+ * 2020-03-30
+ * ---------------------------------------------------------------------------
+ */
+static void mybzero(void *dest, size_t size)
+{
+ size_t i = 0;
+ char *s = NULL;
+ HDassert(dest);
+ s = (char *)dest;
+ for (i = 0; i < size; i++) {
+ *(s+i) = 0;
+ }
+} /* end mybzero() */
+
+
+/* ---------------------------------------------------------------------------
+ * 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;
+ mybzero(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;
+ mybzero(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()");
+
+ mybzero(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() */
+
+
+/* ---------------------------------------------------------------------------
+ * Function: run_writer
+ *
+ * Purpose: Initiate Writer operations.
+ *
+ * Receives as parameters a socket which has accepted the
+ * connection to the Driver and the OPEN xmit (which must be
+ * decoded into the structure and verified prior to being passed
+ * to this function).
+ *
+ * Is not responsible for closing or cleaning up any of the
+ * received parameters.
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ * ---------------------------------------------------------------------------
+ */
+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 */
+