From fa133cfb95c052f3b96b0a5089b98491b77628ef Mon Sep 17 00:00:00 2001 From: Quincey Koziol Date: Thu, 24 May 2007 13:36:53 -0500 Subject: [svn-r13808] Description: Fix possible file corruption when using "new" format object headers and the size of chunk #0 for an object header transitions between needing 1->2->4->8- byte encoding for the size and there are "clean" messages in the object header already. (Usually triggered by flushing the file while adding attributes to an object) Tested on: Mac OS X/32 10.4.9 (amazon) Linux/32 2.6 (chicago) Linux/64 2.6 (chicago2) --- src/H5O.c | 24 ++-- src/H5Oalloc.c | 6 +- test/ohdr.c | 424 +++++++++++++++++++++++++++++++-------------------------- 3 files changed, 253 insertions(+), 201 deletions(-) diff --git a/src/H5O.c b/src/H5O.c index 0001529..9f66642 100644 --- a/src/H5O.c +++ b/src/H5O.c @@ -720,7 +720,7 @@ H5O_create(H5F_t *f, hid_t dxpl_id, size_t size_hint, hid_t ocpl_id, /* Allocate disk space for header and first chunk */ if(HADDR_UNDEF == (oh_addr = H5MF_alloc(f, H5FD_MEM_OHDR, dxpl_id, (hsize_t)oh_size))) - HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "file allocation failed for object header header") + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "file allocation failed for object header") /* Create the chunk list */ oh->nchunks = oh->alloc_nchunks = 1; @@ -1511,23 +1511,27 @@ static herr_t H5O_obj_type_real(H5O_t *oh, H5O_type_t *obj_type) { const H5O_obj_class_t *obj_class; /* Class of object for header */ - herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_NOAPI_NOINIT(H5O_obj_type_real) + FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5O_obj_type_real) /* Sanity check */ HDassert(oh); HDassert(obj_type); /* Look up class for object header */ - if(NULL == (obj_class = H5O_obj_class_real(oh))) - HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "unable to determine object type") + if(NULL == (obj_class = H5O_obj_class_real(oh))) { + /* Clear error stack from "failed" class lookup */ + H5E_clear_stack(NULL); - /* Set object type */ - *obj_type = obj_class->type; + /* Set type to "unknown" */ + *obj_type = H5O_TYPE_UNKNOWN; + } /* end if */ + else { + /* Set object type */ + *obj_type = obj_class->type; + } /* end else */ -done: - FUNC_LEAVE_NOAPI(ret_value) + FUNC_LEAVE_NOAPI(SUCCEED) } /* end H5O_obj_type_real() */ @@ -1927,7 +1931,7 @@ H5O_get_info(H5O_loc_t *oloc, H5O_info_t *oinfo, hid_t dxpl_id) /* Iterate over all the chunks, adding any gaps to the free space */ oinfo->hdr.space.total = 0; for(u = 0, curr_chunk = &oh->chunk[0]; u < oh->nchunks; u++, curr_chunk++) { - /* Accumulate the size of the header on header */ + /* Accumulate the size of the header on disk */ oinfo->hdr.space.total += curr_chunk->size; /* If the chunk has a gap, add it to the free space */ diff --git a/src/H5Oalloc.c b/src/H5Oalloc.c index 6badfb7..ae07294 100644 --- a/src/H5Oalloc.c +++ b/src/H5Oalloc.c @@ -594,9 +594,13 @@ H5O_alloc_extend_chunk(H5F_t *f, /* Spin through existing messages, adjusting them */ for(u = 0; u < oh->nmesgs; u++) { /* Adjust raw addresses for messages in this chunk to reflect new 'image' address */ - if(oh->mesg[u].chunkno == chunkno) + if(oh->mesg[u].chunkno == chunkno) { oh->mesg[u].raw = oh->chunk[chunkno].image + extra_prfx_size + (oh->mesg[u].raw - old_image); + /* Flag message as dirty */ + oh->mesg[u].dirty = TRUE; + } /* endif */ + /* Find continuation message which points to this chunk and adjust chunk's size */ /* (Chunk 0 doesn't have a continuation message that points to it and * it's size is directly encoded in the object header) */ diff --git a/test/ohdr.c b/test/ohdr.c index 73ede43..d4b6ea4 100644 --- a/test/ohdr.c +++ b/test/ohdr.c @@ -67,240 +67,284 @@ main(void) hid_t dset=-1; H5F_t *f=NULL; char filename[1024]; + H5O_info_t oinfo; /* Object info */ H5O_loc_t oh_loc; time_t time_new, ro; int i; + hbool_t b; /* Index for "new format" loop */ const char *envval = NULL; /* Reset library */ h5_reset(); fapl = h5_fileaccess(); h5_fixname(FILENAME[0], fapl, filename, sizeof filename); - if((file = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0) TEST_ERROR - if(NULL == (f = H5I_object(file))) FAIL_STACK_ERROR - - - /* - * Test object header creation - * (using default group creation property list only because it's convenient) - */ - TESTING("object header creation"); - HDmemset(&oh_loc, 0, sizeof(oh_loc)); - if(H5O_create(f, H5P_DATASET_XFER_DEFAULT, (size_t)64, H5P_GROUP_CREATE_DEFAULT, &oh_loc/*out*/) < 0) - FAIL_STACK_ERROR - PASSED(); - - - /* create a new message */ - TESTING("message creation"); - time_new = 11111111; - if(H5O_msg_create(&oh_loc, H5O_MTIME_NEW_ID, 0, 0, &time_new, H5P_DATASET_XFER_DEFAULT) < 0) - FAIL_STACK_ERROR - if(H5AC_flush(f, H5P_DATASET_XFER_DEFAULT, TRUE) < 0) - FAIL_STACK_ERROR - if(NULL == H5O_msg_read(&oh_loc, H5O_MTIME_NEW_ID, &ro, H5P_DATASET_XFER_DEFAULT)) - FAIL_STACK_ERROR - if(ro != time_new) - TEST_ERROR - PASSED(); - - - /* - * Test modification of an existing message. - */ - TESTING("message modification"); - time_new = 33333333; - if(H5O_msg_write(&oh_loc, H5O_MTIME_NEW_ID, 0, 0, &time_new, H5P_DATASET_XFER_DEFAULT) < 0) - FAIL_STACK_ERROR - if(H5AC_flush(f, H5P_DATASET_XFER_DEFAULT, TRUE) < 0) - FAIL_STACK_ERROR - if(NULL == H5O_msg_read(&oh_loc, H5O_MTIME_NEW_ID, &ro, H5P_DATASET_XFER_DEFAULT)) - FAIL_STACK_ERROR - if(ro != time_new) - TEST_ERROR - PASSED(); - - - /* - * Test creation of a bunch of messages one after another to see - * what happens when the object header overflows in core. - * (Use 'old' MTIME message here, because it is large enough to be - * replaced with a continuation message (the new one is too small) - * and the library doesn't understand how to migrate more than one - * message from an object header currently - QAK - 10/8/03) - */ - TESTING("object header overflow in memory"); - for(i = 0; i < 40; i++) { - time_new = (i + 1) * 1000 + 1; - if(H5O_msg_create(&oh_loc, H5O_MTIME_ID, 0, 0, &time_new, H5P_DATASET_XFER_DEFAULT) < 0) + + /* Loop over old & new formats */ + for(b = FALSE; b <= TRUE; b++) { + /* Display info about testing */ + if(b) + HDputs("Using new file format"); + else + HDputs("Using default file format"); + + /* Set the format to use for the file */ + if (H5Pset_latest_format(fapl, b) < 0) FAIL_STACK_ERROR + + /* Create the file to operate on */ + if((file = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0) TEST_ERROR + if(NULL == (f = H5I_object(file))) FAIL_STACK_ERROR + + + /* + * Test object header creation + * (using default group creation property list only because it's convenient) + */ + TESTING("object header creation"); + HDmemset(&oh_loc, 0, sizeof(oh_loc)); + if(H5O_create(f, H5P_DATASET_XFER_DEFAULT, (size_t)64, H5P_GROUP_CREATE_DEFAULT, &oh_loc/*out*/) < 0) FAIL_STACK_ERROR - } /* end for */ - if(H5AC_flush(f, H5P_DATASET_XFER_DEFAULT, TRUE) < 0) - FAIL_STACK_ERROR - PASSED(); - - - /* - * Test creation of a bunch of messages one after another to see - * what happens when the object header overflows on disk. - */ - TESTING("object header overflow on disk"); - for(i = 0; i < 10; i++) { - time_new = (i + 1) * 1000 + 10; + PASSED(); + + + /* create a new message */ + TESTING("message creation"); + time_new = 11111111; if(H5O_msg_create(&oh_loc, H5O_MTIME_NEW_ID, 0, 0, &time_new, H5P_DATASET_XFER_DEFAULT) < 0) FAIL_STACK_ERROR if(H5AC_flush(f, H5P_DATASET_XFER_DEFAULT, TRUE) < 0) FAIL_STACK_ERROR - } /* end for */ - PASSED(); - - - /* - * Delete all time messages. - */ - TESTING("message deletion"); - if(H5O_msg_remove(&oh_loc, H5O_MTIME_NEW_ID, H5O_ALL, TRUE, H5P_DATASET_XFER_DEFAULT) < 0) - FAIL_STACK_ERROR - if(H5O_msg_remove(&oh_loc, H5O_MTIME_ID, H5O_ALL, TRUE, H5P_DATASET_XFER_DEFAULT) < 0) - FAIL_STACK_ERROR - if(H5O_msg_read(&oh_loc, H5O_MTIME_NEW_ID, &ro, H5P_DATASET_XFER_DEFAULT)) - FAIL_STACK_ERROR - if(H5O_msg_read(&oh_loc, H5O_MTIME_ID, &ro, H5P_DATASET_XFER_DEFAULT)) - FAIL_STACK_ERROR - PASSED(); - - - /* release resources */ - TESTING("object header closing"); - if(H5O_close(&oh_loc) < 0) - FAIL_STACK_ERROR - PASSED(); - - - /* Test reading datasets with undefined object header messages */ - puts("Reading objects with unknown header messages"); - envval = HDgetenv("HDF5_DRIVER"); - if(envval == NULL) - envval = "nomatch"; - if(HDstrcmp(envval, "core") && HDstrcmp(envval, "multi") && HDstrcmp(envval, "split") && HDstrcmp(envval, "family")) { - hid_t file2; /* File ID for 'bogus' object file */ - char testpath[512] = ""; - char testfile[512] = ""; - char *srcdir = HDgetenv("srcdir"); - - /* Build path to all test files */ - if(srcdir && ((HDstrlen(srcdir) + 2) < sizeof(testpath))) { - HDstrcpy(testpath, srcdir); - HDstrcat(testpath, "/"); - } /* end if */ - - /* Build path to test file */ - if(srcdir && ((HDstrlen(testpath) + HDstrlen(FILE_BOGUS) + 1) < sizeof(testfile))) - HDstrcpy(testfile, testpath); - HDstrcat(testfile, FILE_BOGUS); + if(NULL == H5O_msg_read(&oh_loc, H5O_MTIME_NEW_ID, &ro, H5P_DATASET_XFER_DEFAULT)) + FAIL_STACK_ERROR + if(ro != time_new) + TEST_ERROR + PASSED(); - TESTING("object with unknown header message and no flags set"); - /* Open the file with objects that have unknown header messages (generated with gen_bogus.c) */ - if((file2 = H5Fopen(testfile, H5F_ACC_RDONLY, fapl)) < 0) + /* + * Test modification of an existing message. + */ + TESTING("message modification"); + time_new = 33333333; + if(H5O_msg_write(&oh_loc, H5O_MTIME_NEW_ID, 0, 0, &time_new, H5P_DATASET_XFER_DEFAULT) < 0) + FAIL_STACK_ERROR + if(H5AC_flush(f, H5P_DATASET_XFER_DEFAULT, TRUE) < 0) + FAIL_STACK_ERROR + if(NULL == H5O_msg_read(&oh_loc, H5O_MTIME_NEW_ID, &ro, H5P_DATASET_XFER_DEFAULT)) + FAIL_STACK_ERROR + if(ro != time_new) TEST_ERROR - /* Open the dataset with the unknown header message, but no extra flags */ - if((dset = H5Dopen(file2, "/Dataset1")) < 0) - TEST_ERROR - if(H5Dclose(dset) < 0) + /* Make certain that chunk #0 in the object header can be encoded with a 1-byte size */ + if(H5O_get_info(&oh_loc, &oinfo, H5P_DATASET_XFER_DEFAULT) < 0) + FAIL_STACK_ERROR + if(oinfo.hdr.space.total >=256) TEST_ERROR PASSED(); - TESTING("object with unknown header message & 'fail if unknown' flag set"); + /* + * Test creation of a bunch of messages one after another to see + * what happens when the object header overflows in core. + * (Use 'old' MTIME message here, because it is large enough to be + * replaced with a continuation message (the new one is too small) + * and the library doesn't understand how to migrate more than one + * message from an object header currently - QAK - 10/8/03) + */ + TESTING("object header overflow in memory"); + for(i = 0; i < 40; i++) { + time_new = (i + 1) * 1000 + 1; + if(H5O_msg_create(&oh_loc, H5O_MTIME_ID, 0, 0, &time_new, H5P_DATASET_XFER_DEFAULT) < 0) + FAIL_STACK_ERROR + } /* end for */ + if(H5AC_flush(f, H5P_DATASET_XFER_DEFAULT, TRUE) < 0) + FAIL_STACK_ERROR - /* Attempt to open the dataset with the unknown header message, and "fail if unknown" flag */ - H5E_BEGIN_TRY { - dset = H5Dopen(file2, "/Dataset2"); - } H5E_END_TRY; - if(dset >= 0) { - H5Dclose(dset); + /* Make certain that chunk #0 in the object header will be encoded with a 2-byte size */ + if(H5O_get_info(&oh_loc, &oinfo, H5P_DATASET_XFER_DEFAULT) < 0) + FAIL_STACK_ERROR + if(oinfo.hdr.space.total < 256) TEST_ERROR - } /* end if */ PASSED(); - TESTING("object with unknown header message & 'mark if unknown' flag set"); - - /* Copy object with "mark if unknown" flag on message into file that can be modified */ - if(H5Ocopy(file2, "/Dataset3", file, "/Dataset3", H5P_DEFAULT, H5P_DEFAULT) < 0) - TEST_ERROR - - /* Close the file we created (to flush changes to file) */ + /* Close & re-open file & object header */ + /* (makes certain that an object header in the new format that transitions + * between 1-byte chunk #0 size encoding and 2-byte chunk #0 size encoding + * works correctly - QAK) + */ + if(H5O_close(&oh_loc) < 0) + FAIL_STACK_ERROR if(H5Fclose(file) < 0) - TEST_ERROR + FAIL_STACK_ERROR + if((file = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0) + FAIL_STACK_ERROR + if(NULL == (f = H5I_object(file))) + FAIL_STACK_ERROR + oh_loc.file = f; + if(H5O_open(&oh_loc) < 0) + FAIL_STACK_ERROR - /* Re-open the file created, with read-only permissions */ - if((file = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0) - TEST_ERROR + /* + * Test creation of a bunch of messages one after another to see + * what happens when the object header overflows on disk. + */ + TESTING("object header overflow on disk"); + for(i = 0; i < 10; i++) { + time_new = (i + 1) * 1000 + 10; + if(H5O_msg_create(&oh_loc, H5O_MTIME_NEW_ID, 0, 0, &time_new, H5P_DATASET_XFER_DEFAULT) < 0) + FAIL_STACK_ERROR + if(H5AC_flush(f, H5P_DATASET_XFER_DEFAULT, TRUE) < 0) + FAIL_STACK_ERROR + } /* end for */ + PASSED(); - /* Open the dataset with the "mark if unknown" message */ - if((dset = H5Dopen(file, "/Dataset3")) < 0) - TEST_ERROR + /* + * Delete all time messages. + */ + TESTING("message deletion"); + if(H5O_msg_remove(&oh_loc, H5O_MTIME_NEW_ID, H5O_ALL, TRUE, H5P_DATASET_XFER_DEFAULT) < 0) + FAIL_STACK_ERROR + if(H5O_msg_remove(&oh_loc, H5O_MTIME_ID, H5O_ALL, TRUE, H5P_DATASET_XFER_DEFAULT) < 0) + FAIL_STACK_ERROR + if(H5O_msg_read(&oh_loc, H5O_MTIME_NEW_ID, &ro, H5P_DATASET_XFER_DEFAULT)) + FAIL_STACK_ERROR + if(H5O_msg_read(&oh_loc, H5O_MTIME_ID, &ro, H5P_DATASET_XFER_DEFAULT)) + FAIL_STACK_ERROR + PASSED(); - /* Check that the "unknown" message was _NOT_ marked */ - if(H5O_check_msg_marked_test(dset, FALSE) < 0) + + /* release resources */ + TESTING("object header closing"); + if(H5O_close(&oh_loc) < 0) FAIL_STACK_ERROR + PASSED(); - /* Close the dataset */ - if(H5Dclose(dset) < 0) - TEST_ERROR - /* Close the file we created (to flush change to object header) */ - if(H5Fclose(file) < 0) - TEST_ERROR + /* Test reading datasets with undefined object header messages */ + puts("Reading objects with unknown header messages"); + envval = HDgetenv("HDF5_DRIVER"); + if(envval == NULL) + envval = "nomatch"; + if(HDstrcmp(envval, "core") && HDstrcmp(envval, "multi") && HDstrcmp(envval, "split") && HDstrcmp(envval, "family")) { + hid_t file2; /* File ID for 'bogus' object file */ + char testpath[512] = ""; + char testfile[512] = ""; + char *srcdir = HDgetenv("srcdir"); - /* Re-open the file created */ - if((file = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0) - TEST_ERROR + /* Build path to all test files */ + if(srcdir && ((HDstrlen(srcdir) + 2) < sizeof(testpath))) { + HDstrcpy(testpath, srcdir); + HDstrcat(testpath, "/"); + } /* end if */ - /* Open the dataset with the "mark if unknown" message */ - if((dset = H5Dopen(file, "/Dataset3")) < 0) - TEST_ERROR - if(H5Dclose(dset) < 0) - TEST_ERROR + /* Build path to test file */ + if(srcdir && ((HDstrlen(testpath) + HDstrlen(FILE_BOGUS) + 1) < sizeof(testfile))) + HDstrcpy(testfile, testpath); + HDstrcat(testfile, FILE_BOGUS); - /* Close the file we created (to flush change to object header) */ - if(H5Fclose(file) < 0) - TEST_ERROR + TESTING("object with unknown header message and no flags set"); - /* Re-open the file created */ - if((file = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0) - TEST_ERROR + /* Open the file with objects that have unknown header messages (generated with gen_bogus.c) */ + if((file2 = H5Fopen(testfile, H5F_ACC_RDONLY, fapl)) < 0) + TEST_ERROR - /* Re-open the dataset with the "mark if unknown" message */ - if((dset = H5Dopen(file, "/Dataset3")) < 0) - TEST_ERROR + /* Open the dataset with the unknown header message, but no extra flags */ + if((dset = H5Dopen(file2, "/Dataset1")) < 0) + TEST_ERROR + if(H5Dclose(dset) < 0) + TEST_ERROR - /* Check that the "unknown" message was marked */ - if(H5O_check_msg_marked_test(dset, TRUE) < 0) - FAIL_STACK_ERROR + PASSED(); - /* Close the dataset */ - if(H5Dclose(dset) < 0) - TEST_ERROR + TESTING("object with unknown header message & 'fail if unknown' flag set"); + /* Attempt to open the dataset with the unknown header message, and "fail if unknown" flag */ + H5E_BEGIN_TRY { + dset = H5Dopen(file2, "/Dataset2"); + } H5E_END_TRY; + if(dset >= 0) { + H5Dclose(dset); + TEST_ERROR + } /* end if */ - /* Close the file with the bogus objects */ - if(H5Fclose(file2) < 0) - TEST_ERROR + PASSED(); - PASSED(); - } /* end if */ - else { - SKIPPED(); - puts(" Test not compatible with current Virtual File Driver"); - } /* end else */ - - /* Close the file we created */ - if(H5Fclose(file) < 0) - TEST_ERROR + TESTING("object with unknown header message & 'mark if unknown' flag set"); + + /* Copy object with "mark if unknown" flag on message into file that can be modified */ + if(H5Ocopy(file2, "/Dataset3", file, "/Dataset3", H5P_DEFAULT, H5P_DEFAULT) < 0) + TEST_ERROR + + /* Close the file we created (to flush changes to file) */ + if(H5Fclose(file) < 0) + TEST_ERROR + + /* Re-open the file created, with read-only permissions */ + if((file = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0) + TEST_ERROR + + /* Open the dataset with the "mark if unknown" message */ + if((dset = H5Dopen(file, "/Dataset3")) < 0) + TEST_ERROR + + /* Check that the "unknown" message was _NOT_ marked */ + if(H5O_check_msg_marked_test(dset, FALSE) < 0) + FAIL_STACK_ERROR + + /* Close the dataset */ + if(H5Dclose(dset) < 0) + TEST_ERROR + + /* Close the file we created (to flush change to object header) */ + if(H5Fclose(file) < 0) + TEST_ERROR + + /* Re-open the file created */ + if((file = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0) + TEST_ERROR + + /* Open the dataset with the "mark if unknown" message */ + if((dset = H5Dopen(file, "/Dataset3")) < 0) + TEST_ERROR + if(H5Dclose(dset) < 0) + TEST_ERROR + + /* Close the file we created (to flush change to object header) */ + if(H5Fclose(file) < 0) + TEST_ERROR + + /* Re-open the file created */ + if((file = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0) + TEST_ERROR + + /* Re-open the dataset with the "mark if unknown" message */ + if((dset = H5Dopen(file, "/Dataset3")) < 0) + TEST_ERROR + + /* Check that the "unknown" message was marked */ + if(H5O_check_msg_marked_test(dset, TRUE) < 0) + FAIL_STACK_ERROR + + /* Close the dataset */ + if(H5Dclose(dset) < 0) + TEST_ERROR + + + /* Close the file with the bogus objects */ + if(H5Fclose(file2) < 0) + TEST_ERROR + + PASSED(); + } /* end if */ + else { + SKIPPED(); + puts(" Test not compatible with current Virtual File Driver"); + } /* end else */ + + /* Close the file we created */ + if(H5Fclose(file) < 0) + TEST_ERROR + } /* end for */ puts("All object header tests passed."); h5_cleanup(FILENAME, fapl); -- cgit v0.12