hdf5/test/h5test.c
jhendersonHDF cc50a78000
Fix issue with FAPL file locking setting inheriting test (#4053)
Fixes an issue where the HDF5_USE_FILE_LOCKING environment variable being
set can interfere with the file locking setting that the test expects to
be returned.
2024-03-01 07:38:17 -06:00

2350 lines
74 KiB
C

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Copyright by The HDF Group. *
* All rights reserved. *
* *
* This file is part of HDF5. The full HDF5 copyright notice, including *
* terms governing use, modification, and redistribution, is contained in *
* the COPYING file, which can be found at the root of the source code *
* distribution tree, or in https://www.hdfgroup.org/licenses. *
* If you do not have access to either file, you may request a copy from *
* help@hdfgroup.org. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* Purpose: Provides support functions for most of the hdf5 tests cases.
*
*/
#undef NDEBUG /*override -DNDEBUG */
#include "h5test.h"
#include "H5srcdir.h"
#include "H5srcdir_str.h"
/* Necessary for h5_verify_cached_stabs() */
#define H5G_FRIEND /*suppress error about including H5Gpkg */
#define H5G_TESTING
#include "H5Gpkg.h"
#ifdef H5_HAVE_WIN32_API
#include <process.h>
#endif /* H5_HAVE_WIN32_API */
/*
* Define these environment variables or constants to influence functions in
* this test support library. The environment variable is used in preference
* to the cpp constant. If neither is defined then use some default value.
*
* HDF5_DRIVER: This string describes what low level file driver to
* use for HDF5 file access.
*
* HDF5_LIBVER_BOUNDS: This string describes what library version bounds to
* use for HDF5 file access. See h5_get_libver_fapl() for details.
*
* HDF5_PREFIX: A string to add to the beginning of all serial test
* file names. This can be used to run tests in a
* different file system (e.g., "/tmp" or "/tmp/myname").
* The prefix will be separated from the base file name
* by a slash. See h5_fixname() for details.
*
* HDF5_PARAPREFIX: A string to add to the beginning of all parallel test
* file names. This can be used to tell MPIO what driver
* to use (e.g., "gfs:", "ufs:", or "nfs:") or to use a
* different file system (e.g., "/tmp" or "/tmp/myname").
* The prefix will be separated from the base file name
* by a slash. See h5_fixname() for details.
*
*/
/*
* In a parallel machine, the filesystem suitable for compiling is
* unlikely a parallel file system that is suitable for parallel I/O.
* There is no standard pathname for the parallel file system. /tmp
* is about the best guess.
*/
#ifndef HDF5_PARAPREFIX
#define HDF5_PARAPREFIX ""
#endif
char *paraprefix = NULL; /* for command line option para-prefix */
#ifdef H5_HAVE_PARALLEL
MPI_Info h5_io_info_g = MPI_INFO_NULL; /* MPI INFO object for IO */
#endif
#define READ_BUF_SIZE 65536
/*
* These are the letters that are appended to the file name when generating
* names for the split and multi drivers. They are:
*
* m: All meta data when using the split driver.
* s: The userblock, superblock, and driver info block
* b: B-tree nodes
* r: Dataset raw data
* g: Global heap
* l: local heap (object names)
* o: object headers
*/
static const char *multi_letters = "msbrglo";
/* Temporary file for sending signal messages */
#define TMP_SIGNAL_FILE "tmp_signal_file"
/* The # of seconds to wait for the message file--used by h5_wait_message() */
#define MESSAGE_TIMEOUT 300 /* Timeout in seconds */
/* Buffer to construct path in and return pointer to */
static char srcdir_path[1024];
/* Buffer to construct file in and return pointer to */
static char srcdir_testpath[1024];
/* The strings that correspond to library version bounds H5F_libver_t in H5Fpublic.h */
/* This is used by h5_get_version_string() */
const char *LIBVER_NAMES[] = {"earliest", /* H5F_LIBVER_EARLIEST = 0 */
"v18", /* H5F_LIBVER_V18 = 1 */
"v110", /* H5F_LIBVER_V110 = 2 */
"v112", /* H5F_LIBVER_V112 = 3 */
"v114", /* H5F_LIBVER_V114 = 4 */
"latest", /* H5F_LIBVER_V116 = 5 */
NULL};
/* Previous error reporting function */
static H5E_auto2_t err_func = NULL;
/* Global variables for testing */
size_t n_tests_run_g = 0;
size_t n_tests_passed_g = 0;
size_t n_tests_failed_g = 0;
size_t n_tests_skipped_g = 0;
uint64_t vol_cap_flags_g = H5VL_CAP_FLAG_NONE;
static herr_t h5_errors(hid_t estack, void *client_data);
static char *h5_fixname_real(const char *base_name, hid_t fapl, const char *_suffix, char *fullname,
size_t size, bool nest_printf, bool subst_for_superblock);
/*-------------------------------------------------------------------------
* Function: h5_errors
*
* Purpose: Displays the error stack after printing "*FAILED*".
*
* Return: Success: 0
*
* Failure: -1
*
*-------------------------------------------------------------------------
*/
static herr_t
h5_errors(hid_t estack, void H5_ATTR_UNUSED *client_data)
{
H5_FAILED();
H5Eprint2(estack, stdout);
return 0;
}
/*-------------------------------------------------------------------------
* Function: h5_clean_files
*
* Purpose: Cleanup temporary test files (always).
* base_name contains the list of test file names.
*
* Return: void
*
*-------------------------------------------------------------------------
*/
void
h5_clean_files(const char *base_name[], hid_t fapl)
{
int i;
for (i = 0; base_name[i]; i++) {
h5_delete_test_file(base_name[i], fapl);
}
/* Close the FAPL used to access the file */
H5Pclose(fapl);
} /* end h5_clean_files() */
/*-------------------------------------------------------------------------
* Function: h5_delete_test_file
*
* Purpose Clean up temporary test files.
*
* When a test calls h5_fixname() to get a VFD-dependent
* test file name, this function can be used to clean it up.
*
* Return: void
*
* Since this is a cleanup file, we don't care if it fails.
*
*-------------------------------------------------------------------------
*/
void
h5_delete_test_file(const char *base_name, hid_t fapl)
{
char filename[1024]; /* VFD-dependent filename to delete */
/* Get the VFD-dependent filename */
if (NULL == h5_fixname(base_name, fapl, filename, sizeof(filename)))
return;
H5E_BEGIN_TRY
{
H5Fdelete(filename, fapl);
}
H5E_END_TRY
} /* end h5_delete_test_file() */
/*-------------------------------------------------------------------------
* Function: h5_delete_all_test_files
*
* Purpose Clean up temporary test files.
*
* When a test calls h5_fixname() get a VFD-dependent
* test file name, this function can be used to clean it up.
*
* This function takes an array of filenames that ends with
* a NULL string and cleans them all.
*
* Return: void
*
* Since this is a cleanup file, we don't care if it fails.
*
*-------------------------------------------------------------------------
*/
void
h5_delete_all_test_files(const char *base_name[], hid_t fapl)
{
int i; /* iterator */
for (i = 0; base_name[i]; i++) {
h5_delete_test_file(base_name[i], fapl);
} /* end for */
} /* end h5_delete_all_test_files() */
/*-------------------------------------------------------------------------
* Function: h5_cleanup
*
* Purpose: Cleanup temporary test files.
* base_name contains the list of test file names.
* The file access property list is also closed.
*
* Return: Non-zero if cleanup actions were performed; zero otherwise.
*
*-------------------------------------------------------------------------
*/
int
h5_cleanup(const char *base_name[], hid_t fapl)
{
int retval = 0;
if (GetTestCleanup()) {
/* Clean up files in base_name, and the FAPL */
h5_clean_files(base_name, fapl);
retval = 1;
} /* end if */
/* Restore the original error reporting routine */
h5_restore_err();
return retval;
} /* end h5_cleanup() */
/*-------------------------------------------------------------------------
* Function: h5_test_shutdown
*
* Purpose: Performs any special test cleanup required before the test
* ends.
*
* NOTE: This function should normally only be called once
* in a given test, usually just before leaving main(). It
* is intended for use in the single-file unit tests, not
* testhdf5.
*
* Return: void
*
*-------------------------------------------------------------------------
*/
void
h5_test_shutdown(void)
{
/* Restore the original error reporting routine */
h5_restore_err();
} /* end h5_test_shutdown() */
/*-------------------------------------------------------------------------
* Function: h5_restore_err
*
* Purpose: Restore the default error handler.
*
* Return: N/A
*
*-------------------------------------------------------------------------
*/
void
h5_restore_err(void)
{
/* Restore the original error reporting routine */
assert(err_func != NULL);
H5Eset_auto2(H5E_DEFAULT, err_func, NULL);
err_func = NULL;
}
/*-------------------------------------------------------------------------
* Function: h5_reset
*
* Purpose: Reset the library by closing it
*
* Return: void
*-------------------------------------------------------------------------
*/
void
h5_reset(void)
{
fflush(stdout);
fflush(stderr);
H5close();
/* Save current error stack reporting routine and redirect to our local one */
assert(err_func == NULL);
H5Eget_auto2(H5E_DEFAULT, &err_func, NULL);
H5Eset_auto2(H5E_DEFAULT, h5_errors, NULL);
}
/*-------------------------------------------------------------------------
* Function: h5_test_init
*
* Purpose: Performs any special actions before the test begins.
*
* NOTE: This function should normally only be called once
* in a given test, usually at the beginning of main(). It
* is intended for use in the single-file unit tests, not
* testhdf5.
*
* Return: void
*
*-------------------------------------------------------------------------
*/
void
h5_test_init(void)
{
fflush(stdout);
fflush(stderr);
H5close();
/* Save current error stack reporting routine and redirect to our local one */
assert(err_func == NULL);
H5Eget_auto2(H5E_DEFAULT, &err_func, NULL);
H5Eset_auto2(H5E_DEFAULT, h5_errors, NULL);
} /* end h5_test_init() */
/*-------------------------------------------------------------------------
* Function: h5_fixname
*
* Purpose: Create a file name from a file base name like `test' and
* return it through the FULLNAME (at most SIZE characters
* counting the null terminator). The full name is created by
* prepending the contents of HDF5_PREFIX (separated from the
* base name by a slash) and appending a file extension based on
* the driver supplied, resulting in something like
* `ufs:/u/matzke/test.h5'.
*
* Return: Success: The FULLNAME pointer.
*
* Failure: NULL if BASENAME or FULLNAME is the null
* pointer or if FULLNAME isn't large enough for
* the result.
*
*-------------------------------------------------------------------------
*/
char *
h5_fixname(const char *base_name, hid_t fapl, char *fullname, size_t size)
{
return (h5_fixname_real(base_name, fapl, ".h5", fullname, size, false, false));
}
/*-------------------------------------------------------------------------
* Function: h5_fixname_superblock
*
* Purpose: Like h5_fixname() but returns the name of the file you'd
* open to find the superblock. Useful for when you have to
* open a file with open(2) but the h5_fixname() string
* contains stuff like format strings.
*
* Return: Success: The FULLNAME pointer.
*
* Failure: NULL if BASENAME or FULLNAME is the null
* pointer or if FULLNAME isn't large enough for
* the result.
*
*-------------------------------------------------------------------------
*/
char *
h5_fixname_superblock(const char *base_name, hid_t fapl_id, char *fullname, size_t size)
{
return (h5_fixname_real(base_name, fapl_id, ".h5", fullname, size, false, true));
}
/*-------------------------------------------------------------------------
* Function: h5_fixname_no_suffix
*
* Purpose: Same as h5_fixname but with no suffix appended
*
* Return: Success: The FULLNAME pointer.
*
* Failure: NULL if BASENAME or FULLNAME is the null
* pointer or if FULLNAME isn't large enough for
* the result.
*
*-------------------------------------------------------------------------
*/
char *
h5_fixname_no_suffix(const char *base_name, hid_t fapl, char *fullname, size_t size)
{
return (h5_fixname_real(base_name, fapl, NULL, fullname, size, false, false));
}
/*-------------------------------------------------------------------------
* Function: h5_fixname_printf
*
* Purpose: Same as h5_fixname but returns a filename that can be passed
* through a printf-style function once before being passed to the file
* driver. Basically, replaces all % characters used by the file
* driver with %%.
*
* Return: Success: The FULLNAME pointer.
*
* Failure: NULL if BASENAME or FULLNAME is the null
* pointer or if FULLNAME isn't large enough for
* the result.
*
*-------------------------------------------------------------------------
*/
char *
h5_fixname_printf(const char *base_name, hid_t fapl, char *fullname, size_t size)
{
return (h5_fixname_real(base_name, fapl, ".h5", fullname, size, true, false));
}
/*-------------------------------------------------------------------------
* Function: h5_fixname_real
*
* Purpose: Create a file name from a file base name like `test' and
* return it through the FULLNAME (at most SIZE characters
* counting the null terminator). The full name is created by
* prepending the contents of HDF5_PREFIX (separated from the
* base name by a slash) and appending a file extension based on
* the driver supplied, resulting in something like
* `ufs:/u/matzke/test.h5'.
*
* Return: Success: The FULLNAME pointer.
*
* Failure: NULL if BASENAME or FULLNAME is the null
* pointer or if FULLNAME isn't large enough for
* the result.
*
*-------------------------------------------------------------------------
*/
static char *
h5_fixname_real(const char *base_name, hid_t fapl, const char *_suffix, char *fullname, size_t size,
bool nest_printf, bool subst_for_superblock)
{
const char *prefix = NULL;
const char *driver_env_var = NULL; /* HDF5_DRIVER environment variable */
char *ptr, last = '\0';
const char *suffix = _suffix;
size_t i, j;
hid_t driver = H5I_INVALID_HID;
bool isppdriver = false; /* if the driver is MPI parallel */
if (!base_name || !fullname || size < 1)
return NULL;
memset(fullname, 0, size);
/* Determine if driver is set by environment variable. If it is,
* only generate a suffix if fixing the filename for the superblock
* file. */
driver_env_var = getenv(HDF5_DRIVER);
if (driver_env_var && (H5P_DEFAULT == fapl) && subst_for_superblock)
fapl = H5P_FILE_ACCESS_DEFAULT;
/* figure out the suffix */
if (H5P_DEFAULT != fapl) {
if ((driver = H5Pget_driver(fapl)) < 0)
return NULL;
if (suffix) {
if (H5FD_FAMILY == driver) {
if (subst_for_superblock)
suffix = "-000000.h5";
else {
if (nest_printf) {
suffix = "-%%06d.h5";
}
else {
suffix = "-%06d.h5";
}
}
}
else if (H5FD_MULTI == driver) {
/* Check the HDF5_DRIVER environment variable in case
* we are using the split driver since both of those
* use the multi VFD under the hood.
*/
#ifdef HDF5_DRIVER
/* Use the environment variable, then the compile-time constant */
if (!driver_env_var)
driver_env_var = HDF5_DRIVER;
#endif
if (driver_env_var && !strcmp(driver_env_var, "split")) {
/* split VFD */
if (subst_for_superblock)
suffix = ".h5.meta";
}
else {
/* multi VFD */
if (subst_for_superblock)
suffix = "-s.h5";
else
suffix = NULL;
}
}
}
}
if (h5_using_parallel_driver(fapl, &isppdriver) < 0)
return NULL;
/* Check HDF5_NOCLEANUP environment setting.
* (The #ifdef is needed to prevent compile failure in case MPI is not
* configured.)
*/
if (isppdriver) {
#ifdef H5_HAVE_PARALLEL
if (getenv_all(MPI_COMM_WORLD, 0, HDF5_NOCLEANUP))
SetTestNoCleanup();
#endif /* H5_HAVE_PARALLEL */
}
else {
if (getenv(HDF5_NOCLEANUP))
SetTestNoCleanup();
}
/* Check what prefix to use for test files. Process HDF5_PARAPREFIX and
* HDF5_PREFIX.
* Use different ones depending on parallel or serial driver used.
* (The #ifdef is needed to prevent compile failure in case MPI is not
* configured.)
*/
if (isppdriver) {
#ifdef H5_HAVE_PARALLEL
/*
* For parallel:
* First use command line option, then the environment
* variable, then try the constant
*/
static int explained = 0;
prefix = (paraprefix ? paraprefix : getenv_all(MPI_COMM_WORLD, 0, "HDF5_PARAPREFIX"));
if (!prefix && !explained) {
/* print hint by process 0 once. */
int mpi_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
if (mpi_rank == 0)
printf("*** Hint ***\n"
"You can use environment variable HDF5_PARAPREFIX to "
"run parallel test files in a\n"
"different directory or to add file type prefix. e.g.,\n"
" HDF5_PARAPREFIX=pfs:/PFS/user/me\n"
" export HDF5_PARAPREFIX\n"
"*** End of Hint ***\n");
explained = true;
#ifdef HDF5_PARAPREFIX
prefix = HDF5_PARAPREFIX;
#endif /* HDF5_PARAPREFIX */
}
#endif /* H5_HAVE_PARALLEL */
}
else {
/*
* For serial:
* First use the environment variable, then try the constant
*/
prefix = getenv("HDF5_PREFIX");
#ifdef HDF5_PREFIX
if (!prefix)
prefix = HDF5_PREFIX;
#endif /* HDF5_PREFIX */
}
/* Prepend the prefix value to the base name */
if (prefix && *prefix) {
if (isppdriver) {
/* This is a parallel system */
char *subdir;
if (!strcmp(prefix, HDF5_PARAPREFIX)) {
/*
* If the prefix specifies the HDF5_PARAPREFIX directory, then
* default to using the "/tmp/$USER" or "/tmp/$LOGIN"
* directory instead.
*/
char *user, *login;
user = getenv("USER");
login = getenv("LOGIN");
subdir = (user ? user : login);
if (subdir) {
for (i = 0; i < size && prefix[i]; i++)
fullname[i] = prefix[i];
fullname[i++] = '/';
for (j = 0; i < size && subdir[j]; ++i, ++j)
fullname[i] = subdir[j];
}
}
if (!fullname[0]) {
/* We didn't append the prefix yet */
strncpy(fullname, prefix, size);
fullname[size - 1] = '\0';
}
if (strlen(fullname) + strlen(base_name) + 1 < size) {
/*
* Append the base_name with a slash first. Multiple
* slashes are handled below.
*/
h5_stat_t buf;
if (HDstat(fullname, &buf) < 0)
/* The directory doesn't exist just yet */
if (HDmkdir(fullname, (mode_t)0755) < 0 && errno != EEXIST)
/*
* We couldn't make the "/tmp/${USER,LOGIN}"
* subdirectory. Default to PREFIX's original
* prefix value.
*/
strcpy(fullname, prefix);
strcat(fullname, "/");
strcat(fullname, base_name);
}
else {
/* Buffer is too small */
return NULL;
}
}
else {
if (snprintf(fullname, size, "%s/%s", prefix, base_name) == (int)size)
/* Buffer is too small */
return NULL;
}
}
else if (strlen(base_name) >= size) {
/* Buffer is too small */
return NULL;
}
else {
strcpy(fullname, base_name);
}
/* Append a suffix */
if (suffix) {
if (strlen(fullname) + strlen(suffix) >= size)
return NULL;
strcat(fullname, suffix);
}
/* Remove any double slashes in the filename */
for (ptr = fullname, i = j = 0; ptr && i < size; i++, ptr++) {
if (*ptr != '/' || last != '/')
fullname[j++] = *ptr;
last = *ptr;
}
return fullname;
}
/*-------------------------------------------------------------------------
* Function: h5_rmprefix
*
* Purpose: This "removes" the MPIO driver prefix part of the file name
* by returning a pointer that points at the non-prefix component
* part of the file name. E.g.,
* Input Return
* pfs:/scratch1/dataX /scratch1/dataX
* /scratch2/dataY /scratch2/dataY
* Note that there is no change to the original file name.
*
* Return: Success: a pointer at the non-prefix part.
*
*-------------------------------------------------------------------------
*/
H5_ATTR_PURE const char *
h5_rmprefix(const char *filename)
{
const char *ret_ptr;
if ((ret_ptr = strstr(filename, ":")) == NULL)
ret_ptr = filename;
else
ret_ptr++;
return (ret_ptr);
}
/*-------------------------------------------------------------------------
* Function: h5_fileaccess
*
* Purpose: Returns a file access template which is the default template
* but with a file driver, VOL connector, or libver bound set
* according to a constant or environment variable
*
* Return: Success: A file access property list
* Failure: H5I_INVALID_HID
*
*-------------------------------------------------------------------------
*/
hid_t
h5_fileaccess(void)
{
hid_t fapl_id = H5I_INVALID_HID;
if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0)
goto error;
/* Check for libver bounds */
if (h5_get_libver_fapl(fapl_id) < 0)
goto error;
return fapl_id;
error:
if (fapl_id != H5I_INVALID_HID)
H5Pclose(fapl_id);
return H5I_INVALID_HID;
} /* end h5_fileaccess() */
/*-------------------------------------------------------------------------
* Function: h5_fileaccess_flags
*
* Purpose: Returns a file access template which is the default template
* but with a file driver, VOL connector, or libver bound set
* according to a constant or environment variable
*
* Return: Success: A file access property list
* Failure: H5I_INVALID_HID
*
*-------------------------------------------------------------------------
*/
hid_t
h5_fileaccess_flags(unsigned flags)
{
hid_t fapl_id = H5I_INVALID_HID;
if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0)
goto error;
/* Check for libver bounds */
if ((flags & H5_FILEACCESS_LIBVER) && h5_get_libver_fapl(fapl_id) < 0)
goto error;
return fapl_id;
error:
if (fapl_id != H5I_INVALID_HID)
H5Pclose(fapl_id);
return H5I_INVALID_HID;
} /* end h5_fileaccess_flags() */
/*-------------------------------------------------------------------------
* Function: h5_get_libver_fapl
*
* Purpose: Sets the library version bounds for a FAPL according to the
* value in the constant or environment variable "HDF5_LIBVER_BOUNDS".
*
* Return: Success: 0
* Failure: -1
*
*-------------------------------------------------------------------------
*/
herr_t
h5_get_libver_fapl(hid_t fapl)
{
const char *env = NULL; /* HDF5_DRIVER environment variable */
const char *tok = NULL; /* strtok pointer */
char *lasts = NULL; /* Context pointer for strtok_r() call */
char buf[1024]; /* buffer for tokenizing HDF5_DRIVER */
/* Get the environment variable, if it exists */
env = getenv("HDF5_LIBVER_BOUNDS");
#ifdef HDF5_LIBVER_BOUNDS
/* Use the environment variable, then the compile-time constant */
if (!env)
env = HDF5_LIBVER_BOUNDS;
#endif
/* If the environment variable was not set, just return
* without modifying the FAPL.
*/
if (!env || !*env)
goto done;
/* Get the first 'word' of the environment variable.
* If it's nothing (environment variable was whitespace)
* just return the default fapl.
*/
strncpy(buf, env, sizeof(buf));
buf[sizeof(buf) - 1] = '\0';
if (NULL == (tok = HDstrtok_r(buf, " \t\n\r", &lasts)))
goto done;
if (!strcmp(tok, "latest")) {
/* use the latest format */
if (H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
goto error;
} /* end if */
else {
/* Unknown setting */
goto error;
} /* end else */
done:
return 0;
error:
return -1;
} /* end h5_get_libver_fapl() */
/*-------------------------------------------------------------------------
* Function: h5_no_hwconv
*
* Purpose: Turn off hardware data type conversions.
*
* Return: void
*
*-------------------------------------------------------------------------
*/
void
h5_no_hwconv(void)
{
H5Tunregister(H5T_PERS_HARD, NULL, (hid_t)-1, (hid_t)-1, NULL);
}
/*-------------------------------------------------------------------------
* Function: h5_show_hostname
*
* Purpose: Show hostname. Show process ID if in MPI environment.
*
* Return: void
*
*-------------------------------------------------------------------------
*/
void
h5_show_hostname(void)
{
char hostname[80];
#ifdef H5_HAVE_WIN32_API
WSADATA wsaData;
int err;
#endif
#ifdef H5_HAVE_PARALLEL
int mpi_rank, mpi_initialized, mpi_finalized;
#endif
/* try show the process or thread id in multiple processes cases*/
#ifdef H5_HAVE_PARALLEL
MPI_Initialized(&mpi_initialized);
MPI_Finalized(&mpi_finalized);
if (mpi_initialized && !mpi_finalized) {
/* Prevent output here from getting mixed with later output */
MPI_Barrier(MPI_COMM_WORLD);
MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
printf("MPI-process %d.", mpi_rank);
}
else
printf("thread 0.");
#else
printf("thread %" PRIu64 ".", H5TS_thread_id());
#endif
#ifdef H5_HAVE_WIN32_API
err = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (err != 0) {
/* could not find a usable WinSock DLL */
return;
}
/* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions greater */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we */
/* requested. */
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
/* could not find a usable WinSock DLL */
WSACleanup();
return;
}
#endif
#ifdef H5_HAVE_GETHOSTNAME
if (gethostname(hostname, (size_t)80) < 0)
printf(" gethostname failed\n");
else
printf(" hostname=%s\n", hostname);
#else
printf(" gethostname not supported\n");
#endif
#ifdef H5_HAVE_WIN32_API
WSACleanup();
#endif
#ifdef H5_HAVE_PARALLEL
/* Prevent output here from getting mixed with later output */
if (mpi_initialized && !mpi_finalized)
MPI_Barrier(MPI_COMM_WORLD);
#endif
}
#ifdef H5_HAVE_PARALLEL
/*
* Function: h5_set_info_object
* Purpose: Process environment variables setting to set up MPI Info
* object.
* Return: 0 if all is fine; otherwise non-zero.
*/
int
h5_set_info_object(void)
{
char *envp; /* environment pointer */
int ret_value = 0;
/* handle any MPI INFO hints via $HDF5_MPI_INFO */
if ((envp = getenv("HDF5_MPI_INFO")) != NULL) {
char *next, *valp;
valp = envp = next = strdup(envp);
if (!valp)
return 0;
/* create an INFO object if not created yet */
if (h5_io_info_g == MPI_INFO_NULL)
MPI_Info_create(&h5_io_info_g);
do {
size_t len;
char *key_val, *endp, *namep;
if (*valp == ';')
valp++;
/* copy key/value pair into temporary buffer */
len = strcspn(valp, ";");
next = &valp[len];
if (NULL == (key_val = (char *)calloc(1, len + 1)))
return -1;
/* increment the next pointer past the terminating semicolon */
if (*next == ';')
++next;
namep = strncpy(key_val, valp, len);
/* pass up any beginning whitespaces */
while (*namep && (*namep == ' ' || *namep == '\t'))
namep++;
if (!*namep)
continue; /* was all white space, so move to next k/v pair */
/* eat up any ending white spaces */
endp = &namep[strlen(namep) - 1];
while (endp && (*endp == ' ' || *endp == '\t'))
*endp-- = '\0';
/* find the '=' */
valp = strchr(namep, '=');
if (valp != NULL) { /* it's a valid key/value pairing */
char *tmp_val = valp + 1;
/* change '=' to \0, move valp down one */
*valp-- = '\0';
/* eat up ending whitespace on the "key" part */
while (*valp == ' ' || *valp == '\t')
*valp-- = '\0';
valp = tmp_val;
/* eat up beginning whitespace on the "value" part */
while (*valp == ' ' || *valp == '\t')
*valp++ = '\0';
/* actually set the darned thing */
if (MPI_SUCCESS != MPI_Info_set(h5_io_info_g, namep, valp)) {
printf("MPI_Info_set failed\n");
ret_value = -1;
}
}
valp = next;
free(key_val);
} while (next && *next);
free(envp);
}
return ret_value;
}
/*
* Function: h5_dump_info_object
* Purpose: Display content of an MPI Info object
* Return: void
*/
void
h5_dump_info_object(MPI_Info info)
{
char key[MPI_MAX_INFO_KEY + 1];
char value[MPI_MAX_INFO_VAL + 1];
int flag;
int i, nkeys;
printf("Dumping MPI Info Object (up to %d bytes per item):\n", MPI_MAX_INFO_VAL);
if (info == MPI_INFO_NULL) {
printf("object is MPI_INFO_NULL\n");
}
else {
MPI_Info_get_nkeys(info, &nkeys);
printf("object has %d items\n", nkeys);
for (i = 0; i < nkeys; i++) {
MPI_Info_get_nthkey(info, i, key);
MPI_Info_get(info, key, MPI_MAX_INFO_VAL, value, &flag);
printf("%s=%s\n", key, value);
}
}
}
#endif /* H5_HAVE_PARALLEL */
/*-------------------------------------------------------------------------
* Function: h5_get_file_size
*
* Purpose: Get the current size of a file (in bytes)
*
* Return: Success: Size of file in bytes
* Failure: -1
*
*-------------------------------------------------------------------------
*/
/* Disable warning for "format not a string literal" here -QAK */
/*
* This pragma only needs to surround the snprintf() calls with
* temp in the code below, but early (4.4.7, at least) gcc only
* allows diagnostic pragmas to be toggled outside of functions.
*/
H5_GCC_CLANG_DIAG_OFF("format-nonliteral")
h5_stat_size_t
h5_get_file_size(const char *filename, hid_t fapl)
{
char temp[2048]; /* Temporary buffer for file names */
h5_stat_t sb; /* Structure for querying file info */
int j = 0;
if (fapl == H5P_DEFAULT) {
/* Get the file's statistics */
if (0 == HDstat(filename, &sb))
return ((h5_stat_size_t)sb.st_size);
} /* end if */
else {
hid_t driver; /* VFD used for file */
/* Get the driver used when creating the file */
if ((driver = H5Pget_driver(fapl)) < 0)
return (-1);
/* Check for simple cases */
if (driver == H5FD_SEC2 || driver == H5FD_STDIO || driver == H5FD_CORE ||
#ifdef H5_HAVE_WINDOWS
driver == H5FD_WINDOWS ||
#endif /* H5_HAVE_WINDOWS */
#ifdef H5_HAVE_DIRECT
driver == H5FD_DIRECT ||
#endif /* H5_HAVE_DIRECT */
driver == H5FD_LOG || driver == H5FD_SPLITTER) {
/* Get the file's statistics */
if (0 == HDstat(filename, &sb))
return ((h5_stat_size_t)sb.st_size);
} /* end if */
else if (driver == H5FD_MULTI) {
H5FD_mem_t mt;
h5_stat_size_t tot_size = 0;
char *driver_env_var = NULL;
driver_env_var = getenv(HDF5_DRIVER);
if (driver_env_var && !strcmp(driver_env_var, "split")) {
for (mt = H5FD_MEM_DEFAULT; mt < H5FD_MEM_NTYPES; mt++) {
if (mt != H5FD_MEM_DRAW && mt != H5FD_MEM_SUPER)
continue;
/* Create the filename to query */
if (mt == H5FD_MEM_DRAW) {
snprintf(temp, sizeof temp, "%s.raw", filename);
}
else {
snprintf(temp, sizeof temp, "%s.meta", filename);
}
/* Check for existence of file */
if (0 == HDaccess(temp, F_OK)) {
/* Get the file's statistics */
if (0 != HDstat(temp, &sb))
return (-1);
/* Add to total size */
tot_size += (h5_stat_size_t)sb.st_size;
} /* end if */
} /* end for */
}
else {
assert(strlen(multi_letters) == H5FD_MEM_NTYPES);
for (mt = H5FD_MEM_DEFAULT; mt < H5FD_MEM_NTYPES; mt++) {
/* Create the filename to query */
snprintf(temp, sizeof temp, "%s-%c.h5", filename, multi_letters[mt]);
/* Check for existence of file */
if (0 == HDaccess(temp, F_OK)) {
/* Get the file's statistics */
if (0 != HDstat(temp, &sb))
return (-1);
/* Add to total size */
tot_size += (h5_stat_size_t)sb.st_size;
} /* end if */
} /* end for */
}
/* Return total size */
return (tot_size);
} /* end if */
#ifdef H5_HAVE_PARALLEL
else if (driver == H5FD_MPIO) {
MPI_File fh; /* MPI file handle used to open the file and verify its size */
int mpi_ret;
MPI_Offset file_size;
mpi_ret = MPI_File_open(MPI_COMM_WORLD, filename, MPI_MODE_RDONLY, MPI_INFO_NULL, &fh);
if (mpi_ret != MPI_SUCCESS)
return -1;
mpi_ret = MPI_File_get_size(fh, &file_size);
if (mpi_ret != MPI_SUCCESS)
return -1;
mpi_ret = MPI_File_close(&fh);
if (mpi_ret != MPI_SUCCESS)
return -1;
return file_size;
}
#endif /* H5_HAVE_PARALLEL */
else if (driver == H5FD_FAMILY) {
h5_stat_size_t tot_size = 0;
/* Try all filenames possible, until we find one that's missing */
for (j = 0; /*void*/; j++) {
/* Create the filename to query */
snprintf(temp, sizeof temp, filename, j);
/* Check for existence of file */
if (HDaccess(temp, F_OK) < 0)
break;
/* Get the file's statistics */
if (0 != HDstat(temp, &sb))
return (-1);
/* Add to total size */
tot_size += (h5_stat_size_t)sb.st_size;
} /* end for */
/* Return total size */
return (tot_size);
} /* end if */
else if (driver == H5FD_SUBFILING) {
hsize_t size;
hid_t fid = H5I_INVALID_HID;
if ((fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
return -1;
if (H5Fget_filesize(fid, &size) < 0) {
H5Fclose(fid);
return -1;
}
if (H5Fclose(fid) < 0)
return -1;
return (h5_stat_size_t)size;
}
else {
/* Get the file's statistics */
if (0 == HDstat(filename, &sb))
return ((h5_stat_size_t)sb.st_size);
} /* end else */
} /* end else */
return (-1);
} /* end get_file_size() */
H5_GCC_CLANG_DIAG_ON("format-nonliteral")
/*
* This routine is designed to provide equivalent functionality to 'printf'
* and allow easy replacement for environments which don't have stdin/stdout
* available. (i.e. Windows & the Mac)
*/
H5_ATTR_FORMAT(printf, 1, 2)
int
print_func(const char *format, ...)
{
va_list arglist;
int ret_value;
va_start(arglist, format);
ret_value = vprintf(format, arglist);
va_end(arglist);
return ret_value;
}
#ifdef H5_HAVE_FILTER_SZIP
/*-------------------------------------------------------------------------
* Function: h5_szip_can_encode
*
* Purpose: Retrieve the filter config flags for szip, tell if
* encoder is available.
*
* Return: 1: decode+encode is enabled
* 0: only decode is enabled
* -1: other
*
*-------------------------------------------------------------------------
*/
int
h5_szip_can_encode(void)
{
unsigned int filter_config_flags;
H5Zget_filter_info(H5Z_FILTER_SZIP, &filter_config_flags);
if ((filter_config_flags & (H5Z_FILTER_CONFIG_ENCODE_ENABLED | H5Z_FILTER_CONFIG_DECODE_ENABLED)) == 0) {
/* filter present but neither encode nor decode is supported (???) */
return -1;
}
else if ((filter_config_flags & (H5Z_FILTER_CONFIG_ENCODE_ENABLED | H5Z_FILTER_CONFIG_DECODE_ENABLED)) ==
H5Z_FILTER_CONFIG_DECODE_ENABLED) {
/* decoder only: read but not write */
return 0;
}
else if ((filter_config_flags & (H5Z_FILTER_CONFIG_ENCODE_ENABLED | H5Z_FILTER_CONFIG_DECODE_ENABLED)) ==
H5Z_FILTER_CONFIG_ENCODE_ENABLED) {
/* encoder only: write but not read (???) */
return -1;
}
else if ((filter_config_flags & (H5Z_FILTER_CONFIG_ENCODE_ENABLED | H5Z_FILTER_CONFIG_DECODE_ENABLED)) ==
(H5Z_FILTER_CONFIG_ENCODE_ENABLED | H5Z_FILTER_CONFIG_DECODE_ENABLED)) {
return 1;
}
return (-1);
}
#endif /* H5_HAVE_FILTER_SZIP */
#ifdef H5_HAVE_PARALLEL
/*-------------------------------------------------------------------------
* Function: getenv_all
*
* Purpose: Used to get the environment that the root MPI task has.
* name specifies which environment variable to look for
* val is the string to which the value of that environment
* variable will be copied.
*
* NOTE: The pointer returned by this function is only
* valid until the next call to getenv_all and the data
* stored there must be copied somewhere else before any
* further calls to getenv_all take place.
*
* Return: pointer to a string containing the value of the environment variable
* NULL if the variable doesn't exist in task 'root's environment.
*
*-------------------------------------------------------------------------
*/
char *
getenv_all(MPI_Comm comm, int root, const char *name)
{
int mpi_size, mpi_rank, mpi_initialized, mpi_finalized;
int len;
static char *env = NULL;
assert(name);
MPI_Initialized(&mpi_initialized);
MPI_Finalized(&mpi_finalized);
if (mpi_initialized && !mpi_finalized) {
MPI_Comm_rank(comm, &mpi_rank);
MPI_Comm_size(comm, &mpi_size);
assert(root < mpi_size);
/* The root task does the getenv call
* and sends the result to the other tasks */
if (mpi_rank == root) {
env = getenv(name);
if (env) {
len = (int)strlen(env);
MPI_Bcast(&len, 1, MPI_INT, root, comm);
MPI_Bcast(env, len, MPI_CHAR, root, comm);
}
else {
/* len -1 indicates that the variable was not in the environment */
len = -1;
MPI_Bcast(&len, 1, MPI_INT, root, comm);
}
}
else {
MPI_Bcast(&len, 1, MPI_INT, root, comm);
if (len >= 0) {
if (env == NULL)
env = (char *)malloc((size_t)len + 1);
else if (strlen(env) < (size_t)len)
env = (char *)realloc(env, (size_t)len + 1);
MPI_Bcast(env, len, MPI_CHAR, root, comm);
env[len] = '\0';
}
else {
if (env)
free(env);
env = NULL;
}
}
#ifndef NDEBUG
MPI_Barrier(comm);
#endif
}
else {
/* use original getenv */
if (env)
free(env);
env = getenv(name);
} /* end if */
return env;
}
#endif
/*-------------------------------------------------------------------------
* Function: h5_make_local_copy
*
* Purpose: Make copy of file. Some tests write to data files under that
* are under version control. Those tests should make a copy of
* the versioned file and write to the copy. This function
* prepends srcdir to the name of the file to be copied and uses
* the name of the copy as is.
*
* Return: Success: 0
*
* Failure: -1
*
*-------------------------------------------------------------------------
*/
int
h5_make_local_copy(const char *origfilename, const char *local_copy_name)
{
int fd_old = (-1), fd_new = (-1); /* File descriptors for copying data */
ssize_t nread; /* Number of bytes read in */
void *buf = NULL; /* Buffer for copying data */
const char *filename = H5_get_srcdir_filename(origfilename); /* Get the test file name to copy */
if (!filename)
goto error;
/* Allocate copy buffer */
if (NULL == (buf = calloc((size_t)1, (size_t)READ_BUF_SIZE)))
goto error;
/* Copy old file into temporary file */
if ((fd_old = HDopen(filename, O_RDONLY)) < 0)
goto error;
if ((fd_new = HDopen(local_copy_name, O_RDWR | O_CREAT | O_TRUNC, H5_POSIX_CREATE_MODE_RW)) < 0)
goto error;
/* Copy data */
while ((nread = HDread(fd_old, buf, (size_t)READ_BUF_SIZE)) > 0)
if (HDwrite(fd_new, buf, (size_t)nread) < 0)
goto error;
/* Close files */
if (HDclose(fd_old) < 0)
goto error;
if (HDclose(fd_new) < 0)
goto error;
/* Release memory */
free(buf);
return 0;
error:
/* ignore return values since we're already noted the problem */
if (fd_old > 0)
HDclose(fd_old);
if (fd_new > 0)
HDclose(fd_new);
free(buf);
return -1;
} /* end h5_make_local_copy() */
/*-------------------------------------------------------------------------
* Function: h5_verify_cached_stabs_cb
*
* Purpose: Callback function for h5_verify_cached_stabs.
*
* Return: SUCCEED/FAIL
*
*-------------------------------------------------------------------------
*/
static herr_t
h5_verify_cached_stabs_cb(hid_t oid, const char H5_ATTR_UNUSED *name, const H5O_info2_t *oinfo,
void H5_ATTR_UNUSED *udata)
{
if (oinfo->type == H5O_TYPE_GROUP)
return H5G__verify_cached_stabs_test(oid);
else
return SUCCEED;
} /* end h5_verify_cached_stabs_cb() */
/*-------------------------------------------------------------------------
* Function: h5_verify_cached_stabs
*
* Purpose: Verifies that all groups in every file in base_name have
* their symbol table information cached (if present, and if
* the parent group also uses a symbol table). Does not
* check that the root group's symbol table information is
* cached in the superblock.
*
* Return: Success: 0
*
* Failure: -1
*
*-------------------------------------------------------------------------
*/
herr_t
h5_verify_cached_stabs(const char *base_name[], hid_t fapl)
{
hid_t file = H5I_INVALID_HID;
char filename[1024];
int i = 0;
while (base_name[i]) {
if (h5_fixname(base_name[i], fapl, filename, sizeof(filename)) == NULL)
continue;
H5E_BEGIN_TRY
{
file = H5Fopen(filename, H5F_ACC_RDONLY, fapl);
}
H5E_END_TRY
if (file < 0) {
i++;
continue;
} /* end if */
if (H5Ovisit3(file, H5_INDEX_NAME, H5_ITER_NATIVE, h5_verify_cached_stabs_cb, NULL, H5O_INFO_BASIC) <
0)
goto error;
if (H5Fclose(file) < 0)
goto error;
file = -1;
i++;
} /* end while */
return 0;
error:
H5E_BEGIN_TRY
{
H5Fclose(file);
}
H5E_END_TRY
return -1;
}
/*-------------------------------------------------------------------------
* Function: h5_send_message
*
* Purpose: Sends the specified signal.
*
* In terms of this test framework, a signal consists of a file
* on disk. Since there are multiple processes that need to
* communicate with each other, they do so by writing and
* reading signal files on disk, the names and contents of
* which are used to inform a process about when it can
* proceed and what it should do next.
*
* This function writes a signal file. The first argument is
* the name of the signal file, and the second and third
* arguments are the contents of the first two lines of the
* signal file. The last two arguments may be NULL.
*
* Return: void
*
*-------------------------------------------------------------------------
*/
void
h5_send_message(const char *send, const char *arg1, const char *arg2)
{
FILE *signalfile = NULL;
/* Create signal file (which will send signal to some other process) */
signalfile = fopen(TMP_SIGNAL_FILE, "w+");
/* Write messages to signal file, if provided */
if (arg2 != NULL) {
assert(arg1);
fprintf(signalfile, "%s\n%s\n", arg1, arg2);
} /* end if */
else if (arg1 != NULL) {
assert(arg2 == NULL);
fprintf(signalfile, "%s\n", arg1);
} /* end if */
else {
assert(arg1 == NULL);
assert(arg2 == NULL);
} /* end else */
fclose(signalfile);
HDrename(TMP_SIGNAL_FILE, send);
} /* h5_send_message() */
/*-------------------------------------------------------------------------
* Function: h5_wait_message
*
* Purpose: Waits for the specified signal.
*
* In terms of this test framework, a signal consists of a file
* on disk. Since there are multiple processes that need to
* communicate with each other, they do so by writing and
* reading signal files on disk, the names and contents of
* which are used to inform a process about when it can
* proceed and what it should do next.
*
* This function continuously attempts to read the specified
* signal file from disk, and only continues once it has
* successfully done so (i.e., only after another process has
* called the "h5_send_message" function to write the signal file).
* This function will then immediately remove the file (i.e.,
* to indicate that it has been received and can be reused),
* and then exits, allowing the calling function to continue.
*
* Return: void
*
*-------------------------------------------------------------------------
*/
herr_t
h5_wait_message(const char *waitfor)
{
FILE *returnfile;
time_t t0, t1;
/* Start timer. If this function runs for too long (i.e.,
expected signal is never received), it will
return failure */
HDtime(&t0);
/* Wait for return signal from some other process */
while ((returnfile = fopen(waitfor, "r")) == NULL) {
/* make note of current time. */
HDtime(&t1);
/* If we've been waiting for a signal for too long, then
it was likely never sent and we should fail rather
than loop infinitely */
if (HDdifftime(t1, t0) > MESSAGE_TIMEOUT) {
fprintf(stdout, "Error communicating between processes. Make sure test script is running.\n");
TEST_ERROR;
} /* end if */
} /* end while */
fclose(returnfile);
HDunlink(waitfor);
return SUCCEED;
error:
return FAIL;
} /* h5_wait_message() */
/* Functions for the dummy VFD class (see below).
*
* Useful for testing things like ID handling where we shouldn't mess with the
* real VFDs.
*/
static H5FD_t *dummy_vfd_open(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr);
static H5FD_t *
dummy_vfd_open(const char H5_ATTR_UNUSED *name, unsigned H5_ATTR_UNUSED flags, hid_t H5_ATTR_UNUSED fapl_id,
haddr_t H5_ATTR_UNUSED maxaddr)
{
return NULL;
}
static herr_t dummy_vfd_close(H5FD_t *_file);
static herr_t
dummy_vfd_close(H5FD_t H5_ATTR_UNUSED *_file)
{
return FAIL;
}
static haddr_t dummy_vfd_get_eoa(const H5FD_t *file, H5FD_mem_t type);
static haddr_t
dummy_vfd_get_eoa(const H5FD_t H5_ATTR_UNUSED *file, H5FD_mem_t H5_ATTR_UNUSED type)
{
return HADDR_UNDEF;
}
static herr_t dummy_vfd_set_eoa(H5FD_t *_file, H5FD_mem_t type, haddr_t addr);
static herr_t
dummy_vfd_set_eoa(H5FD_t H5_ATTR_UNUSED *_file, H5FD_mem_t H5_ATTR_UNUSED type, haddr_t H5_ATTR_UNUSED addr)
{
return FAIL;
}
static haddr_t dummy_vfd_get_eof(const H5FD_t *file, H5FD_mem_t type);
static haddr_t
dummy_vfd_get_eof(const H5FD_t H5_ATTR_UNUSED *file, H5FD_mem_t H5_ATTR_UNUSED type)
{
return HADDR_UNDEF;
}
static herr_t dummy_vfd_read(H5FD_t *_file, H5FD_mem_t type, hid_t fapl_id, haddr_t addr, size_t size,
void *buf);
static herr_t
dummy_vfd_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)
{
return FAIL;
}
static herr_t dummy_vfd_write(H5FD_t *_file, H5FD_mem_t type, hid_t fapl_id, haddr_t addr, size_t size,
const void *buf);
static herr_t
dummy_vfd_write(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, const void H5_ATTR_UNUSED *buf)
{
return FAIL;
}
/* Dummy VFD with the minimum parameters to make a VFD that can be registered */
#define DUMMY_VFD_VALUE (H5FD_class_value_t)155
static const H5FD_class_t H5FD_dummy_g = {
H5FD_CLASS_VERSION, /* struct version */
DUMMY_VFD_VALUE, /* value */
"dummy", /* name */
1, /* maxaddr */
H5F_CLOSE_WEAK, /* fc_degree */
NULL, /* terminate */
NULL, /* sb_size */
NULL, /* sb_encode */
NULL, /* sb_decode */
0, /* fapl_size */
NULL, /* fapl_get */
NULL, /* fapl_copy */
NULL, /* fapl_free */
0, /* dxpl_size */
NULL, /* dxpl_copy */
NULL, /* dxpl_free */
dummy_vfd_open, /* open */
dummy_vfd_close, /* close */
NULL, /* cmp */
NULL, /* query */
NULL, /* get_type_map */
NULL, /* alloc */
NULL, /* free */
dummy_vfd_get_eoa, /* get_eoa */
dummy_vfd_set_eoa, /* set_eoa */
dummy_vfd_get_eof, /* get_eof */
NULL, /* get_handle */
dummy_vfd_read, /* read */
dummy_vfd_write, /* write */
NULL, /* read_vector */
NULL, /* write_vector */
NULL, /* read_selection */
NULL, /* write_selection */
NULL, /* flush */
NULL, /* truncate */
NULL, /* lock */
NULL, /* unlock */
NULL, /* del */
NULL, /* ctl */
H5FD_FLMAP_DICHOTOMY /* fl_map */
};
/*-------------------------------------------------------------------------
* Function: h5_get_dummy_vfd_class()
*
* Purpose: Returns a disposable, generally non-functional,
* VFD class struct.
*
* In some of the test code, we need a disposable VFD but
* we don't want to mess with the real VFDs and we also
* don't have access to the internals of the real VFDs (which
* use static globals and functions) to easily duplicate
* them (e.g.: for testing VFD ID handling).
*
* This API call will return a pointer to a VFD class that
* can be used to construct a test VFD using H5FDregister().
*
* Return: Success: A pointer to a VFD class struct
* Failure: NULL
*
*-------------------------------------------------------------------------
*/
H5FD_class_t *
h5_get_dummy_vfd_class(void)
{
H5FD_class_t *vfd_class = NULL; /* Dummy VFD that will be returned */
/* Create the class and initialize everything to zero/NULL */
if (NULL == (vfd_class = (H5FD_class_t *)malloc(sizeof(H5FD_class_t))))
TEST_ERROR;
/* Copy the dummy VFD */
memcpy(vfd_class, &H5FD_dummy_g, sizeof(H5FD_class_t));
return vfd_class;
error:
if (vfd_class)
free(vfd_class);
return NULL;
} /* h5_get_dummy_vfd_class */
/*-------------------------------------------------------------------------
* Function: h5_get_dummy_vol_class()
*
* Purpose: Returns a disposable, generally non-functional,
* VOL class struct.
*
* In some of the test code, we need a disposable VOL connector
* but we don't want to mess with the real VFDs and we also
* don't have access to the internals of the real VOL connectors
* (which use static globals and functions) to easily duplicate
* them (e.g.: for testing VOL connector ID handling).
*
* This API call will return a pointer to a VOL class that
* can be used to construct a test VOL using H5VLregister_connector().
*
* Return: Success: A pointer to a VOL class struct
* Failure: NULL
*
*-------------------------------------------------------------------------
*/
H5VL_class_t *
h5_get_dummy_vol_class(void)
{
H5VL_class_t *vol_class = NULL; /* Dummy VOL class that will be returned */
/* Create the class and initialize everything to zero/NULL */
if (NULL == (vol_class = (H5VL_class_t *)calloc((size_t)1, sizeof(H5VL_class_t))))
TEST_ERROR;
/* Fill in the minimum parameters to make a VOL connector class that
* can be registered.
*/
vol_class->version = H5VL_VERSION;
vol_class->name = "dummy";
return vol_class;
error:
if (vol_class)
free(vol_class);
return NULL;
} /* h5_get_dummy_vol_class */
/*-------------------------------------------------------------------------
* Function: h5_get_version_string
*
* Purpose: Get the string that corresponds to the libvery version bound.
*
* Return: The string
*
*-------------------------------------------------------------------------
*/
H5_ATTR_PURE const char *
h5_get_version_string(H5F_libver_t libver)
{
return (LIBVER_NAMES[libver]);
} /* end of h5_get_version_string */
/*-------------------------------------------------------------------------
* Function: h5_compare_file_bytes()
*
* Purpose: Helper function to compare two files byte-for-byte
*
* Return: Success: 0, if files are identical
* Failure: -1, if files differ
*
*-------------------------------------------------------------------------
*/
int
h5_compare_file_bytes(char *f1name, char *f2name)
{
FILE *f1ptr = NULL; /* two file pointers */
FILE *f2ptr = NULL;
HDoff_t f1size = 0; /* size of the files */
HDoff_t f2size = 0;
char f1char = 0; /* one char from each file */
char f2char = 0;
HDoff_t ii = 0;
int ret_value = 0; /* for error handling */
/* Open files for reading */
f1ptr = fopen(f1name, "rb");
if (f1ptr == NULL) {
fprintf(stderr, "Unable to fopen() %s\n", f1name);
ret_value = -1;
goto done;
}
f2ptr = fopen(f2name, "rb");
if (f2ptr == NULL) {
fprintf(stderr, "Unable to fopen() %s\n", f2name);
ret_value = -1;
goto done;
}
/* Get the file sizes and verify that they equal */
HDfseek(f1ptr, 0, SEEK_END);
f1size = HDftell(f1ptr);
HDfseek(f2ptr, 0, SEEK_END);
f2size = HDftell(f2ptr);
if (f1size != f2size) {
fprintf(stderr, "Files differ in size, %" PRIuHSIZE " vs. %" PRIuHSIZE "\n", (hsize_t)f1size,
(hsize_t)f2size);
ret_value = -1;
goto done;
}
/* Compare each byte and fail if a difference is found */
HDrewind(f1ptr);
HDrewind(f2ptr);
for (ii = 0; ii < f1size; ii++) {
if (fread(&f1char, 1, 1, f1ptr) != 1) {
ret_value = -1;
goto done;
}
if (fread(&f2char, 1, 1, f2ptr) != 1) {
ret_value = -1;
goto done;
}
if (f1char != f2char) {
fprintf(stderr, "Mismatch @ 0x%" PRIXHSIZE ": 0x%X != 0x%X\n", (hsize_t)ii, f1char, f2char);
ret_value = -1;
goto done;
}
}
done:
if (f1ptr)
fclose(f1ptr);
if (f2ptr)
fclose(f2ptr);
return ret_value;
} /* end h5_compare_file_bytes() */
/*-------------------------------------------------------------------------
* Function: H5_get_srcdir_filename
*
* Purpose: Append the test file name to the srcdir path and return the whole string
*
* Return: The string or NULL (errors or not enough space)
*
*-------------------------------------------------------------------------
*/
const char *
H5_get_srcdir_filename(const char *filename)
{
const char *srcdir = H5_get_srcdir();
/* Check for error */
if (NULL == srcdir)
return NULL;
/* Build path to test file. We're checking the length so suppress
* the gcc format-truncation warning.
*/
if ((strlen(srcdir) + strlen("testfiles/") + strlen(filename) + 1) < sizeof(srcdir_testpath)) {
H5_GCC_DIAG_OFF("format-truncation")
snprintf(srcdir_testpath, sizeof(srcdir_testpath), "%stestfiles/%s", srcdir, filename);
H5_GCC_DIAG_ON("format-truncation")
return srcdir_testpath;
}
/* If not enough space, just return NULL */
return NULL;
} /* end H5_get_srcdir_filename() */
/*-------------------------------------------------------------------------
* Function: H5_get_srcdir
*
* Purpose: Just return the srcdir path
*
* Return: The string
*
*-------------------------------------------------------------------------
*/
const char *
H5_get_srcdir(void)
{
const char *srcdir = getenv("srcdir");
/* Check for using the srcdir from configure time */
if (NULL == srcdir)
srcdir = config_srcdir;
/* Build path to all test files */
if ((strlen(srcdir) + 2) < sizeof(srcdir_path)) {
snprintf(srcdir_path, sizeof(srcdir_path), "%s/", srcdir);
return (srcdir_path);
} /* end if */
else
return (NULL);
} /* end H5_get_srcdir() */
/*-------------------------------------------------------------------------
* Function: h5_duplicate_file_by_bytes
*
* Purpose: Duplicate a file byte-for-byte at filename/path 'orig'
* to a new (or replaced) file at 'dest'.
*
* Return: Success: 0, completed successfully
* Failure: -1
*
*-------------------------------------------------------------------------
*/
int
h5_duplicate_file_by_bytes(const char *orig, const char *dest)
{
FILE *orig_ptr = NULL;
FILE *dest_ptr = NULL;
hsize_t fsize = 0;
hsize_t read_size = 0;
hsize_t max_buf = 0;
void *dup_buf = NULL;
int ret_value = 0;
max_buf = 4096 * sizeof(char);
orig_ptr = fopen(orig, "rb");
if (NULL == orig_ptr) {
ret_value = -1;
goto done;
}
HDfseek(orig_ptr, 0, SEEK_END);
fsize = (hsize_t)HDftell(orig_ptr);
HDrewind(orig_ptr);
dest_ptr = fopen(dest, "wb");
if (NULL == dest_ptr) {
ret_value = -1;
goto done;
}
read_size = MIN(fsize, max_buf);
dup_buf = malloc(read_size);
if (NULL == dup_buf) {
ret_value = -1;
goto done;
}
while (read_size > 0) {
if (fread(dup_buf, read_size, 1, orig_ptr) != 1) {
ret_value = -1;
goto done;
}
fwrite(dup_buf, read_size, 1, dest_ptr);
fsize -= read_size;
read_size = MIN(fsize, max_buf);
}
done:
if (orig_ptr != NULL)
fclose(orig_ptr);
if (dest_ptr != NULL)
fclose(dest_ptr);
if (dup_buf != NULL)
free(dup_buf);
return ret_value;
} /* end h5_duplicate_file_by_bytes() */
/*-------------------------------------------------------------------------
* Function: h5_check_if_file_locking_enabled
*
* Purpose: Checks if file locking is enabled on this file system.
*
* Return: SUCCEED/FAIL
* are_enabled will be false if file locking is disabled on
* the file system of if there were errors.
*
*-------------------------------------------------------------------------
*/
herr_t
h5_check_if_file_locking_enabled(bool *is_enabled)
{
const char *filename = "locking_test_file";
int pmode = O_RDWR | O_CREAT | O_TRUNC;
int fd = -1;
*is_enabled = true;
if ((fd = HDopen(filename, pmode, H5_POSIX_CREATE_MODE_RW)) < 0)
goto error;
/* Test HDflock() to see if it works */
if (HDflock(fd, LOCK_EX | LOCK_NB) < 0) {
if (ENOSYS == errno) {
/* When errno is set to ENOSYS, the file system does not support
* locking, so ignore it. This is most frequently used on
* Lustre. If we also want to check for disabled NFS locks
* we'll need to check for ENOLCK, too. That isn't done by
* default here since that could also represent an actual
* error condition.
*/
errno = 0;
*is_enabled = false;
}
else
goto error;
}
if (HDflock(fd, LOCK_UN) < 0)
goto error;
if (HDclose(fd) < 0)
goto error;
if (HDremove(filename) < 0)
goto error;
return SUCCEED;
error:
*is_enabled = false;
if (fd > -1) {
HDclose(fd);
HDremove(filename);
}
return FAIL;
} /* end h5_check_if_file_locking_enabled() */
/*-------------------------------------------------------------------------
* Function: h5_check_file_locking_env_var
*
* Purpose: Checks if the HDF5_USE_FILE_LOCKING file locking
* environment variable is set and parses its value if so.
*
* If the environment variable is not set, both `use_locks`
* and `ignore_disabled_locks` will be set to FAIL to indicate
* this. Otherwise, they will each be set appropriately based
* on the setting for the environment variable.
*
* Return: Nothing
*
*-------------------------------------------------------------------------
*/
void
h5_check_file_locking_env_var(htri_t *use_locks, htri_t *ignore_disabled_locks)
{
char *lock_env_var = NULL;
assert(use_locks);
assert(ignore_disabled_locks);
lock_env_var = getenv(HDF5_USE_FILE_LOCKING);
if (lock_env_var && (!strcmp(lock_env_var, "FALSE") || !strcmp(lock_env_var, "0"))) {
*use_locks = false; /* Override: Never use locks */
*ignore_disabled_locks = FAIL;
}
else if (lock_env_var && !strcmp(lock_env_var, "BEST_EFFORT")) {
*use_locks = true; /* Override: Always use locks */
*ignore_disabled_locks = true; /* Override: Ignore disabled locks */
}
else if (lock_env_var && (!strcmp(lock_env_var, "TRUE") || !strcmp(lock_env_var, "1"))) {
*use_locks = true; /* Override: Always use locks */
*ignore_disabled_locks = false; /* Override: Don't ignore disabled locks */
}
else {
/* Environment variable not set, or not set correctly */
*use_locks = FAIL;
*ignore_disabled_locks = FAIL;
}
}
/*-------------------------------------------------------------------------
* Function: h5_using_native_vol
*
* Purpose: Checks if the VOL connector being used is (or the VOL
* connector stack being used resolves to) the native VOL
* connector. Either or both of fapl_id and obj_id may be
* provided, but checking of obj_id takes precedence.
* H5I_INVALID_HID should be specified for the parameter that
* is not provided.
*
* obj_id must be the ID of an HDF5 object that is accessed
* with the VOL connector to check. If obj_id is provided, the
* entire VOL connector stack is checked to see if it resolves
* to the native VOL connector. If only fapl_id is provided,
* only the top-most VOL connector set on fapl_id is checked
* against the native VOL connector.
*
* The HDF5_VOL_CONNECTOR environment variable is not checked
* here, as that only overrides the setting for the default
* File Access Property List, which may not be the File Access
* Property List used for accessing obj_id. There is also
* complexity in determining whether the connector stack
* resolves to the native VOL connector when the only
* information available is a string.
*
* Return: Non-negative on success/Negative on failure
*
*-------------------------------------------------------------------------
*/
herr_t
h5_using_native_vol(hid_t fapl_id, hid_t obj_id, bool *is_native_vol)
{
hbool_t is_native = false;
hid_t native_id = H5I_INVALID_HID;
hid_t vol_id = H5I_INVALID_HID;
herr_t ret_value = SUCCEED;
assert((fapl_id >= 0) || (obj_id >= 0));
assert(is_native_vol);
if (fapl_id == H5P_DEFAULT)
fapl_id = H5P_FILE_ACCESS_DEFAULT;
if (obj_id >= 0) {
if (H5VLobject_is_native(obj_id, &is_native) < 0) {
ret_value = FAIL;
goto done;
}
}
else {
if (true != H5VLis_connector_registered_by_value(H5VL_NATIVE_VALUE)) {
ret_value = FAIL;
goto done;
}
if ((native_id = H5VLget_connector_id_by_value(H5VL_NATIVE_VALUE)) < 0) {
ret_value = FAIL;
goto done;
}
if (H5Pget_vol_id(fapl_id, &vol_id) < 0) {
ret_value = FAIL;
goto done;
}
if (vol_id == native_id)
is_native = true;
}
*is_native_vol = is_native;
done:
if (vol_id != H5I_INVALID_HID)
H5VLclose(vol_id);
if (native_id != H5I_INVALID_HID)
H5VLclose(native_id);
return ret_value;
}
/*-------------------------------------------------------------------------
* Function: h5_using_default_driver
*
* Purpose: Checks if the specified VFD name matches the library's
* default VFD. If `drv_name` is NULL, the HDF5_DRIVER
* environment is checked instead (if it is set).
*
* Return: true/false
*
*-------------------------------------------------------------------------
*/
bool
h5_using_default_driver(const char *drv_name)
{
bool ret_val = true;
assert(H5_DEFAULT_VFD == H5FD_SEC2);
if (!drv_name)
drv_name = getenv(HDF5_DRIVER);
if (drv_name)
return (!strcmp(drv_name, "sec2") || !strcmp(drv_name, "nomatch"));
return ret_val;
}
/*-------------------------------------------------------------------------
* Function: h5_using_parallel_driver
*
* Purpose: Checks if the current VFD set on the given FAPL is a
* parallel-enabled VFD (The MPI I/O VFD, for example).
*
* This is mostly useful for avoiding tests that use features
* which are not currently supported for parallel HDF5, such
* as writing of VL or region reference datatypes.
*
* Return: Non-negative on success/Negative on failure
*
*-------------------------------------------------------------------------
*/
herr_t
h5_using_parallel_driver(hid_t fapl_id, bool *driver_is_parallel)
{
unsigned long feat_flags = 0;
hid_t driver_id = H5I_INVALID_HID;
herr_t ret_value = SUCCEED;
assert(fapl_id >= 0);
assert(driver_is_parallel);
if (fapl_id == H5P_DEFAULT)
fapl_id = H5P_FILE_ACCESS_DEFAULT;
if ((driver_id = H5Pget_driver(fapl_id)) < 0)
return FAIL;
if (H5FDdriver_query(driver_id, &feat_flags) < 0)
return FAIL;
*driver_is_parallel = (feat_flags & H5FD_FEAT_HAS_MPI);
return ret_value;
}
/*-------------------------------------------------------------------------
* Function: h5_driver_is_default_vfd_compatible
*
* Purpose: Checks if the current VFD set on the given FAPL creates a
* file that is compatible with the default VFD. Some examples
* are the core and MPI I/O drivers. Some counterexamples are
* the multi and family drivers, which split the HDF5 file
* into several different files.
*
* This routine is helpful for skipping tests that use
* pre-generated files. VFDs that create files which aren't
* compatible with the default VFD will generally not be able
* to open these pre-generated files and those particular
* tests will fail.
*
* Return: Non-negative on success/Negative on failure
*
*-------------------------------------------------------------------------
*/
herr_t
h5_driver_is_default_vfd_compatible(hid_t fapl_id, bool *default_vfd_compatible)
{
unsigned long feat_flags = 0;
hid_t driver_id = H5I_INVALID_HID;
herr_t ret_value = SUCCEED;
assert(fapl_id >= 0);
assert(default_vfd_compatible);
if (fapl_id == H5P_DEFAULT)
fapl_id = H5P_FILE_ACCESS_DEFAULT;
if ((driver_id = H5Pget_driver(fapl_id)) < 0)
return FAIL;
if (H5FDdriver_query(driver_id, &feat_flags) < 0)
return FAIL;
*default_vfd_compatible = (feat_flags & H5FD_FEAT_DEFAULT_VFD_COMPATIBLE);
return ret_value;
} /* end h5_driver_is_default_vfd_compatible() */
/*-------------------------------------------------------------------------
* Function: h5_driver_uses_multiple_files
*
* Purpose: Checks if the specified VFD name matches a driver that
* stores data using multiple files.
*
* The following flags can be used to control what types of
* drivers are checked for by this routine:
*
* H5_EXCLUDE_MULTIPART_DRIVERS - This flag excludes any
* drivers which store data using multiple files which,
* together, make up a single logical file. These are
* drivers like the split, multi and family drivers.
*
* H5_EXCLUDE_NON_MULTIPART_DRIVERS - This flag excludes any
* drivers which store data using multiple files which are
* separate logical files. The splitter driver is an example
* of this type of driver.
*
* Eventually, this should become a VFD feature flag so this
* check is less fragile.
*
* Return: true/false
*
*-------------------------------------------------------------------------
*/
bool
h5_driver_uses_multiple_files(const char *drv_name, unsigned flags)
{
bool ret_val = false;
if (!drv_name)
drv_name = getenv(HDF5_DRIVER);
if (drv_name) {
if ((flags & H5_EXCLUDE_MULTIPART_DRIVERS) == 0) {
if (!strcmp(drv_name, "split") || !strcmp(drv_name, "multi") || !strcmp(drv_name, "family") ||
!strcmp(drv_name, H5FD_SUBFILING_NAME))
return true;
}
if ((flags & H5_EXCLUDE_NON_MULTIPART_DRIVERS) == 0) {
if (!strcmp(drv_name, "splitter"))
return true;
}
}
return ret_val;
}