/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /*********************************************************** * * Test program: titerate * * Test the Group & Attribute functionality * *************************************************************/ #include "testhdf5.h" #include "H5srcdir.h" #define DATAFILE "titerate.h5" /* Number of datasets for group iteration test */ #define NDATASETS 50 /* Number of attributes for attribute iteration test */ #define NATTR 50 /* Number of groups for second group iteration test */ #define ITER_NGROUPS 150 /* General maximum length of names used */ #define NAMELEN 80 /* 1-D dataset with fixed dimensions */ #define SPACE1_RANK 1 #define SPACE1_DIM1 4 typedef enum { RET_ZERO, RET_TWO, RET_CHANGE, RET_CHANGE2 } iter_enum; /* Custom group iteration callback data */ typedef struct { char name[NAMELEN]; /* The name of the object */ H5O_type_t type; /* The type of the object */ iter_enum command; /* The type of return value */ } iter_info; /* Definition for test_corrupted_attnamelen */ #define CORRUPTED_ATNAMELEN_FILE "memleak_H5O_dtype_decode_helper_H5Odtype.h5" #define DSET_NAME "image" typedef struct searched_err_t { char message[256]; bool found; } searched_err_t; /* Call back function for test_corrupted_attnamelen */ static int find_err_msg_cb(unsigned n, const H5E_error2_t *err_desc, void *_client_data); /* Local functions */ int iter_strcmp(const void *s1, const void *s2); int iter_strcmp2(const void *s1, const void *s2); static herr_t liter_cb(hid_t group, const char *name, const H5L_info2_t *info, void *op_data); static herr_t liter_cb2(hid_t group, const char *name, const H5L_info2_t *info, void *op_data); herr_t aiter_cb(hid_t group, const char *name, const H5A_info_t *ainfo, void *op_data); /**************************************************************** ** ** iter_strcmp(): String comparison routine for qsort ** ****************************************************************/ H5_ATTR_PURE int iter_strcmp(const void *s1, const void *s2) { return (strcmp(*(const char *const *)s1, *(const char *const *)s2)); } /**************************************************************** ** ** liter_cb(): Custom link iteration callback routine. ** ****************************************************************/ static herr_t liter_cb(hid_t H5_ATTR_UNUSED group, const char *name, const H5L_info2_t H5_ATTR_UNUSED *link_info, void *op_data) { iter_info *info = (iter_info *)op_data; static int count = 0; static int count2 = 0; if (!(vol_cap_flags_g & H5VL_CAP_FLAG_ITERATE)) { SKIPPED(); printf(" API functions for iterate aren't " "supported with this connector\n"); return 1; } strcpy(info->name, name); switch (info->command) { case RET_ZERO: return (0); case RET_TWO: return (2); case RET_CHANGE: count++; return (count > 10 ? 1 : 0); case RET_CHANGE2: count2++; return (count2 > 10 ? 1 : 0); default: printf("invalid iteration command"); return (-1); } /* end switch */ } /* end liter_cb() */ /**************************************************************** ** ** test_iter_group(): Test group iteration functionality ** ****************************************************************/ static void test_iter_group(hid_t fapl, bool new_format) { hid_t file; /* File ID */ hid_t dataset; /* Dataset ID */ hid_t datatype; /* Common datatype ID */ hid_t filespace; /* Common dataspace ID */ hid_t root_group, grp; /* Root group ID */ int i; /* counting variable */ hsize_t idx; /* Index in the group */ char name[NAMELEN]; /* temporary name buffer */ char *lnames[NDATASETS + 2]; /* Names of the links created */ char dataset_name[NAMELEN]; /* dataset name */ iter_info info; /* Custom iteration information */ H5G_info_t ginfo; /* Buffer for querying object's info */ herr_t ret; /* Generic return value */ /* Output message about test being performed */ MESSAGE(5, ("Testing Group Iteration Functionality\n")); if (!(vol_cap_flags_g & H5VL_CAP_FLAG_FILE_BASIC) || !(vol_cap_flags_g & H5VL_CAP_FLAG_GROUP_BASIC) || !(vol_cap_flags_g & H5VL_CAP_FLAG_GROUP_MORE) || !(vol_cap_flags_g & H5VL_CAP_FLAG_DATASET_BASIC) || !(vol_cap_flags_g & H5VL_CAP_FLAG_STORED_DATATYPES) || !(vol_cap_flags_g & H5VL_CAP_FLAG_LINK_MORE) || !(vol_cap_flags_g & H5VL_CAP_FLAG_ITERATE)) { MESSAGE(5, (" -- SKIPPED --\n")); return; } /* Create the test file with the datasets */ file = H5Fcreate(DATAFILE, H5F_ACC_TRUNC, H5P_DEFAULT, fapl); CHECK(file, FAIL, "H5Fcreate"); /* Test iterating over empty group */ info.command = RET_ZERO; idx = 0; ret = H5Literate2(file, H5_INDEX_NAME, H5_ITER_INC, &idx, liter_cb, &info); VERIFY(ret, SUCCEED, "H5Literate2"); datatype = H5Tcopy(H5T_NATIVE_INT); CHECK(datatype, FAIL, "H5Tcopy"); filespace = H5Screate(H5S_SCALAR); CHECK(filespace, FAIL, "H5Screate"); for (i = 0; i < NDATASETS; i++) { snprintf(name, sizeof(name), "Dataset %d", i); dataset = H5Dcreate2(file, name, datatype, filespace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); CHECK(dataset, FAIL, "H5Dcreate2"); /* Keep a copy of the dataset names around for later */ lnames[i] = strdup(name); CHECK_PTR(lnames[i], "strdup"); ret = H5Dclose(dataset); CHECK(ret, FAIL, "H5Dclose"); } /* end for */ /* Create a group and named datatype under root group for testing */ grp = H5Gcreate2(file, "grp", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); CHECK(ret, FAIL, "H5Gcreate2"); lnames[NDATASETS] = strdup("grp"); CHECK_PTR(lnames[NDATASETS], "strdup"); ret = H5Tcommit2(file, "dtype", datatype, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); CHECK(ret, FAIL, "H5Tcommit2"); lnames[NDATASETS + 1] = strdup("dtype"); CHECK_PTR(lnames[NDATASETS], "strdup"); /* Close everything up */ ret = H5Tclose(datatype); CHECK(ret, FAIL, "H5Tclose"); ret = H5Gclose(grp); CHECK(ret, FAIL, "H5Gclose"); ret = H5Sclose(filespace); CHECK(ret, FAIL, "H5Sclose"); ret = H5Fclose(file); CHECK(ret, FAIL, "H5Fclose"); /* Sort the dataset names */ qsort(lnames, (size_t)(NDATASETS + 2), sizeof(char *), iter_strcmp); /* Iterate through the datasets in the root group in various ways */ file = H5Fopen(DATAFILE, H5F_ACC_RDONLY, fapl); CHECK(file, FAIL, "H5Fopen"); /* These two functions, H5Oget_info_by_idx and H5Lget_name_by_idx, actually * iterate through B-tree for group members in internal library design. */ root_group = H5Gopen2(file, "/", H5P_DEFAULT); CHECK(root_group, FAIL, "H5Gopen2"); ret = H5Gget_info(root_group, &ginfo); CHECK(ret, FAIL, "H5Gget_info"); VERIFY(ginfo.nlinks, (NDATASETS + 2), "H5Gget_info"); for (i = 0; i < (int)ginfo.nlinks; i++) { H5O_info2_t oinfo; /* Object info */ ret = (herr_t)H5Lget_name_by_idx(root_group, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, dataset_name, (size_t)NAMELEN, H5P_DEFAULT); CHECK(ret, FAIL, "H5Lget_name_by_idx"); //! [H5Oget_info_by_idx3_snip] ret = H5Oget_info_by_idx3(root_group, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, &oinfo, H5O_INFO_BASIC, H5P_DEFAULT); CHECK(ret, FAIL, "H5Oget_info_by_idx"); //! [H5Oget_info_by_idx3_snip] } /* end for */ H5E_BEGIN_TRY { ret = (herr_t)H5Lget_name_by_idx(root_group, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)(NDATASETS + 3), dataset_name, (size_t)NAMELEN, H5P_DEFAULT); } H5E_END_TRY VERIFY(ret, FAIL, "H5Lget_name_by_idx"); ret = H5Gclose(root_group); CHECK(ret, FAIL, "H5Gclose"); /* These two functions, H5Oget_info_by_idx and H5Lget_name_by_idx, actually * iterate through B-tree for group members in internal library design. * (Same as test above, but with the file ID instead of opening the root group) */ ret = H5Gget_info(file, &ginfo); CHECK(ret, FAIL, "H5Gget_info"); VERIFY(ginfo.nlinks, NDATASETS + 2, "H5Gget_info"); for (i = 0; i < (int)ginfo.nlinks; i++) { H5O_info2_t oinfo; /* Object info */ ret = (herr_t)H5Lget_name_by_idx(file, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, dataset_name, (size_t)NAMELEN, H5P_DEFAULT); CHECK(ret, FAIL, "H5Lget_name_by_idx"); ret = H5Oget_info_by_idx3(file, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, &oinfo, H5O_INFO_BASIC, H5P_DEFAULT); CHECK(ret, FAIL, "H5Oget_info_by_idx3"); } /* end for */ H5E_BEGIN_TRY { ret = (herr_t)H5Lget_name_by_idx(file, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)(NDATASETS + 3), dataset_name, (size_t)NAMELEN, H5P_DEFAULT); } H5E_END_TRY VERIFY(ret, FAIL, "H5Lget_name_by_idx"); /* Test invalid indices for starting iteration */ info.command = RET_ZERO; idx = (hsize_t)-1; H5E_BEGIN_TRY { ret = H5Literate2(file, H5_INDEX_NAME, H5_ITER_INC, &idx, liter_cb, &info); } H5E_END_TRY VERIFY(ret, FAIL, "H5Literate2"); /* Test skipping exactly as many entries as in the group */ idx = NDATASETS + 2; H5E_BEGIN_TRY { ret = H5Literate2(file, H5_INDEX_NAME, H5_ITER_INC, &idx, liter_cb, &info); } H5E_END_TRY VERIFY(ret, FAIL, "H5Literate2"); /* Test skipping more entries than are in the group */ idx = NDATASETS + 3; H5E_BEGIN_TRY { ret = H5Literate2(file, H5_INDEX_NAME, H5_ITER_INC, &idx, liter_cb, &info); } H5E_END_TRY VERIFY(ret, FAIL, "H5Literate2"); /* Test all objects in group, when callback always returns 0 */ info.command = RET_ZERO; idx = 0; if ((ret = H5Literate2(file, H5_INDEX_NAME, H5_ITER_INC, &idx, liter_cb, &info)) > 0) TestErrPrintf("Group iteration function didn't return zero correctly!\n"); /* Test all objects in group, when callback always returns 1 */ /* This also tests the "restarting" ability, because the index changes */ info.command = RET_TWO; i = 0; idx = 0; memset(info.name, 0, NAMELEN); while ((ret = H5Literate2(file, H5_INDEX_NAME, H5_ITER_INC, &idx, liter_cb, &info)) > 0) { /* Verify return value from iterator gets propagated correctly */ VERIFY(ret, 2, "H5Literate2"); /* Increment the number of times "2" is returned */ i++; /* Verify that the index is the correct value */ VERIFY(idx, (hsize_t)i, "H5Literate2"); if (idx != (hsize_t)i) break; if (idx > (NDATASETS + 2)) TestErrPrintf("Group iteration function walked too far!\n"); /* Verify that the correct name is retrieved */ if (strncmp(info.name, lnames[(size_t)(idx - 1)], NAMELEN) != 0) TestErrPrintf( "Group iteration function didn't return name correctly for link - lnames[%u] = '%s'!\n", (unsigned)(idx - 1), lnames[(size_t)(idx - 1)]); } /* end while */ VERIFY(ret, -1, "H5Literate2"); if (i != (NDATASETS + 2)) TestErrPrintf("%u: Group iteration function didn't perform multiple iterations correctly!\n", __LINE__); /* Test all objects in group, when callback changes return value */ /* This also tests the "restarting" ability, because the index changes */ info.command = new_format ? RET_CHANGE2 : RET_CHANGE; i = 0; idx = 0; memset(info.name, 0, NAMELEN); while ((ret = H5Literate2(file, H5_INDEX_NAME, H5_ITER_INC, &idx, liter_cb, &info)) >= 0) { /* Verify return value from iterator gets propagated correctly */ VERIFY(ret, 1, "H5Literate2"); /* Increment the number of times "1" is returned */ i++; /* Verify that the index is the correct value */ VERIFY(idx, (hsize_t)(i + 10), "H5Literate2"); if (idx != (hsize_t)(i + 10)) break; if (idx > (NDATASETS + 2)) TestErrPrintf("Group iteration function walked too far!\n"); /* Verify that the correct name is retrieved */ if (strncmp(info.name, lnames[(size_t)(idx - 1)], NAMELEN) != 0) TestErrPrintf( "Group iteration function didn't return name correctly for link - lnames[%u] = '%s'!\n", (unsigned)(idx - 1), lnames[(size_t)(idx - 1)]); } /* end while */ VERIFY(ret, -1, "H5Literate2"); if (i != 42 || idx != 52) TestErrPrintf("%u: Group iteration function didn't perform multiple iterations correctly!\n", __LINE__); ret = H5Fclose(file); CHECK(ret, FAIL, "H5Fclose"); /* Free the dataset names */ for (i = 0; i < (NDATASETS + 2); i++) free(lnames[i]); } /* test_iter_group() */ /**************************************************************** ** ** aiter_cb(): Custom group iteration callback routine. ** ****************************************************************/ herr_t aiter_cb(hid_t H5_ATTR_UNUSED group, const char *name, const H5A_info_t H5_ATTR_UNUSED *ainfo, void *op_data) { iter_info *info = (iter_info *)op_data; static int count = 0; static int count2 = 0; strcpy(info->name, name); switch (info->command) { case RET_ZERO: return (0); case RET_TWO: return (2); case RET_CHANGE: count++; return (count > 10 ? 1 : 0); case RET_CHANGE2: count2++; return (count2 > 10 ? 1 : 0); default: printf("invalid iteration command"); return (-1); } /* end switch */ } /* end aiter_cb() */ /**************************************************************** ** ** test_iter_attr(): Test attribute iteration functionality ** ****************************************************************/ static void test_iter_attr(hid_t fapl, bool new_format) { hid_t file; /* File ID */ hid_t dataset; /* Common Dataset ID */ hid_t filespace; /* Common dataspace ID */ hid_t attribute; /* Attribute ID */ int i; /* counting variable */ hsize_t idx; /* Index in the attribute list */ char name[NAMELEN]; /* temporary name buffer */ char *anames[NATTR]; /* Names of the attributes created */ iter_info info; /* Custom iteration information */ herr_t ret; /* Generic return value */ /* Output message about test being performed */ MESSAGE(5, ("Testing Attribute Iteration Functionality\n")); if (!(vol_cap_flags_g & H5VL_CAP_FLAG_FILE_BASIC) || !(vol_cap_flags_g & H5VL_CAP_FLAG_DATASET_BASIC) || !(vol_cap_flags_g & H5VL_CAP_FLAG_ATTR_BASIC) || !(vol_cap_flags_g & H5VL_CAP_FLAG_ITERATE)) { MESSAGE(5, (" -- SKIPPED --\n")); return; } memset(&info, 0, sizeof(iter_info)); /* Create the test file with the datasets */ file = H5Fcreate(DATAFILE, H5F_ACC_TRUNC, H5P_DEFAULT, fapl); CHECK(file, FAIL, "H5Fcreate"); filespace = H5Screate(H5S_SCALAR); CHECK(filespace, FAIL, "H5Screate"); dataset = H5Dcreate2(file, "Dataset", H5T_NATIVE_INT, filespace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); CHECK(dataset, FAIL, "H5Dcreate2"); for (i = 0; i < NATTR; i++) { snprintf(name, sizeof(name), "Attribute %02d", i); attribute = H5Acreate2(dataset, name, H5T_NATIVE_INT, filespace, H5P_DEFAULT, H5P_DEFAULT); CHECK(attribute, FAIL, "H5Acreate2"); /* Keep a copy of the attribute names around for later */ anames[i] = strdup(name); CHECK_PTR(anames[i], "strdup"); ret = H5Aclose(attribute); CHECK(ret, FAIL, "H5Aclose"); } /* end for */ /* Close everything up */ ret = H5Dclose(dataset); CHECK(ret, FAIL, "H5Dclose"); ret = H5Sclose(filespace); CHECK(ret, FAIL, "H5Sclose"); ret = H5Fclose(file); CHECK(ret, FAIL, "H5Fclose"); /* Iterate through the attributes on the dataset in various ways */ file = H5Fopen(DATAFILE, H5F_ACC_RDONLY, fapl); CHECK(file, FAIL, "H5Fopen"); dataset = H5Dopen2(file, "Dataset", H5P_DEFAULT); CHECK(dataset, FAIL, "H5Dopen2"); /* Test invalid indices for starting iteration */ info.command = RET_ZERO; /* Test skipping exactly as many attributes as there are */ idx = NATTR; H5E_BEGIN_TRY { ret = H5Aiterate2(dataset, H5_INDEX_NAME, H5_ITER_INC, &idx, aiter_cb, &info); } H5E_END_TRY VERIFY(ret, FAIL, "H5Aiterate2"); /* Test skipping more attributes than there are */ idx = NATTR + 1; H5E_BEGIN_TRY { ret = H5Aiterate2(dataset, H5_INDEX_NAME, H5_ITER_INC, &idx, aiter_cb, &info); } H5E_END_TRY VERIFY(ret, FAIL, "H5Aiterate2"); /* Test all attributes on dataset, when callback always returns 0 */ info.command = RET_ZERO; idx = 0; if ((ret = H5Aiterate2(dataset, H5_INDEX_NAME, H5_ITER_INC, &idx, aiter_cb, &info)) > 0) TestErrPrintf("Attribute iteration function didn't return zero correctly!\n"); /* Test all attributes on dataset, when callback always returns 2 */ /* This also tests the "restarting" ability, because the index changes */ info.command = RET_TWO; i = 0; idx = 0; while ((ret = H5Aiterate2(dataset, H5_INDEX_NAME, H5_ITER_INC, &idx, aiter_cb, &info)) > 0) { /* Verify return value from iterator gets propagated correctly */ VERIFY(ret, 2, "H5Aiterate2"); /* Increment the number of times "2" is returned */ i++; /* Verify that the index is the correct value */ VERIFY(idx, (unsigned)i, "H5Aiterate2"); /* Don't check name when new format is used */ if (!new_format) { /* Verify that the correct name is retrieved */ if (idx > 0) { if (strcmp(info.name, anames[(size_t)idx - 1]) != 0) TestErrPrintf("%u: Attribute iteration function didn't set names correctly, info.name = " "'%s', anames[%u] = '%s'!\n", __LINE__, info.name, (unsigned)(idx - 1), anames[(size_t)idx - 1]); } else TestErrPrintf("%u: 'idx' was not set correctly!\n", __LINE__); } /* end if */ } /* end while */ VERIFY(ret, -1, "H5Aiterate2"); if (i != 50 || idx != 50) TestErrPrintf("%u: Attribute iteration function didn't perform multiple iterations correctly!\n", __LINE__); /* Test all attributes on dataset, when callback changes return value */ /* This also tests the "restarting" ability, because the index changes */ info.command = new_format ? RET_CHANGE2 : RET_CHANGE; i = 0; idx = 0; while ((ret = H5Aiterate2(dataset, H5_INDEX_NAME, H5_ITER_INC, &idx, aiter_cb, &info)) > 0) { /* Verify return value from iterator gets propagated correctly */ VERIFY(ret, 1, "H5Aiterate2"); /* Increment the number of times "1" is returned */ i++; /* Verify that the index is the correct value */ VERIFY(idx, (unsigned)i + 10, "H5Aiterate2"); /* Don't check name when new format is used */ if (!new_format) { /* Verify that the correct name is retrieved */ if (idx > 0) { if (strcmp(info.name, anames[(size_t)idx - 1]) != 0) TestErrPrintf("%u: Attribute iteration function didn't set names correctly, info.name = " "'%s', anames[%u] = '%s'!\n", __LINE__, info.name, (unsigned)(idx - 1), anames[(size_t)idx - 1]); } else TestErrPrintf("%u: 'idx' was not set correctly!\n", __LINE__); } /* end if */ } /* end while */ VERIFY(ret, -1, "H5Aiterate2"); if (i != 40 || idx != 50) TestErrPrintf("%u: Attribute iteration function didn't perform multiple iterations correctly!\n", __LINE__); ret = H5Fclose(file); CHECK(ret, FAIL, "H5Fclose"); ret = H5Dclose(dataset); CHECK(ret, FAIL, "H5Dclose"); /* Free the attribute names */ for (i = 0; i < NATTR; i++) free(anames[i]); } /* test_iter_attr() */ /**************************************************************** ** ** iter_strcmp2(): String comparison routine for qsort ** ****************************************************************/ H5_ATTR_PURE int iter_strcmp2(const void *s1, const void *s2) { return (strcmp((const char *)s1, (const char *)s2)); } /* end iter_strcmp2() */ /**************************************************************** ** ** liter_cb2(): Custom link iteration callback routine. ** ****************************************************************/ static herr_t liter_cb2(hid_t loc_id, const char *name, const H5L_info2_t H5_ATTR_UNUSED *link_info, void *opdata) { const iter_info *test_info = (const iter_info *)opdata; H5O_info2_t oinfo; herr_t ret; /* Generic return value */ if (!(vol_cap_flags_g & H5VL_CAP_FLAG_ITERATE) || !(vol_cap_flags_g & H5VL_CAP_FLAG_LINK_BASIC)) { SKIPPED(); printf(" API functions for iterate and basic links aren't " "supported with this connector\n"); return 1; } if (strcmp(name, test_info->name) != 0) { TestErrPrintf("name = '%s', test_info = '%s'\n", name, test_info->name); return (H5_ITER_ERROR); } /* end if */ /* * Get type of the object and check it. */ ret = H5Oget_info_by_name3(loc_id, name, &oinfo, H5O_INFO_BASIC, H5P_DEFAULT); CHECK(ret, FAIL, "H5Oget_info_by_name3"); if (test_info->type != oinfo.type) { TestErrPrintf("test_info->type = %d, oinfo.type = %d\n", test_info->type, (int)oinfo.type); return (H5_ITER_ERROR); } /* end if */ return (H5_ITER_STOP); } /* liter_cb2() */ /**************************************************************** ** ** test_iter_group_large(): Test group iteration functionality ** for groups with large #'s of objects ** ****************************************************************/ static void test_iter_group_large(hid_t fapl) { hid_t file; /* HDF5 File IDs */ hid_t dataset; /* Dataset ID */ hid_t group; /* Group ID */ hid_t sid; /* Dataspace ID */ hid_t tid; /* Datatype ID */ hsize_t dims[] = {SPACE1_DIM1}; herr_t ret; /* Generic return value */ char gname[20]; /* Temporary group name */ iter_info *names; /* Names of objects in the root group */ iter_info *curr_name; /* Pointer to the current name in the root group */ int i; /* Compound datatype */ typedef struct s1_t { unsigned int a; unsigned int b; float c; } s1_t; /* Allocate & initialize array */ names = (iter_info *)calloc(sizeof(iter_info), (ITER_NGROUPS + 2)); CHECK_PTR(names, "calloc"); /* Output message about test being performed */ MESSAGE(5, ("Testing Large Group Iteration Functionality\n")); if (!(vol_cap_flags_g & H5VL_CAP_FLAG_FILE_BASIC) || !(vol_cap_flags_g & H5VL_CAP_FLAG_GROUP_BASIC) || !(vol_cap_flags_g & H5VL_CAP_FLAG_DATASET_BASIC) || !(vol_cap_flags_g & H5VL_CAP_FLAG_STORED_DATATYPES) || !(vol_cap_flags_g & H5VL_CAP_FLAG_ITERATE)) { MESSAGE(5, (" -- SKIPPED --\n")); return; } /* Create file */ file = H5Fcreate(DATAFILE, H5F_ACC_TRUNC, H5P_DEFAULT, fapl); CHECK(file, FAIL, "H5Fcreate"); /* Create dataspace for datasets */ sid = H5Screate_simple(SPACE1_RANK, dims, NULL); CHECK(sid, FAIL, "H5Screate_simple"); /* Create a bunch of groups */ for (i = 0; i < ITER_NGROUPS; i++) { snprintf(gname, sizeof(gname), "Group_%d", i); /* Add the name to the list of objects in the root group */ strcpy(names[i].name, gname); names[i].type = H5O_TYPE_GROUP; /* Create a group */ group = H5Gcreate2(file, gname, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); CHECK(group, FAIL, "H5Gcreate2"); /* Close a group */ ret = H5Gclose(group); CHECK(ret, FAIL, "H5Gclose"); } /* end for */ /* Create a dataset */ dataset = H5Dcreate2(file, "Dataset1", H5T_STD_U32LE, sid, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); CHECK(dataset, FAIL, "H5Dcreate2"); /* Add the name to the list of objects in the root group */ strcpy(names[ITER_NGROUPS].name, "Dataset1"); names[ITER_NGROUPS].type = H5O_TYPE_DATASET; /* Close Dataset */ ret = H5Dclose(dataset); CHECK(ret, FAIL, "H5Dclose"); /* Close Dataspace */ ret = H5Sclose(sid); CHECK(ret, FAIL, "H5Sclose"); /* Create a datatype */ tid = H5Tcreate(H5T_COMPOUND, sizeof(s1_t)); CHECK(tid, FAIL, "H5Tcreate"); /* Insert fields */ ret = H5Tinsert(tid, "a", HOFFSET(s1_t, a), H5T_NATIVE_INT); CHECK(ret, FAIL, "H5Tinsert"); ret = H5Tinsert(tid, "b", HOFFSET(s1_t, b), H5T_NATIVE_INT); CHECK(ret, FAIL, "H5Tinsert"); ret = H5Tinsert(tid, "c", HOFFSET(s1_t, c), H5T_NATIVE_FLOAT); CHECK(ret, FAIL, "H5Tinsert"); /* Save datatype for later */ ret = H5Tcommit2(file, "Datatype1", tid, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); CHECK(ret, FAIL, "H5Tcommit2"); /* Add the name to the list of objects in the root group */ strcpy(names[ITER_NGROUPS + 1].name, "Datatype1"); names[ITER_NGROUPS + 1].type = H5O_TYPE_NAMED_DATATYPE; /* Close datatype */ ret = H5Tclose(tid); CHECK(ret, FAIL, "H5Tclose"); /* Need to sort the names in the root group, cause that's what the library does */ qsort(names, (size_t)(ITER_NGROUPS + 2), sizeof(iter_info), iter_strcmp2); /* Iterate through the file to see members of the root group */ curr_name = &names[0]; ret = H5Literate2(file, H5_INDEX_NAME, H5_ITER_INC, NULL, liter_cb2, curr_name); CHECK(ret, FAIL, "H5Literate2"); for (i = 1; i < 100; i++) { hsize_t idx = (hsize_t)i; curr_name = &names[i]; ret = H5Literate2(file, H5_INDEX_NAME, H5_ITER_INC, &idx, liter_cb2, curr_name); CHECK(ret, FAIL, "H5Literate2"); } /* end for */ /* Close file */ ret = H5Fclose(file); CHECK(ret, FAIL, "H5Fclose"); /* Release memory */ free(names); } /* test_iterate_group_large() */ /**************************************************************** ** ** test_grp_memb_funcs(): Test group member information ** functionality ** ****************************************************************/ static void test_grp_memb_funcs(hid_t fapl) { hid_t file; /* File ID */ hid_t dataset; /* Dataset ID */ hid_t datatype; /* Common datatype ID */ hid_t filespace; /* Common dataspace ID */ hid_t root_group, grp; /* Root group ID */ int i; /* counting variable */ char name[NAMELEN]; /* temporary name buffer */ char *dnames[NDATASETS + 2]; /* Names of the datasets created */ char *obj_names[NDATASETS + 2]; /* Names of the objects in group */ char dataset_name[NAMELEN]; /* dataset name */ ssize_t name_len; /* Length of object's name */ H5G_info_t ginfo; /* Buffer for querying object's info */ herr_t ret = SUCCEED; /* Generic return value */ /* Output message about test being performed */ MESSAGE(5, ("Testing Group Member Information Functionality\n")); /* Create the test file with the datasets */ file = H5Fcreate(DATAFILE, H5F_ACC_TRUNC, H5P_DEFAULT, fapl); CHECK(file, FAIL, "H5Fcreate"); datatype = H5Tcopy(H5T_NATIVE_INT); CHECK(datatype, FAIL, "H5Tcopy"); filespace = H5Screate(H5S_SCALAR); CHECK(filespace, FAIL, "H5Screate"); for (i = 0; i < NDATASETS; i++) { snprintf(name, sizeof(name), "Dataset %d", i); dataset = H5Dcreate2(file, name, datatype, filespace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); CHECK(dataset, FAIL, "H5Dcreate2"); /* Keep a copy of the dataset names around for later */ dnames[i] = strdup(name); CHECK_PTR(dnames[i], "strdup"); ret = H5Dclose(dataset); CHECK(ret, FAIL, "H5Dclose"); } /* end for */ /* Create a group and named datatype under root group for testing */ grp = H5Gcreate2(file, "grp", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); CHECK(ret, FAIL, "H5Gcreate2"); dnames[NDATASETS] = strdup("grp"); CHECK_PTR(dnames[NDATASETS], "strdup"); ret = H5Tcommit2(file, "dtype", datatype, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); CHECK(ret, FAIL, "H5Tcommit2"); dnames[NDATASETS + 1] = strdup("dtype"); CHECK_PTR(dnames[NDATASETS], "strdup"); /* Close everything up */ ret = H5Tclose(datatype); CHECK(ret, FAIL, "H5Tclose"); ret = H5Gclose(grp); CHECK(ret, FAIL, "H5Gclose"); ret = H5Sclose(filespace); CHECK(ret, FAIL, "H5Sclose"); ret = H5Fclose(file); CHECK(ret, FAIL, "H5Fclose"); /* Sort the dataset names */ qsort(dnames, (size_t)(NDATASETS + 2), sizeof(char *), iter_strcmp); /* Iterate through the datasets in the root group in various ways */ file = H5Fopen(DATAFILE, H5F_ACC_RDONLY, fapl); CHECK(file, FAIL, "H5Fopen"); /* These two functions, H5Oget_info_by_idx and H5Lget_name_by_idx, actually * iterate through B-tree for group members in internal library design. */ root_group = H5Gopen2(file, "/", H5P_DEFAULT); CHECK(root_group, FAIL, "H5Gopen2"); ret = H5Gget_info(root_group, &ginfo); CHECK(ret, FAIL, "H5Gget_info"); VERIFY(ginfo.nlinks, (NDATASETS + 2), "H5Gget_info"); for (i = 0; i < (int)ginfo.nlinks; i++) { H5O_info2_t oinfo; /* Object info */ /* Test with NULL for name, to query length */ name_len = H5Lget_name_by_idx(root_group, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, NULL, (size_t)NAMELEN, H5P_DEFAULT); CHECK(name_len, FAIL, "H5Lget_name_by_idx"); ret = (herr_t)H5Lget_name_by_idx(root_group, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, dataset_name, (size_t)(name_len + 1), H5P_DEFAULT); CHECK(ret, FAIL, "H5Lget_name_by_idx"); /* Double-check that the length is the same */ VERIFY(ret, name_len, "H5Lget_name_by_idx"); /* Keep a copy of the dataset names around for later */ obj_names[i] = strdup(dataset_name); CHECK_PTR(obj_names[i], "strdup"); ret = H5Oget_info_by_idx3(root_group, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, &oinfo, H5O_INFO_BASIC, H5P_DEFAULT); CHECK(ret, FAIL, "H5Oget_info_by_idx3"); if (!strcmp(dataset_name, "grp")) VERIFY(oinfo.type, H5O_TYPE_GROUP, "H5Lget_name_by_idx"); if (!strcmp(dataset_name, "dtype")) VERIFY(oinfo.type, H5O_TYPE_NAMED_DATATYPE, "H5Lget_name_by_idx"); if (!strncmp(dataset_name, "Dataset", (size_t)7)) VERIFY(oinfo.type, H5O_TYPE_DATASET, "H5Lget_name_by_idx"); } /* end for */ H5E_BEGIN_TRY { ret = (herr_t)H5Lget_name_by_idx(root_group, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)(NDATASETS + 3), dataset_name, (size_t)NAMELEN, H5P_DEFAULT); } H5E_END_TRY VERIFY(ret, FAIL, "H5Lget_name_by_idx"); /* Sort the dataset names */ qsort(obj_names, (size_t)(NDATASETS + 2), sizeof(char *), iter_strcmp); /* Compare object names */ for (i = 0; i < (int)ginfo.nlinks; i++) { ret = strcmp(dnames[i], obj_names[i]); VERIFY(ret, 0, "strcmp"); } /* end for */ ret = H5Gclose(root_group); CHECK(ret, FAIL, "H5Gclose"); ret = H5Fclose(file); CHECK(ret, FAIL, "H5Fclose"); /* Free the dataset names */ for (i = 0; i < (NDATASETS + 2); i++) { free(dnames[i]); free(obj_names[i]); } /* end for */ } /* test_grp_memb_funcs() */ /**************************************************************** ** ** test_links(): Test soft and hard link iteration ** ****************************************************************/ static void test_links(hid_t fapl) { hid_t file; /* File ID */ char obj_name[NAMELEN]; /* Names of the object in group */ ssize_t name_len; /* Length of object's name */ hid_t gid, gid1; H5G_info_t ginfo; /* Buffer for querying object's info */ hsize_t i; herr_t ret; /* Generic return value */ /* Output message about test being performed */ MESSAGE(5, ("Testing Soft and Hard Link Iteration Functionality\n")); /* Create the test file with the datasets */ file = H5Fcreate(DATAFILE, H5F_ACC_TRUNC, H5P_DEFAULT, fapl); CHECK(file, FAIL, "H5Fcreate"); /* create groups */ gid = H5Gcreate2(file, "/g1", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); CHECK(gid, FAIL, "H5Gcreate2"); gid1 = H5Gcreate2(file, "/g1/g1.1", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); CHECK(gid1, FAIL, "H5Gcreate2"); /* create soft and hard links to the group "/g1". */ ret = H5Lcreate_soft("something", gid, "softlink", H5P_DEFAULT, H5P_DEFAULT); CHECK(ret, FAIL, "H5Lcreate_soft"); ret = H5Lcreate_hard(gid, "/g1", H5L_SAME_LOC, "hardlink", H5P_DEFAULT, H5P_DEFAULT); CHECK(ret, FAIL, "H5Lcreate_hard"); ret = H5Gget_info(gid, &ginfo); CHECK(ret, FAIL, "H5Gget_info"); VERIFY(ginfo.nlinks, 3, "H5Gget_info"); /* Test these two functions, H5Oget_info_by_idx and H5Lget_name_by_idx */ for (i = 0; i < ginfo.nlinks; i++) { H5O_info2_t oinfo; /* Object info */ H5L_info2_t linfo; /* Link info */ /* Get link name */ name_len = H5Lget_name_by_idx(gid, ".", H5_INDEX_NAME, H5_ITER_INC, i, obj_name, (size_t)NAMELEN, H5P_DEFAULT); CHECK(name_len, FAIL, "H5Lget_name_by_idx"); /* Get link type */ ret = H5Lget_info_by_idx2(gid, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, &linfo, H5P_DEFAULT); CHECK(ret, FAIL, "H5Lget_info_by_idx2"); /* Get object type */ if (linfo.type == H5L_TYPE_HARD) { ret = H5Oget_info_by_idx3(gid, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, &oinfo, H5O_INFO_BASIC, H5P_DEFAULT); CHECK(ret, FAIL, "H5Oget_info_by_idx3"); } /* end if */ if (!strcmp(obj_name, "g1.1")) VERIFY(oinfo.type, H5O_TYPE_GROUP, "H5Lget_name_by_idx"); else if (!strcmp(obj_name, "hardlink")) VERIFY(oinfo.type, H5O_TYPE_GROUP, "H5Lget_name_by_idx"); else if (!strcmp(obj_name, "softlink")) VERIFY(linfo.type, H5L_TYPE_SOFT, "H5Lget_name_by_idx"); else ERROR("unknown object name"); } /* end for */ ret = H5Gclose(gid); CHECK(ret, FAIL, "H5Gclose"); ret = H5Gclose(gid1); CHECK(ret, FAIL, "H5Gclose"); ret = H5Fclose(file); CHECK(ret, FAIL, "H5Fclose"); } /* test_links() */ /*------------------------------------------------------------------------- * Function: find_err_msg_cb * * Purpose: Callback function to find the given error message. * Helper function for test_corrupted_attnamelen(). * * Return: H5_ITER_STOP when the message is found * H5_ITER_CONT, otherwise * *------------------------------------------------------------------------- */ static int find_err_msg_cb(unsigned H5_ATTR_UNUSED n, const H5E_error2_t *err_desc, void *_client_data) { int status = H5_ITER_CONT; searched_err_t *searched_err = (searched_err_t *)_client_data; if (searched_err == NULL) return H5_ITER_ERROR; /* If the searched error message is found, stop the iteration */ if (err_desc->desc != NULL && strcmp(err_desc->desc, searched_err->message) == 0) { searched_err->found = true; status = H5_ITER_STOP; } return status; } /* end find_err_msg_cb() */ /************************************************************************** ** ** test_corrupted_attnamelen(): Test the fix for the JIRA issue HDFFV-10588, ** where corrupted attribute's name length can be ** detected and invalid read can be avoided. ** **************************************************************************/ static void test_corrupted_attnamelen(void) { hid_t fid = H5I_INVALID_HID; /* File ID */ hid_t did = H5I_INVALID_HID; /* Dataset ID */ searched_err_t err_caught; /* Data to be passed to callback func */ int err_status; /* Status returned by H5Aiterate2 */ herr_t ret; /* Return value */ bool vol_is_native; bool driver_is_default_compatible; const char *testfile = H5_get_srcdir_filename(CORRUPTED_ATNAMELEN_FILE); /* Corrected test file name */ /* The error message produced when the failure occurs * * FIXME: This is incredibly fragile! */ const char *err_message = "ran off end of input buffer while decoding"; /* Output message about test being performed */ MESSAGE(5, ("Testing the Handling of Corrupted Attribute's Name Length\n")); /* Check if native VOL is being used */ CHECK(h5_using_native_vol(H5P_DEFAULT, H5I_INVALID_HID, &vol_is_native), FAIL, "h5_using_native_vol"); if (!vol_is_native) { MESSAGE(5, (" -- SKIPPED --\n")); return; } ret = h5_driver_is_default_vfd_compatible(H5P_DEFAULT, &driver_is_default_compatible); CHECK(ret, FAIL, "h5_driver_is_default_vfd_compatible"); if (!driver_is_default_compatible) { MESSAGE(5, (" -- SKIPPED --\n")); return; } fid = H5Fopen(testfile, H5F_ACC_RDONLY, H5P_DEFAULT); CHECK(fid, FAIL, "H5Fopen"); /* Open the dataset */ did = H5Dopen2(fid, DSET_NAME, H5P_DEFAULT); CHECK(did, FAIL, "H5Dopen2"); /* Call H5Aiterate2 to trigger the failure in HDFFV-10588. Failure should occur in the decoding stage, so some arguments are not needed. */ err_status = H5Aiterate2(did, H5_INDEX_NAME, H5_ITER_INC, NULL, NULL, NULL); VERIFY(err_status, FAIL, "H5Aiterate2"); /* Make sure the intended error was caught */ if (err_status == -1) { /* Initialize client data */ strcpy(err_caught.message, err_message); err_caught.found = false; /* Look for the correct error message */ ret = H5Ewalk2(H5E_DEFAULT, H5E_WALK_UPWARD, find_err_msg_cb, &err_caught); CHECK(ret, FAIL, "H5Ewalk2"); /* Fail if the indicated message is not found */ CHECK(err_caught.found, false, "test_corrupted_attnamelen: Expected error not found"); } /* Close the dataset and file */ ret = H5Dclose(did); CHECK(ret, FAIL, "H5Dclose"); ret = H5Fclose(fid); CHECK(ret, FAIL, "H5Fclose"); } /* test_corrupted_attnamelen() */ #ifndef H5_NO_DEPRECATED_SYMBOLS /**************************************************************** ** ** test_links_deprec(): Test soft and hard link iteration ** ****************************************************************/ static void test_links_deprec(hid_t fapl) { hid_t file; /* File ID */ char obj_name[NAMELEN]; /* Names of the object in group */ ssize_t name_len; /* Length of object's name */ hid_t gid, gid1; H5G_info_t ginfo; /* Buffer for querying object's info */ hsize_t i; bool vol_is_native; herr_t ret; /* Generic return value */ /* Output message about test being performed */ MESSAGE(5, ("Testing Soft and Hard Link Iteration Functionality Using Deprecated Routines\n")); /* Create the test file with the datasets */ file = H5Fcreate(DATAFILE, H5F_ACC_TRUNC, H5P_DEFAULT, fapl); CHECK(file, FAIL, "H5Fcreate"); /* Check if native VOL is being used */ CHECK(h5_using_native_vol(fapl, file, &vol_is_native), FAIL, "h5_using_native_vol"); if (!vol_is_native) { CHECK(H5Fclose(file), FAIL, "H5Fclose"); MESSAGE(5, (" -- SKIPPED --\n")); return; } /* create groups */ gid = H5Gcreate2(file, "/g1", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); CHECK(gid, FAIL, "H5Gcreate2"); gid1 = H5Gcreate2(file, "/g1/g1.1", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); CHECK(gid1, FAIL, "H5Gcreate2"); /* create soft and hard links to the group "/g1". */ ret = H5Lcreate_soft("something", gid, "softlink", H5P_DEFAULT, H5P_DEFAULT); CHECK(ret, FAIL, "H5Lcreate_soft"); ret = H5Lcreate_hard(gid, "/g1", H5L_SAME_LOC, "hardlink", H5P_DEFAULT, H5P_DEFAULT); CHECK(ret, FAIL, "H5Lcreate_hard"); ret = H5Gget_info(gid, &ginfo); CHECK(ret, FAIL, "H5Gget_info"); VERIFY(ginfo.nlinks, 3, "H5Gget_info"); /* Test these two functions, H5Oget_info_by_idx and H5Lget_name_by_idx */ for (i = 0; i < ginfo.nlinks; i++) { H5O_info2_t oinfo; /* Object info */ H5L_info2_t linfo; /* Link info */ /* Get link name */ name_len = H5Lget_name_by_idx(gid, ".", H5_INDEX_NAME, H5_ITER_INC, i, obj_name, (size_t)NAMELEN, H5P_DEFAULT); CHECK(name_len, FAIL, "H5Lget_name_by_idx"); /* Get link type */ ret = H5Lget_info_by_idx2(gid, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, &linfo, H5P_DEFAULT); CHECK(ret, FAIL, "H5Lget_info_by_idx1"); /* Get object type */ if (linfo.type == H5L_TYPE_HARD) { ret = H5Oget_info_by_idx3(gid, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, &oinfo, H5O_INFO_BASIC, H5P_DEFAULT); CHECK(ret, FAIL, "H5Oget_info_by_idx"); } /* end if */ if (!strcmp(obj_name, "g1.1")) VERIFY(oinfo.type, H5O_TYPE_GROUP, "H5Lget_name_by_idx"); else if (!strcmp(obj_name, "hardlink")) VERIFY(oinfo.type, H5O_TYPE_GROUP, "H5Lget_name_by_idx"); else if (!strcmp(obj_name, "softlink")) VERIFY(linfo.type, H5L_TYPE_SOFT, "H5Lget_name_by_idx"); else ERROR("unknown object name"); } /* end for */ ret = H5Gclose(gid); CHECK(ret, FAIL, "H5Gclose"); ret = H5Gclose(gid1); CHECK(ret, FAIL, "H5Gclose"); ret = H5Fclose(file); CHECK(ret, FAIL, "H5Fclose"); } /* test_links_deprec() */ #endif /**************************************************************** ** ** test_iterate(): Main iteration testing routine. ** ****************************************************************/ void test_iterate(void) { hid_t fapl, fapl2; /* File access property lists */ unsigned new_format; /* Whether to use the new format or not */ herr_t ret; /* Generic return value */ /* Output message about test being performed */ MESSAGE(5, ("Testing Iteration Operations\n")); /* Get the default FAPL */ fapl = H5Pcreate(H5P_FILE_ACCESS); CHECK(fapl, FAIL, "H5Pcreate"); /* Copy the file access property list */ fapl2 = H5Pcopy(fapl); CHECK(fapl2, FAIL, "H5Pcopy"); /* Set the "use the latest version of the format" bounds for creating objects in the file */ ret = H5Pset_libver_bounds(fapl2, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST); CHECK(ret, FAIL, "H5Pset_libver_bounds"); /* These next tests use the same file */ for (new_format = false; new_format <= true; new_format++) { test_iter_group(new_format ? fapl2 : fapl, new_format); /* Test group iteration */ test_iter_group_large(new_format ? fapl2 : fapl); /* Test group iteration for large # of objects */ test_iter_attr(new_format ? fapl2 : fapl, new_format); /* Test attribute iteration */ test_grp_memb_funcs(new_format ? fapl2 : fapl); /* Test group member information functions */ test_links(new_format ? fapl2 : fapl); /* Test soft and hard link iteration */ #ifndef H5_NO_DEPRECATED_SYMBOLS test_links_deprec(new_format ? fapl2 : fapl); /* Test soft and hard link iteration */ #endif } /* end for */ /* Test the fix for issue HDFFV-10588 */ test_corrupted_attnamelen(); /* Close FAPLs */ ret = H5Pclose(fapl); CHECK(ret, FAIL, "H5Pclose"); ret = H5Pclose(fapl2); CHECK(ret, FAIL, "H5Pclose"); } /* test_iterate() */ /*------------------------------------------------------------------------- * Function: cleanup_iterate * * Purpose: Cleanup temporary test files * * Return: none * *------------------------------------------------------------------------- */ void cleanup_iterate(void) { H5E_BEGIN_TRY { H5Fdelete(DATAFILE, H5P_DEFAULT); } H5E_END_TRY }