Hello,

On 2015-03-08 22:52, Elena Pourmal wrote:
Your patch was reviewed before 1.8.14 release but didn’t make it into the 
1.8.14 release due to a few missing items (see below).

I've attached a new version of the patch.


Would it be possible for you to create:
    1. A test for this new functionality that passes at least on Linux and 
Windows.
       Please take a look at the test/dset.c and test/external.c files and add 
the new tests where they fit best. Separate test file is also OK, but then you 
will need to modify
       autoconf and CMake related files, and I don’t think it makes sense in 
this particular case.

I've added tests in test/external.c which test absolute and relative filenames and on windows path-absolute (\dir\path).

The tests work on Linux and Windows.

    2. Add the tests to the h5dump, h5ls, h5diff and h5repack tools.

I'm not sure what test to add here, as the functionality of these tools is not affected. (The output of h5dump / h5ls / h5diff should not change in any way, and h5repack should not do anything different.)

    3. A short paragraph that describes the issue and the fix for it for the 
RELEASE.txt file.

I've added that.

    4. Enhancements to the HDF5 Reference Manual and User’s Guide entries for 
H5Pset_external where applicable.

(I'm not sure where the sources for the Reference Manual and the User's Guide are, so I'm not sure who will apply these changes / how I can submit patches for that.)


http://www.hdfgroup.org/HDF5/doc/RM/RM_H5P.html#Property-SetExternal

A paragraph can be added to the Description:

If 'name' is a relative filename, it will be considered relative to the HDF5 file. In this case when the HDF5 file is moved, the external files should be moved along with it.

And to History:
1.8.15 Relative filenames are now considered relative to the HDF5 file, not to the current directory.



http://www.hdfgroup.org/HDF5/doc/UG/10_Datasets.html

To last paragraph of "5.5.4. External Storage Properties":
All referenced external data files must exist before performing raw data I/O on the dataset. This is normally not a problem since those files are being managed directly by the application or indirectly through some other library. However, if the file is transferred from its original context, care must be taken to assure that all the external files are accessible in the new location.

Add:
If relative filenames are used, they will be considered relative to the location of the HDF5 file.



Code freeze for 1.8.15 is April 1, 2015. We will need the patches for 1. and 2. 
before March 25 to tests it on the platforms beyond Linux and Windows.
Documentation patches should be provided by April 15.


Best regards,
Steffen Kieß

Index: release_docs/RELEASE.txt
===================================================================
--- release_docs/RELEASE.txt	(revision 26434)
+++ release_docs/RELEASE.txt	(working copy)
@@ -692,6 +692,8 @@
             (PVN - 2009/01/8)
       - Added code to maintain a min_clean_fraction in the metadata cache when
             in serial mode. (MAM - 2009/01/9)
+      - Interpret filesnames for external files as relative to the HDF5 file,
+            not relative to the current directory. HDFFV-8740
 
 
 
Index: src/H5Defl.c
===================================================================
--- src/H5Defl.c	(revision 26434)
+++ src/H5Defl.c	(working copy)
@@ -33,6 +33,7 @@
 #include "H5Eprivate.h"		/* Error handling		  	*/
 #include "H5Fprivate.h"		/* Files				*/
 #include "H5HLprivate.h"	/* Local Heaps				*/
+#include "H5MMprivate.h"	/* Memory management			*/
 #include "H5VMprivate.h"		/* Vector and array functions		*/
 
 
@@ -48,6 +49,7 @@
 /* Callback info for readvv operation */
 typedef struct H5D_efl_readvv_ud_t {
     const H5O_efl_t *efl;       /* Pointer to efl info */
+    H5F_t *file;                /* File the data set is in */
     unsigned char *rbuf;        /* Read buffer */
 } H5D_efl_readvv_ud_t;
 
@@ -54,6 +56,7 @@
 /* Callback info for writevv operation */
 typedef struct H5D_efl_writevv_ud_t {
     const H5O_efl_t *efl;       /* Pointer to efl info */
+    H5F_t *file;                /* File the data set is in */
     const unsigned char *wbuf;  /* Write buffer */
 } H5D_efl_writevv_ud_t;
 
@@ -75,9 +78,9 @@
     size_t mem_max_nseq, size_t *mem_curr_seq, size_t mem_len_arr[], hsize_t mem_offset_arr[]);
 
 /* Helper routines */
-static herr_t H5D__efl_read(const H5O_efl_t *efl, haddr_t addr, size_t size,
+static herr_t H5D__efl_read(const H5O_efl_t *efl, H5F_t *file, haddr_t addr, size_t size,
     uint8_t *buf);
-static herr_t H5D__efl_write(const H5O_efl_t *efl, haddr_t addr, size_t size,
+static herr_t H5D__efl_write(const H5O_efl_t *efl, H5F_t *file, haddr_t addr, size_t size,
     const uint8_t *buf);
 
 
@@ -110,6 +113,58 @@
 
 
 
+/*--------------------------------------------------------------------------
+ * Function: H5D_build_name
+ *
+ * Purpose:  Prepend extpath to external file name (for relative files) and store in FULL_NAME
+ *
+ * Return:   Non-negative on success/Negative on failure
+ *
+ * Programmer:	Steffen Kiess
+ *              March 10, 2015
+--------------------------------------------------------------------------*/
+static herr_t
+H5D_build_name(const H5F_t *file, const char* filename, char **full_name/*out*/)
+{
+    char        *extpath;               /* absolute path of directory the HDF5 file is in */
+    size_t      extpath_len;            /* length of extpath */
+    size_t      filename_len;           /* length of external file name */
+    herr_t      ret_value = SUCCEED;    /* Return value */
+
+    FUNC_ENTER_NOAPI_NOINIT
+
+    HDassert(file);
+    extpath = H5F_EXTPATH(file);
+    HDassert(extpath);
+    extpath_len = HDstrlen(extpath);
+    filename_len = HDstrlen(filename);
+
+    if (H5_CHECK_ABSOLUTE(filename)) {
+      /* If the filename is absolute, simply use filename */
+      if(NULL == (*full_name = (char *)H5MM_strdup(filename)))
+        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed")
+    } else if (H5_CHECK_ABS_PATH(filename)) {
+      /* On windows: For path absolute names: Use the drive letter of extpath + the filename */
+      if(NULL == (*full_name = (char *)H5MM_malloc(filename_len + 3)))
+        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate filename buffer")
+      HDsnprintf(*full_name, (filename_len + 3), "%c:%s", extpath[0], filename);
+
+      /* No support for filenames like "d:path\file" */
+    } else {
+      /* Relative filename: Allocate a buffer to hold the filename + extpath + possibly the delimiter + terminating null byte */
+      if(NULL == (*full_name = (char *)H5MM_malloc(extpath_len + filename_len + 2)))
+        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate filename buffer")
+
+      /* Compose the full file name */
+      HDsnprintf(*full_name, (extpath_len + filename_len + 2), "%s%s%s", extpath,
+          (H5_CHECK_DELIMITER(extpath[extpath_len - 1]) ? "" : H5_DIR_SEPS), filename);
+    }
+
+done:
+    FUNC_LEAVE_NOAPI(ret_value)
+} /* H5D_build_name() */
+
+
 /*-------------------------------------------------------------------------
  * Function:	H5D__efl_construct
  *
@@ -254,7 +309,7 @@
  *-------------------------------------------------------------------------
  */
 static herr_t
-H5D__efl_read(const H5O_efl_t *efl, haddr_t addr, size_t size, uint8_t *buf)
+H5D__efl_read(const H5O_efl_t *efl, H5F_t *file, haddr_t addr, size_t size, uint8_t *buf)
 {
     int		fd = -1;
     size_t	to_read;
@@ -266,6 +321,7 @@
     ssize_t	n;
     size_t      u;                      /* Local index variable */
     herr_t      ret_value = SUCCEED;    /* Return value */
+    char *full_name = NULL;             /* File name with prefix */
 
     FUNC_ENTER_STATIC
 
@@ -291,7 +347,9 @@
 	    HGOTO_ERROR(H5E_EFL, H5E_OVERFLOW, FAIL, "read past logical end of file")
 	if(H5F_OVERFLOW_HSIZET2OFFT(efl->slot[u].offset + skip))
 	    HGOTO_ERROR(H5E_EFL, H5E_OVERFLOW, FAIL, "external file address overflowed")
-	if((fd = HDopen(efl->slot[u].name, O_RDONLY, 0)) < 0)
+        if(H5D_build_name(file, efl->slot[u].name, &full_name/*out*/) < 0)
+          HGOTO_ERROR(H5E_EFL, H5E_CANTGET, FAIL, "can't build external file name")
+	if((fd = HDopen(full_name, O_RDONLY, 0)) < 0)
 	    HGOTO_ERROR(H5E_EFL, H5E_CANTOPENFILE, FAIL, "unable to open external raw data file")
 	if(HDlseek(fd, (off_t)(efl->slot[u].offset + skip), SEEK_SET) < 0)
 	    HGOTO_ERROR(H5E_EFL, H5E_SEEKERROR, FAIL, "unable to seek in external raw data file")
@@ -315,6 +373,7 @@
     } /* end while */
 
 done:
+    full_name = (char *)H5MM_xfree(full_name);
     if(fd >= 0)
         HDclose(fd);
 
@@ -341,7 +400,7 @@
  *-------------------------------------------------------------------------
  */
 static herr_t
-H5D__efl_write(const H5O_efl_t *efl, haddr_t addr, size_t size, const uint8_t *buf)
+H5D__efl_write(const H5O_efl_t *efl, H5F_t *file, haddr_t addr, size_t size, const uint8_t *buf)
 {
     int		fd = -1;
     size_t	to_write;
@@ -352,6 +411,7 @@
     hsize_t     skip = 0;
     size_t	u;                      /* Local index variable */
     herr_t      ret_value = SUCCEED;    /* Return value */
+    char *full_name = NULL;             /* File name with prefix */
 
     FUNC_ENTER_STATIC
 
@@ -377,8 +437,10 @@
 	    HGOTO_ERROR(H5E_EFL, H5E_OVERFLOW, FAIL, "write past logical end of file")
 	if(H5F_OVERFLOW_HSIZET2OFFT(efl->slot[u].offset + skip))
 	    HGOTO_ERROR(H5E_EFL, H5E_OVERFLOW, FAIL, "external file address overflowed")
-	if((fd = HDopen(efl->slot[u].name, O_CREAT | O_RDWR, 0666)) < 0) {
-	    if(HDaccess(efl->slot[u].name, F_OK) < 0)
+        if(H5D_build_name(file, efl->slot[u].name, &full_name/*out*/) < 0)
+          HGOTO_ERROR(H5E_EFL, H5E_CANTGET, FAIL, "can't build external file name")
+	if((fd = HDopen(full_name, O_CREAT | O_RDWR, 0666)) < 0) {
+	    if(HDaccess(full_name, F_OK) < 0)
 		HGOTO_ERROR(H5E_EFL, H5E_CANTOPENFILE, FAIL, "external raw data file does not exist")
 	    else
 		HGOTO_ERROR(H5E_EFL, H5E_CANTOPENFILE, FAIL, "unable to open external raw data file")
@@ -403,6 +465,7 @@
     } /* end while */
 
 done:
+    full_name = (char *)H5MM_xfree(full_name);
     if(fd >= 0)
         HDclose(fd);
 
@@ -431,7 +494,7 @@
     FUNC_ENTER_STATIC
 
     /* Read data */
-    if(H5D__efl_read(udata->efl, dst_off, len, (udata->rbuf + src_off)) < 0)
+    if(H5D__efl_read(udata->efl, udata->file, dst_off, len, (udata->rbuf + src_off)) < 0)
         HGOTO_ERROR(H5E_DATASET, H5E_READERROR, FAIL, "EFL read failed")
 
 done:
@@ -468,6 +531,8 @@
     HDassert(io_info);
     HDassert(io_info->store->efl.nused > 0);
     HDassert(io_info->u.rbuf);
+    HDassert(io_info->dset);
+    HDassert(io_info->dset->oloc.file);
     HDassert(dset_curr_seq);
     HDassert(dset_len_arr);
     HDassert(dset_off_arr);
@@ -477,6 +542,7 @@
 
     /* Set up user data for H5VM_opvv() */
     udata.efl = &(io_info->store->efl);
+    udata.file = io_info->dset->oloc.file;
     udata.rbuf = (unsigned char *)io_info->u.rbuf;
 
     /* Call generic sequence operation routine */
@@ -511,7 +577,7 @@
     FUNC_ENTER_STATIC
 
     /* Write data */
-    if(H5D__efl_write(udata->efl, dst_off, len, (udata->wbuf + src_off)) < 0)
+    if(H5D__efl_write(udata->efl, udata->file, dst_off, len, (udata->wbuf + src_off)) < 0)
         HGOTO_ERROR(H5E_DATASET, H5E_WRITEERROR, FAIL, "EFL write failed")
 
 done:
@@ -548,6 +614,8 @@
     HDassert(io_info);
     HDassert(io_info->store->efl.nused > 0);
     HDassert(io_info->u.wbuf);
+    HDassert(io_info->dset);
+    HDassert(io_info->dset->oloc.file);
     HDassert(dset_curr_seq);
     HDassert(dset_len_arr);
     HDassert(dset_off_arr);
@@ -557,6 +625,7 @@
 
     /* Set up user data for H5VM_opvv() */
     udata.efl = &(io_info->store->efl);
+    udata.file = io_info->dset->oloc.file;
     udata.wbuf = (const unsigned char *)io_info->u.wbuf;
 
     /* Call generic sequence operation routine */
Index: src/H5Fquery.c
===================================================================
--- src/H5Fquery.c	(revision 26434)
+++ src/H5Fquery.c	(working copy)
@@ -158,7 +158,7 @@
  * Function:	H5F_get_extpath
  *
  * Purpose:	Retrieve the file's 'extpath' flags
- *		This is used by H5L_extern_traverse() to retrieve the main file's location
+ *		This is used by H5L_extern_traverse() and H5D_build_name() to retrieve the main file's location
  *		when searching the target file.
  *
  * Return:	'extpath' on success/abort on failure (shouldn't fail)
Index: src/H5system.c
===================================================================
--- src/H5system.c	(revision 26434)
+++ src/H5system.c	(working copy)
@@ -729,7 +729,7 @@
  * Function: H5_build_extpath
  *
  * Purpose:  To build the path for later searching of target file for external
- *    link.  This path can be either:
+ *    links and external files.  This path can be either:
  *                  1. The absolute path of NAME
  *                      or
  *                  2. The current working directory + relative path of NAME
Index: test/external.c
===================================================================
--- test/external.c	(revision 26434)
+++ test/external.c	(working copy)
@@ -30,6 +30,8 @@
     "extern_2",
     "extern_3",
     "extern_4",
+    "extern_5",
+    "extern_dir/file_1",
     NULL
 };
 
@@ -919,6 +921,173 @@
 
 
 /*-------------------------------------------------------------------------
+ * Function:	test_5
+ *
+ * Purpose:	Test absolute filenames for external files.
+ * 		This will create an HDF5 file in a subdirectory which will
+ * 		refer to /full/path/extern_*a.raw on unix and to
+ *              c:\full\path\extern_*a.raw and \full\path\extern_*a.raw on
+ *              windows.
+ *
+ * Return:	Success:	0
+ *
+ * 		Failure:	number of errors
+ *
+ * Programmer:	Steffen Kiess
+ *              March 10, 2015
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+test_5 (hid_t fapl)
+{
+    hid_t	file=-1;		/*file to write to		*/
+    hid_t	dcpl=-1;		/*dataset creation properties	*/
+    hid_t	space=-1;		/*data space			*/
+    hid_t	dset=-1;		/*dataset			*/
+    size_t	i;			/*miscellaneous counters	*/
+    char	cwdpath[1024];		/*working directory		*/
+    char	filename[1024];		/*file names			*/
+    int		part[25], whole[100];	/*raw data buffers		*/
+    hsize_t	cur_size;		/*current data space size	*/
+
+    TESTING("absolute filenames for external file");
+
+    h5_fixname(FILENAME[4], fapl, filename, sizeof filename);
+    if ((file=H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0) FAIL_STACK_ERROR;
+
+    /* Create the dataset */
+    if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) goto error;
+    if(NULL == HDgetcwd(cwdpath, sizeof (cwdpath)))
+        TEST_ERROR
+    for (i = 0; i < 4; i++) {
+        HDsnprintf(filename, sizeof(filename), "%s%sextern_%da.raw", cwdpath, H5_DIR_SEPS, (int) i + 1);
+#if defined(H5_HAVE_WINDOW_PATH)
+        // For windows, test path-absolute case (\dir\file.raw) for the second file
+        if (i == 1)
+            HDsnprintf(filename, sizeof(filename), "%s%sextern_%da.raw", cwdpath + 2, H5_DIR_SEPS, i + 1);
+#endif
+        if(H5Pset_external(dcpl, filename, (off_t)(i * 10), (hsize_t)sizeof part) < 0)
+	    goto error;
+    }
+    cur_size = 100;
+    if((space = H5Screate_simple(1, &cur_size, NULL)) < 0) goto error;
+    if((dset = H5Dcreate2(file, "dset1", H5T_NATIVE_INT, space, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) goto error;
+
+    /*
+     * Read the entire dataset and compare with the original
+     */
+    memset(whole, 0, sizeof(whole));
+    if(H5Dread(dset, H5T_NATIVE_INT, space, space, H5P_DEFAULT, whole) < 0) goto error;
+    for(i = 0; i < 100; i++)
+	if(whole[i] != (signed)i) {
+	    H5_FAILED();
+	    puts("    Incorrect value(s) read.");
+	    goto error;
+	} /* end if */
+
+    if (H5Dclose(dset) < 0) goto error;
+    if (H5Pclose(dcpl) < 0) goto error;
+    if (H5Sclose(space) < 0) goto error;
+    if (H5Fclose(file) < 0) goto error;
+    PASSED();
+    return 0;
+
+ error:
+    H5E_BEGIN_TRY {
+	H5Dclose(dset);
+	H5Pclose(dcpl);
+	H5Sclose(space);
+	H5Fclose(file);
+    } H5E_END_TRY;
+    return 1;
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function:	test_6
+ *
+ * Purpose:	Test relative filenames for external files.
+ * 		This will create an HDF5 file in a subdirectory which will
+ * 		refer to ../extern_*a.raw
+ *
+ * Return:	Success:	0
+ *
+ * 		Failure:	number of errors
+ *
+ * Programmer:	Steffen Kiess
+ *              March 10, 2015
+ *
+ * Modifications:
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+test_6 (hid_t fapl)
+{
+    hid_t	file=-1;		/*file to write to		*/
+    hid_t	dcpl=-1;		/*dataset creation properties	*/
+    hid_t	space=-1;		/*data space			*/
+    hid_t	dset=-1;		/*dataset			*/
+    size_t	i;			/*miscellaneous counters	*/
+    char	cwdpath[1024];		/*working directory		*/
+    char	filename[1024];		/*file names			*/
+    int		part[25], whole[100];	/*raw data buffers		*/
+    hsize_t	cur_size;		/*current data space size	*/
+
+    TESTING("relative filenames for external file");
+
+    if (HDmkdir("extern_dir", (mode_t)0755) < 0 && errno != EEXIST) TEST_ERROR;
+
+    h5_fixname(FILENAME[5], fapl, filename, sizeof filename);
+    if ((file=H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0) FAIL_STACK_ERROR;
+
+    /* Create the dataset */
+    if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) goto error;
+    if(NULL == HDgetcwd(cwdpath, sizeof (cwdpath)))
+        TEST_ERROR
+    for (i = 0; i < 4; i++) {
+        HDsnprintf(filename, sizeof(filename), "..%sextern_%da.raw", H5_DIR_SEPS, (int) i + 1);
+        if(H5Pset_external(dcpl, filename, (off_t)(i * 10), (hsize_t)sizeof part) < 0)
+	    goto error;
+    }
+    cur_size = 100;
+    if((space = H5Screate_simple(1, &cur_size, NULL)) < 0) goto error;
+    if((dset = H5Dcreate2(file, "dset1", H5T_NATIVE_INT, space, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) goto error;
+
+    /*
+     * Read the entire dataset and compare with the original
+     */
+    memset(whole, 0, sizeof(whole));
+    if(H5Dread(dset, H5T_NATIVE_INT, space, space, H5P_DEFAULT, whole) < 0) goto error;
+    for(i = 0; i < 100; i++)
+	if(whole[i] != (signed)i) {
+	    H5_FAILED();
+	    puts("    Incorrect value(s) read.");
+	    goto error;
+	} /* end if */
+
+    if (H5Dclose(dset) < 0) goto error;
+    if (H5Pclose(dcpl) < 0) goto error;
+    if (H5Sclose(space) < 0) goto error;
+    if (H5Fclose(file) < 0) goto error;
+    PASSED();
+    return 0;
+
+ error:
+    H5E_BEGIN_TRY {
+	H5Dclose(dset);
+	H5Pclose(dcpl);
+	H5Sclose(space);
+	H5Fclose(file);
+    } H5E_END_TRY;
+    return 1;
+}
+
+
+/*-------------------------------------------------------------------------
  * Function:	main
  *
  * Purpose:	Runs external dataset tests.
@@ -961,6 +1130,8 @@
     nerrors += test_2(fapl);
     nerrors += test_3(fapl);
     nerrors += test_4(fapl);
+    nerrors += test_5(fapl);
+    nerrors += test_6(fapl);
 
     /* Verify symbol table messages are cached */
     nerrors += (h5_verify_cached_stabs(FILENAME, fapl) < 0 ? 1 : 0);
@@ -978,6 +1149,7 @@
         remove("extern_3b.raw");
         remove("extern_4a.raw");
         remove("extern_4b.raw");
+        HDrmdir("extern_dir");
     }
 
     return 0;
_______________________________________________
Hdf-forum is for HDF software users discussion.
[email protected]
http://lists.hdfgroup.org/mailman/listinfo/hdf-forum_lists.hdfgroup.org
Twitter: https://twitter.com/hdf5

Reply via email to