diff --git a/src/rest_vol.c b/src/rest_vol.c index 72852f32..80893ca0 100644 --- a/src/rest_vol.c +++ b/src/rest_vol.c @@ -98,6 +98,9 @@ typedef struct H5_rest_ad_info_t { hbool_t unattended; } H5_rest_ad_info_t; +/* Global array containing information about open objects */ +RV_type_info *RV_type_info_array_g[H5I_MAX_NUM_TYPES]; + /* Host header string for specifying the host (Domain) for requests */ const char *const host_string = "X-Hdf-domain: "; @@ -451,6 +454,12 @@ H5_rest_init(hid_t vipl_id) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); #endif + /* Set up global type info array */ + for (size_t i = 0; i < H5I_MAX_NUM_TYPES; i++) { + RV_type_info_array_g[i] = RV_calloc(sizeof(RV_type_info)); + RV_type_info_array_g[i]->table = rv_hash_table_new(rv_hash_string, H5_rest_compare_string_keys); + } + /* Register the connector with HDF5's error reporting API */ if ((H5_rest_err_class_g = H5Eregister_class(HDF5_VOL_REST_ERR_CLS_NAME, HDF5_VOL_REST_LIB_NAME, HDF5_VOL_REST_LIB_VER)) < 0) @@ -616,6 +625,12 @@ H5_rest_term(void) curl_global_cleanup(); } /* end if */ + /* Cleanup type info array */ + for (size_t i = 0; i < H5I_MAX_NUM_TYPES; i++) { + rv_hash_table_free(RV_type_info_array_g[i]->table); + RV_free(RV_type_info_array_g[i]); + RV_type_info_array_g[i] = NULL; + } /* * "Forget" connector ID. This should normally be called by the library * when it is closing the id, so no need to close it here. diff --git a/src/rest_vol.h b/src/rest_vol.h index f5b19e9d..3b91ba9b 100644 --- a/src/rest_vol.h +++ b/src/rest_vol.h @@ -427,6 +427,18 @@ struct response_buffer { }; extern struct response_buffer response_buffer; +/* Struct containing information about open objects of each type in the VOL*/ +typedef struct RV_type_info { + size_t open_count; + rv_hash_table_t *table; +} RV_type_info; + +/* TODO - This is copied directly from the library */ +#define TYPE_BITS 7 +#define TYPE_MASK (((hid_t)1 << TYPE_BITS) - 1) +#define H5I_MAX_NUM_TYPES TYPE_MASK +extern RV_type_info *RV_type_info_array_g[]; + /************************** * * * Typedefs * diff --git a/src/rest_vol_attr.c b/src/rest_vol_attr.c index 3f8eea48..f88c795e 100644 --- a/src/rest_vol_attr.c +++ b/src/rest_vol_attr.c @@ -353,6 +353,10 @@ RV_attr_create(void *obj, const H5VL_loc_params_t *loc_params, const char *attr_ printf("-> Created attribute\n\n"); #endif + if (rv_hash_table_insert(RV_type_info_array_g[H5I_ATTR]->table, (char *)new_attribute, + (char *)new_attribute) == 0) + FUNC_GOTO_ERROR(H5E_ATTR, H5E_CANTALLOC, NULL, "Failed to add attribute to type info array"); + ret_value = (void *)new_attribute; done: @@ -752,6 +756,10 @@ RV_attr_open(void *obj, const H5VL_loc_params_t *loc_params, const char *attr_na if ((attribute->u.attribute.acpl_id = H5Pcreate(H5P_ATTRIBUTE_CREATE)) < 0) FUNC_GOTO_ERROR(H5E_PLIST, H5E_CANTCREATE, NULL, "can't create ACPL for attribute"); + if (rv_hash_table_insert(RV_type_info_array_g[H5I_ATTR]->table, (char *)attribute, (char *)attribute) == + 0) + FUNC_GOTO_ERROR(H5E_ATTR, H5E_CANTALLOC, NULL, "Failed to add attribute to type info array"); + ret_value = (void *)attribute; done: @@ -2737,6 +2745,9 @@ RV_attr_close(void *attr, hid_t dxpl_id, void **req) FUNC_DONE_ERROR(H5E_PLIST, H5E_CANTCLOSEOBJ, FAIL, "can't close ACPL"); } /* end if */ + if (RV_type_info_array_g[H5I_ATTR]) + rv_hash_table_remove(RV_type_info_array_g[H5I_ATTR]->table, (char *)_attr); + if (RV_file_close(_attr->domain, H5P_DEFAULT, NULL) < 0) FUNC_GOTO_ERROR(H5E_FILE, H5E_CANTCLOSEOBJ, FAIL, "couldn't close attr domain"); diff --git a/src/rest_vol_dataset.c b/src/rest_vol_dataset.c index de977f17..0504b40c 100644 --- a/src/rest_vol_dataset.c +++ b/src/rest_vol_dataset.c @@ -282,6 +282,10 @@ RV_dataset_create(void *obj, const H5VL_loc_params_t *loc_params, const char *na if ((new_dataset->u.dataset.space_id = H5Scopy(space_id)) < 0) FUNC_GOTO_ERROR(H5E_DATASPACE, H5E_CANTCOPY, NULL, "failed to copy dataset's dataspace"); + if (rv_hash_table_insert(RV_type_info_array_g[H5I_DATASET]->table, (char *)new_dataset, + (char *)new_dataset) == 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_CANTALLOC, NULL, "Failed to add dataset to type info array"); + ret_value = (void *)new_dataset; done: @@ -342,6 +346,12 @@ RV_dataset_open(void *obj, const H5VL_loc_params_t *loc_params, const char *name size_t path_size = 0; size_t path_len = 0; + RV_type_info *type_info = RV_type_info_array_g[H5I_DATASET]; + rv_hash_table_iter_t iterator; + rv_hash_table_iterate(type_info->table, &iterator); + hid_t matching_dspace = H5I_INVALID_HID; + RV_object_t *other_dataset = H5I_INVALID_HID; + #ifdef RV_CONNECTOR_DEBUG printf("-> Received dataset open call with following parameters:\n"); printf(" - loc_id object's URI: %s\n", parent->URI); @@ -394,7 +404,23 @@ RV_dataset_open(void *obj, const H5VL_loc_params_t *loc_params, const char *name #endif /* Set up a Dataspace for the opened Dataset */ - if ((dataset->u.dataset.space_id = RV_parse_dataspace(response_buffer.buffer)) < 0) + + /* If this is another view of an already-opened dataset, make them share the same dataspace + * so that changes to it (e.g. resizes) are visible to both views */ + while (rv_hash_table_iter_has_more(&iterator)) { + other_dataset = (RV_object_t *)rv_hash_table_iter_next(&iterator); + + if (!strcmp(other_dataset->URI, dataset->URI)) { + matching_dspace = other_dataset->u.dataset.space_id; + break; + } + } + + if (matching_dspace != H5I_INVALID_HID) { + dataset->u.dataset.space_id = matching_dspace; + H5I_inc_ref(matching_dspace); + } + else if ((dataset->u.dataset.space_id = RV_parse_dataspace(response_buffer.buffer)) < 0) FUNC_GOTO_ERROR(H5E_DATASPACE, H5E_CANTCONVERT, NULL, "can't convert JSON to usable dataspace for dataset"); @@ -423,6 +449,9 @@ RV_dataset_open(void *obj, const H5VL_loc_params_t *loc_params, const char *name FUNC_GOTO_ERROR(H5E_DATASET, H5E_CANTCREATE, NULL, "can't parse dataset's creation properties from JSON representation"); + if (rv_hash_table_insert(RV_type_info_array_g[H5I_DATASET]->table, (char *)dataset, (char *)dataset) == 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_CANTALLOC, NULL, "Failed to add dataset to type info array"); + ret_value = (void *)dataset; done: @@ -1366,8 +1395,19 @@ RV_dataset_get(void *obj, H5VL_dataset_get_args_t *args, hid_t dxpl_id, void **r herr_t RV_dataset_specific(void *obj, H5VL_dataset_specific_args_t *args, hid_t dxpl_id, void **req) { - RV_object_t *dset = (RV_object_t *)obj; - herr_t ret_value = SUCCEED; + RV_object_t *dset = (RV_object_t *)obj; + herr_t ret_value = SUCCEED; + size_t host_header_len = 0; + char *host_header = NULL; + char *request_body = NULL; + char *request_body_shape = NULL; + char request_url[URL_MAX_LENGTH]; + int url_len = 0; + hid_t dspace_id = H5I_INVALID_HID; + hid_t new_dspace_id = H5I_INVALID_HID; + hsize_t *old_extent = NULL; + hsize_t *maxdims = NULL; + upload_info uinfo; #ifdef RV_CONNECTOR_DEBUG printf("-> Received dataset-specific call with following parameters:\n"); @@ -1383,11 +1423,146 @@ RV_dataset_specific(void *obj, H5VL_dataset_specific_args_t *args, hid_t dxpl_id switch (args->op_type) { /* H5Dset_extent */ case H5VL_DATASET_SET_EXTENT: + size_t ndims = 0; + hsize_t *new_extent = NULL; + /* Check for write access */ if (!(dset->domain->u.file.intent & H5F_ACC_RDWR)) FUNC_GOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "no write intent on file"); - FUNC_GOTO_ERROR(H5E_DATASET, H5E_UNSUPPORTED, FAIL, "H5Dset_extent is unsupported"); + if (!args->args.set_extent.size) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_BADVALUE, FAIL, "given dimension array is NULL"); + + new_extent = args->args.set_extent.size; + + /* Assemble curl request */ + if ((url_len = + snprintf(request_url, URL_MAX_LENGTH, "%s/datasets/%s/shape", base_URL, dset->URI)) < 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_SYSERRSTR, FAIL, "snprintf error"); + + if (url_len >= URL_MAX_LENGTH) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_SYSERRSTR, FAIL, + "H5Dset_extent request URL size exceeded maximum URL size"); + + /* Setup the host header */ + host_header_len = strlen(dset->domain->u.file.filepath_name) + strlen(host_string) + 1; + if (NULL == (host_header = (char *)RV_malloc(host_header_len))) + FUNC_GOTO_ERROR(H5E_SYM, H5E_CANTALLOC, FAIL, "can't allocate space for request Host header"); + + strcpy(host_header, host_string); + + curl_headers = + curl_slist_append(curl_headers, strncat(host_header, dset->domain->u.file.filepath_name, + host_header_len - strlen(host_string) - 1)); + + /* Disable use of Expect: 100 Continue HTTP response */ + curl_headers = curl_slist_append(curl_headers, "Expect:"); + + if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_headers)) + FUNC_GOTO_ERROR(H5E_SYM, H5E_CANTSET, FAIL, "can't set cURL HTTP headers: %s", curl_err_buf); + if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_URL, request_url)) + FUNC_GOTO_ERROR(H5E_SYM, H5E_CANTSET, FAIL, "can't set cURL request URL: %s", curl_err_buf); + + /* First, make a GET request for the dataset's shape to do some checks */ + if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_HTTPGET, 1)) + FUNC_GOTO_ERROR(H5E_SYM, H5E_CANTSET, FAIL, "can't set up cURL to make HTTP GET request: %s", + curl_err_buf); + + CURL_PERFORM(curl, H5E_DATASET, H5E_CANTGET, FAIL); + + if ((dspace_id = RV_parse_dataspace(response_buffer.buffer)) < 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_PARSEERROR, FAIL, "failed to parse dataspace"); + + if ((ndims = H5Sget_simple_extent_ndims(dspace_id)) < 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "failed to get number of dataset dimensions"); + + if ((old_extent = calloc((size_t)ndims, sizeof(hssize_t))) == NULL) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_CANTALLOC, FAIL, + "failed to allocate memory for dataset shape"); + + if ((maxdims = calloc((size_t)ndims, sizeof(hssize_t))) == NULL) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_CANTALLOC, FAIL, + "failed to allocate memory for dataset max shape"); + + if (H5Sget_simple_extent_dims(dspace_id, old_extent, maxdims) < 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "failed to get dataset dimensions"); + + for (size_t i = 0; i < (size_t)ndims; i++) + if (new_extent[i] > maxdims[i]) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_BADVALUE, FAIL, + "new dataset dimensions exceed maximum dimensions"); + + /* HSDS limitation */ + for (size_t i = 0; i < (size_t)ndims; i++) + if (new_extent[i] < old_extent[i]) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_UNSUPPORTED, FAIL, + "using H5Dset_extent to decrease dataset size is unsupported"); + + /* Construct JSON containing new dataset extent */ + const char *fmt_string = "{" + "\"shape\": [%s]" + "}"; + + /* Compute space needed for request */ + size_t request_body_shape_size = 0; + + for (size_t i = 0; i < ndims; i++) { + /* N bytes needed to store an N digit number, + * floor(log10) + 1 of an N digit number is >= N, + * plus two bytes for space and comma characters in the list */ + request_body_shape_size += floor(log10((double)new_extent[i])) + 1 + 2; + } + + if ((request_body = RV_malloc(request_body_shape_size + strlen(fmt_string) + 1)) == NULL) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_CANTALLOC, FAIL, "can't allocate memory for request body"); + + if ((request_body_shape = RV_malloc(request_body_shape_size)) == NULL) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_CANTALLOC, FAIL, + "can't allocate memory for request body shape"); + + request_body_shape[0] = '\0'; + char *curr_internal_ptr = request_body_shape; + + char dim_buffer[URL_MAX_LENGTH]; + + for (size_t i = 0; i < ndims; i++) { + int dim_len = snprintf(dim_buffer, URL_MAX_LENGTH, "%zu", new_extent[i]); + + strcat(curr_internal_ptr, dim_buffer); + curr_internal_ptr += dim_len; + + if (i != ndims - 1) { + strcat(curr_internal_ptr, ", "); + curr_internal_ptr += 2; + } + } + + snprintf(request_body, request_body_shape_size + strlen(fmt_string) + 1, fmt_string, + request_body_shape); + + uinfo.buffer = request_body; + uinfo.buffer_size = (size_t)strlen(request_body); + uinfo.bytes_sent = 0; + + /* Make PUT request to change dataset extent */ + if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L)) + FUNC_GOTO_ERROR(H5E_SYM, H5E_CANTSET, FAIL, "can't set up cURL to make HTTP PUT request: %s", + curl_err_buf); + if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_READDATA, &uinfo)) + FUNC_GOTO_ERROR(H5E_ATTR, H5E_CANTSET, FAIL, "can't set cURL PUT data: %s", curl_err_buf); + + if (CURLE_OK != + curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)strlen(request_body))) + FUNC_GOTO_ERROR(H5E_ATTR, H5E_CANTSET, FAIL, "can't set cURL PUT data size: %s", + curl_err_buf); + + CURL_PERFORM(curl, H5E_DATASET, H5E_CANTGET, FAIL); + + /* Modify local dataspace to match version on server */ + if (H5Sset_extent_simple(dset->u.dataset.space_id, ndims, new_extent, maxdims) < 0) + FUNC_GOTO_ERROR(H5E_DATASET, H5E_DATASPACE, FAIL, + "unable to modify extent of local dataspace"); + break; /* H5Dflush */ @@ -1407,6 +1582,29 @@ RV_dataset_specific(void *obj, H5VL_dataset_specific_args_t *args, hid_t dxpl_id done: PRINT_ERROR_STACK; + /* Unset cURL UPLOAD option to ensure that future requests don't try to use PUT calls */ + if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_UPLOAD, 0)) + FUNC_DONE_ERROR(H5E_ATTR, H5E_CANTSET, FAIL, "can't unset cURL PUT option: %s", curl_err_buf); + + if (host_header) + RV_free(host_header); + + if (curl_headers) { + curl_slist_free_all(curl_headers); + curl_headers = NULL; + } + + if (dspace_id != H5I_INVALID_HID) + H5Sclose(dspace_id); + + if ((ret_value < 0) && (new_dspace_id != H5I_INVALID_HID)) + H5Sclose(new_dspace_id); + + RV_free(old_extent); + RV_free(request_body); + RV_free(request_body_shape); + RV_free(maxdims); + return ret_value; } /* end RV_dataset_specific() */ @@ -1458,6 +1656,9 @@ RV_dataset_close(void *dset, hid_t dxpl_id, void **req) FUNC_DONE_ERROR(H5E_PLIST, H5E_CANTCLOSEOBJ, FAIL, "can't close DCPL"); } /* end if */ + if (RV_type_info_array_g[H5I_DATASET]) + rv_hash_table_remove(RV_type_info_array_g[H5I_DATASET]->table, (char *)_dset); + if (RV_file_close(_dset->domain, H5P_DEFAULT, NULL)) { FUNC_DONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "can't close file"); } diff --git a/src/rest_vol_datatype.c b/src/rest_vol_datatype.c index 43bba653..4cf417e8 100644 --- a/src/rest_vol_datatype.c +++ b/src/rest_vol_datatype.c @@ -284,6 +284,10 @@ RV_datatype_commit(void *obj, const H5VL_loc_params_t *loc_params, const char *n if (RV_parse_response(response_buffer.buffer, NULL, new_datatype->URI, RV_copy_object_URI_callback) < 0) FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_CANTGET, NULL, "can't parse committed datatype's URI"); + if (rv_hash_table_insert(RV_type_info_array_g[H5I_DATATYPE]->table, (char *)new_datatype, + (char *)new_datatype) == 0) + FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, NULL, "Failed to add datatype to type info array"); + ret_value = (void *)new_datatype; done: @@ -419,6 +423,10 @@ RV_datatype_open(void *obj, const H5VL_loc_params_t *loc_params, const char *nam if ((datatype->u.datatype.tcpl_id = H5Pcreate(H5P_DATATYPE_CREATE)) < 0) FUNC_GOTO_ERROR(H5E_PLIST, H5E_CANTCREATE, NULL, "can't create TCPL for datatype"); + if (rv_hash_table_insert(RV_type_info_array_g[H5I_DATATYPE]->table, (char *)datatype, (char *)datatype) == + 0) + FUNC_GOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, NULL, "Failed to add datatype to type info array"); + ret_value = (void *)datatype; done: @@ -567,6 +575,9 @@ RV_datatype_close(void *dt, hid_t dxpl_id, void **req) FUNC_DONE_ERROR(H5E_PLIST, H5E_CANTCLOSEOBJ, FAIL, "can't close TCPL"); } /* end if */ + if (RV_type_info_array_g[H5I_DATATYPE]) + rv_hash_table_remove(RV_type_info_array_g[H5I_DATATYPE]->table, (char *)_dtype); + if (RV_file_close(_dtype->domain, H5P_DEFAULT, NULL) < 0) FUNC_DONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "can't close file"); diff --git a/src/rest_vol_file.c b/src/rest_vol_file.c index 59b11ada..65af84b4 100644 --- a/src/rest_vol_file.c +++ b/src/rest_vol_file.c @@ -267,6 +267,9 @@ RV_file_create(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id, h RV_parse_server_version) < 0) FUNC_GOTO_ERROR(H5E_FILE, H5E_CANTCREATE, NULL, "can't parse server version"); + if (rv_hash_table_insert(RV_type_info_array_g[H5I_FILE]->table, (char *)new_file, (char *)new_file) == 0) + FUNC_GOTO_ERROR(H5E_FILE, H5E_CANTALLOC, NULL, "Failed to add file to type info array"); + ret_value = (void *)new_file; done: @@ -347,8 +350,7 @@ RV_file_open(const char *name, unsigned flags, hid_t fapl_id, hid_t dxpl_id, voi */ if (fapl_id == H5P_FILE_ACCESS_DEFAULT) if (H5_rest_set_connection_information() < 0) - FUNC_GOTO_ERROR(H5E_FILE, H5E_CANTINIT, NULL, - "can't set REST VOL connector connection information"); + FUNC_GOTO_ERROR(H5E_FILE, H5E_CANTINIT, NULL, "can't set REST VOL connector connection information"); if (fapl_id == H5I_INVALID_HID) FUNC_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid FAPL"); @@ -438,6 +440,9 @@ RV_file_open(const char *name, unsigned flags, hid_t fapl_id, hid_t dxpl_id, voi if ((file->u.file.fcpl_id = H5Pcreate(H5P_FILE_CREATE)) < 0) FUNC_GOTO_ERROR(H5E_PLIST, H5E_CANTCREATE, NULL, "can't create FCPL for file"); + if (rv_hash_table_insert(RV_type_info_array_g[H5I_FILE]->table, (char *)file, (char *)file) == 0) + FUNC_GOTO_ERROR(H5E_FILE, H5E_CANTALLOC, NULL, "Failed to add file to type info array"); + ret_value = (void *)file; done: @@ -862,6 +867,9 @@ RV_file_close(void *file, hid_t dxpl_id, void **req) _file->handle_path = NULL; } + if (RV_type_info_array_g[H5I_FILE]) + rv_hash_table_remove(RV_type_info_array_g[H5I_FILE]->table, (char *)_file); + RV_free(_file); } diff --git a/src/rest_vol_group.c b/src/rest_vol_group.c index 0367ae7d..d21014a2 100644 --- a/src/rest_vol_group.c +++ b/src/rest_vol_group.c @@ -283,6 +283,10 @@ RV_group_create(void *obj, const H5VL_loc_params_t *loc_params, const char *name if (RV_parse_response(response_buffer.buffer, NULL, new_group->URI, RV_copy_object_URI_callback) < 0) FUNC_GOTO_ERROR(H5E_SYM, H5E_CANTCREATE, NULL, "can't parse new group's URI"); + if (rv_hash_table_insert(RV_type_info_array_g[H5I_GROUP]->table, (char *)new_group, (char *)new_group) == + 0) + FUNC_GOTO_ERROR(H5E_SYM, H5E_CANTALLOC, NULL, "Failed to add group to type info array"); + ret_value = (void *)new_group; done: @@ -434,6 +438,9 @@ RV_group_open(void *obj, const H5VL_loc_params_t *loc_params, const char *name, else group->u.group.gapl_id = H5P_GROUP_ACCESS_DEFAULT; + if (rv_hash_table_insert(RV_type_info_array_g[H5I_GROUP]->table, (char *)group, (char *)group) == 0) + FUNC_GOTO_ERROR(H5E_SYM, H5E_CANTALLOC, NULL, "Failed to add group to type info array"); + ret_value = (void *)group; done: @@ -709,6 +716,9 @@ RV_group_close(void *grp, hid_t dxpl_id, void **req) FUNC_DONE_ERROR(H5E_PLIST, H5E_CANTCLOSEOBJ, FAIL, "can't close GCPL"); } /* end if */ + if (RV_type_info_array_g[H5I_GROUP]) + rv_hash_table_remove(RV_type_info_array_g[H5I_GROUP]->table, (char *)_grp); + if (RV_file_close(_grp->domain, H5P_DEFAULT, NULL) < 0) { FUNC_DONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "can't close file"); }