Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed H5Ovisit2() change of behavior between 1.10.11 and v1.14.4.3 #5022

Merged
merged 16 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions release_docs/RELEASE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,26 @@ Bug Fixes since HDF5-2.0.0 release
would cause those routines to return FAIL instead of FALSE when checking
the existence of a non-existent object with a file ID instead of a
group ID.

- Fixed a segfault in h5dump when a B-tree node level is corrupted

h5dump produced a segfault on a mal-formed file because a B-tree node
level was corrupted.

An internal function was modified to help detecting when a decoded B-tree
node level has an unexpected value and an error will be produced.

Fixes GitHub issue #4432

- Fixed H5Ovisit2 to recursively visit all objects

H5Ovisit2 visited only the root group and not all the nested groups.

This behavior occurred when the fields are not H5O_INFO_BASIC or
H5O_INFO_ALL because an internal function did not obtain the basic
information needed by its caller. This problem is now fixed.

Fixes GitHub issue #4941

- Only clear FE_INVALID when that symbol is present on the system

Expand Down
20 changes: 15 additions & 5 deletions src/H5Oint.c
Original file line number Diff line number Diff line change
Expand Up @@ -2647,7 +2647,9 @@ H5O__visit(H5G_loc_t *loc, const char *obj_name, H5_index_t idx_type, H5_iter_or
H5O_loc_t obj_oloc; /* Opened object object location */
bool loc_found = false; /* Entry at 'name' found */
H5O_info2_t oinfo; /* Object info struct */
void *obj = NULL; /* Object */
H5O_info2_t int_oinfo; /* Internal object info */
H5O_info2_t *oinfop = &oinfo; /* Object info pointer */
void *obj = NULL; /* Object */
H5I_type_t opened_type; /* ID type of object */
hid_t obj_id = H5I_INVALID_HID; /* ID of object */
herr_t ret_value = FAIL; /* Return value */
Expand All @@ -2674,6 +2676,14 @@ H5O__visit(H5G_loc_t *loc, const char *obj_name, H5_index_t idx_type, H5_iter_or
if (H5O_get_info(&obj_oloc, &oinfo, fields) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "unable to get object info");

/* If basic fields are not requested, get object basic info to use here */
if (!(fields & H5O_INFO_BASIC)) {
oinfop = &int_oinfo;
/* Get the object's basic info for local use */
if (H5O_get_info(&obj_oloc, &int_oinfo, H5O_INFO_BASIC) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "unable to get object's basic info");
}

/* Open the object */
/* (Takes ownership of the obj_loc information) */
if (NULL == (obj = H5O_open_by_loc(&obj_loc, &opened_type)))
Expand All @@ -2692,7 +2702,7 @@ H5O__visit(H5G_loc_t *loc, const char *obj_name, H5_index_t idx_type, H5_iter_or
HGOTO_DONE(ret_value);

/* Check for object being a group */
if (oinfo.type == H5O_TYPE_GROUP) {
if (oinfop->type == H5O_TYPE_GROUP) {
H5G_loc_t start_loc; /* Location of starting group */
H5G_loc_t vis_loc; /* Location of visited group */

Expand All @@ -2713,18 +2723,18 @@ H5O__visit(H5G_loc_t *loc, const char *obj_name, H5_index_t idx_type, H5_iter_or

/* If its ref count is > 1, we add it to the list of visited objects */
/* (because it could come up again during traversal) */
if (oinfo.rc > 1) {
if (oinfop->rc > 1) {
H5_obj_t *obj_pos; /* New object node for visited list */

/* Allocate new object "position" node */
if ((obj_pos = H5FL_MALLOC(H5_obj_t)) == NULL)
HGOTO_ERROR(H5E_OHDR, H5E_NOSPACE, FAIL, "can't allocate object node");

/* Construct unique "position" for this object */
obj_pos->fileno = oinfo.fileno;
obj_pos->fileno = oinfop->fileno;

/* De-serialize object token into an object address */
if (H5VL_native_token_to_addr(loc->oloc->file, H5I_FILE, oinfo.token, &(obj_pos->addr)) < 0)
if (H5VL_native_token_to_addr(loc->oloc->file, H5I_FILE, oinfop->token, &(obj_pos->addr)) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTUNSERIALIZE, FAIL,
"can't deserialize object token into address");

Expand Down
163 changes: 163 additions & 0 deletions test/th5o.c
Original file line number Diff line number Diff line change
Expand Up @@ -1879,6 +1879,164 @@ test_h5o_getinfo_visit(void)

} /* test_h5o_getinfo_visit() */

#define G1 "g1" /* Group /g1 */
#define G1G2 "g1/g2" /* Group /g1/g2 */
#define ATTR1 "Attr1" /* Attribute Attr1 */
#define D1G1G2 "/g1/g2/dset1" /* Dataset /g1/g2/dset1 */
#define NUM_OBJS 4 /* Number of objects including root group */
#define NUM_ATTRS 1 /* Number of attributes belong to root group */
#define VISIT2_FILENAME "th5o_visit2"

typedef struct {
unsigned idx; /* Index in object visit structure */
unsigned fields; /* Fields to verify number of attributes in callback */
} ovisit2_ud_t;

/* Names of objects being visited, in this order */
static const char *visited_objs[] = {".", "g1", "g1/g2", "g1/g2/dset1"};

/****************************************************************
**
** visit_obj_cb():
** This is the callback function invoked by H5Ovisit1() in
** test_h5o_getinfo_visit():
** --Verify that the object info returned to the callback
** function is the same as H5Oget_info2().
**
****************************************************************/
static int
visit2_obj_cb(hid_t obj_id, const char *name, const H5O_info1_t H5_ATTR_UNUSED *info, void *_op_data)
{
H5O_info1_t oinfo;

memset(&oinfo, 0, sizeof(oinfo));
ovisit2_ud_t *op_data = (ovisit2_ud_t *)_op_data;

if (strcmp(visited_objs[op_data->idx], name) != 0)
return H5_ITER_ERROR;

if (H5Oget_info2(obj_id, &oinfo, op_data->fields) < 0)
return H5_ITER_ERROR;

if (op_data->fields == H5O_INFO_NUM_ATTRS)
if (oinfo.num_attrs != NUM_ATTRS)
return H5_ITER_ERROR;

/* Advance to next location in expected output */
op_data->idx++;

return H5_ITER_CONT;
}

/****************************************************************
**
** test_h5o_visit2():
** Verify that H5Ovisit2 visits the nested groups and objects
** instead of only root group.
**
****************************************************************/
static void
test_h5o_visit2(void)
{
hid_t fid = H5I_INVALID_HID; /* HDF5 File ID */
hid_t gid1 = H5I_INVALID_HID, gid2 = H5I_INVALID_HID; /* Group IDs */
hid_t sid = H5I_INVALID_HID; /* Dataspace ID */
hid_t did = H5I_INVALID_HID; /* Dataset ID */
hid_t attid = H5I_INVALID_HID; /* Attribute ID */
hid_t obj_id; /* Object ID for root group */
char filename[1024]; /* File used in this test */
ovisit2_ud_t udata; /* User-data for visiting */
unsigned fields; /* Fields to return */
H5_index_t idx_type = H5_INDEX_NAME;
H5_iter_order_t order = H5_ITER_DEC;
bool vol_is_native;
herr_t ret; /* Value returned from API calls */

/* Output message about test being performed */
MESSAGE(5, ("Testing H5Ovisit2 visits all nested groups and objects\n"));

h5_fixname(VISIT2_FILENAME, H5P_DEFAULT, filename, sizeof filename);

/* Create an HDF5 file */
fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
CHECK(fid, FAIL, "H5Fcreate");

/* Check if native VOL is being used */
CHECK(h5_using_native_vol(H5P_DEFAULT, fid, &vol_is_native), FAIL, "h5_using_native_vol");
if (!vol_is_native) {
CHECK(H5Fclose(fid), FAIL, "H5Fclose");
MESSAGE(5, (" -- SKIPPED --\n"));
return;
}

/* Create group G1 in the file */
gid1 = H5Gcreate2(fid, G1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
CHECK(gid1, FAIL, "H5Gcreate2");

/* Create group G1G2 in the file */
gid2 = H5Gcreate2(fid, G1G2, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
CHECK(gid2, FAIL, "H5Gcreate2");

/* Create dataspace */
sid = H5Screate(H5S_SCALAR);
CHECK(sid, FAIL, "H5Screate");

did = H5Dcreate2(gid2, D1G1G2, H5T_NATIVE_INT, sid, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
CHECK(did, FAIL, "H5Dcreate2");

/* Add an attribute to verify H5O_get_info with H5O_INFO_NUM_ATTRS works */
attid = H5Acreate2(fid, ATTR1, H5T_NATIVE_INT, sid, H5P_DEFAULT, H5P_DEFAULT);
CHECK(attid, FAIL, "H5Acreate2");

H5Aclose(attid);
H5Dclose(did);
H5Gclose(gid2);
H5Gclose(gid1);

/* Open root object */
obj_id = H5Oopen(fid, "/", H5P_DEFAULT);
CHECK(obj_id, FAIL, "H5Oopen");

/* Visit root with H5O_INFO_META_SIZE */
udata.idx = 0; /* first object, i.e., root group */
fields = udata.fields = H5O_INFO_META_SIZE;
ret = H5Ovisit2(obj_id, idx_type, order, visit2_obj_cb, &udata, fields);
CHECK(ret, FAIL, "H5Ovisit2");

/* Verify that all objects were visitted */
VERIFY(udata.idx, NUM_OBJS, "idx should be the same as NUM_OBJS");

/* Visit root with H5O_INFO_BASIC */
udata.idx = 0; /* first object, i.e., root group */
fields = udata.fields = H5O_INFO_BASIC;
ret = H5Ovisit2(obj_id, idx_type, order, visit2_obj_cb, &udata, fields);
CHECK(ret, FAIL, "H5Ovisit2");

/* Verify that all objects were visitted */
VERIFY(udata.idx, NUM_OBJS, "idx should be the same as NUM_OBJS");

/* Visit root with H5O_INFO_ALL */
udata.idx = 0; /* first object, i.e., root group */
fields = udata.fields = H5O_INFO_ALL;
ret = H5Ovisit2(obj_id, idx_type, order, visit2_obj_cb, &udata, fields);
CHECK(ret, FAIL, "H5Ovisit2");

/* Visit root with H5O_INFO_NUM_ATTRS */
udata.idx = 0; /* first object, i.e., root group */
fields = udata.fields = H5O_INFO_NUM_ATTRS;
ret = H5Ovisit2(obj_id, idx_type, order, visit2_obj_cb, &udata, fields);
CHECK(ret, FAIL, "H5Ovisit2");

/* Verify that all objects were visitted */
VERIFY(udata.idx, NUM_OBJS, "idx should be the same as NUM_OBJS");

/* Close object and file */
ret = H5Oclose(obj_id);
CHECK(ret, FAIL, "H5Oclose");
ret = H5Fclose(fid);
CHECK(ret, FAIL, "H5Fclose");
}

#endif /* H5_NO_DEPRECATED_SYMBOLS */

/****************************************************************
Expand Down Expand Up @@ -1907,6 +2065,7 @@ test_h5o(const void H5_ATTR_UNUSED *params)
#ifndef H5_NO_DEPRECATED_SYMBOLS
test_h5o_open_by_addr_deprec(); /* Test opening objects by address with H5Lget_info1 */
test_h5o_getinfo_visit(); /* Test object info for H5Oget_info1/2 and H5Ovisit1 */
test_h5o_visit2(); /* Test behavior of H5Ovisit2 */
#endif /* H5_NO_DEPRECATED_SYMBOLS */
} /* test_h5o() */

Expand All @@ -1929,6 +2088,10 @@ cleanup_h5o(void H5_ATTR_UNUSED *params)
{
h5_fixname(TEST_FILENAME, H5P_DEFAULT, filename, sizeof filename);
H5Fdelete(filename, H5P_DEFAULT);
#ifndef H5_NO_DEPRECATED_SYMBOLS
h5_fixname(VISIT2_FILENAME, H5P_DEFAULT, filename, sizeof filename);
H5Fdelete(filename, H5P_DEFAULT);
Copy link
Member

@derobins derobins Nov 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this duplicated? Should this second statement be VISIT2_FILENAME?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

#endif /* H5_NO_DEPRECATED_SYMBOLS */
}
H5E_END_TRY
}
Expand Down