/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* This program demonstrates how to create and use "external links" in * HDF5. * * External links point from one HDF5 file to an object (Group, Dataset, or * committed Datatype) in another file. */ #include "hdf5.h" #include #define SOURCE_FILE "extlink_source.h5" #define TARGET_FILE "extlink_target.h5" #define PREFIX_SOURCE_FILE "extlink_prefix_source.h5" #define SOFT_LINK_FILE "soft_link.h5" #define SOFT_LINK_NAME "soft_link_to_group" #define UD_SOFT_LINK_NAME "ud_soft_link" #define TARGET_GROUP "target_group" #define UD_SOFT_CLASS 65 #define HARD_LINK_FILE "hard_link.h5" #define HARD_LINK_NAME "hard_link_to_group" #define UD_HARD_LINK_NAME "ud_hard_link" #define UD_HARD_CLASS 66 #define PLIST_LINK_PROP "plist_link_prop" #define UD_PLIST_CLASS 66 /* Basic external link example * * Creates two files and uses an external link to access an object in the * second file from the first file. */ static void extlink_example(void) { hid_t source_file_id, targ_file_id; hid_t group_id, group2_id; /* Create two files, a source and a target */ source_file_id = H5Fcreate(SOURCE_FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); targ_file_id = H5Fcreate(TARGET_FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); /* Create a group in the target file for the external link to point to. */ group_id = H5Gcreate2(targ_file_id, "target_group", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); /* Close the group and the target file */ H5Gclose(group_id); /* Create an external link in the source file pointing to the target group. * We could instead have created the external link first, then created the * group it points to; the order doesn't matter. */ H5Lcreate_external(TARGET_FILE, "target_group", source_file_id, "ext_link", H5P_DEFAULT, H5P_DEFAULT); /* Now we can use the external link to create a new group inside the * target group (even though the target file is closed!). The external * link works just like a soft link. */ group_id = H5Gcreate2(source_file_id, "ext_link/new_group", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); /* The group is inside the target file and we can access it normally. * Here, group_id and group2_id point to the same group inside the * target file. */ group2_id = H5Gopen2(targ_file_id, "target_group/new_group", H5P_DEFAULT); /* Don't forget to close the IDs we opened. */ H5Gclose(group2_id); H5Gclose(group_id); H5Fclose(targ_file_id); H5Fclose(source_file_id); /* The link from the source file to the target file will work as long as * the target file can be found. If the target file is moved, renamed, * or deleted in the filesystem, HDF5 won't be able to find it and the * external link will "dangle." */ } /* External link prefix example * * Uses a group access property list to set a "prefix" for the filenames * accessed through an external link. * * Group access property lists inherit from link access property lists; * the external link prefix property is actually a property of LAPLs. * * This example requires a "red" directory and a "blue" directory to exist * where it is run (so to run this example on Unix, first mkdir red and mkdir * blue). */ static void extlink_prefix_example(void) { hid_t source_file_id, red_file_id, blue_file_id; hid_t group_id, group2_id; hid_t gapl_id; /* Create three files, a source and two targets. The targets will have * the same name, but one will be located in the red directory and one will * be located in the blue directory */ source_file_id = H5Fcreate(PREFIX_SOURCE_FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); red_file_id = H5Fcreate("red/prefix_target.h5", H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); blue_file_id = H5Fcreate("blue/prefix_target.h5", H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); /* This test needs a red and a blue directory in the filesystem. If they're not present, * trying to create the files above will fail. */ if (red_file_id < 0 || blue_file_id < 0) printf("This test requires directories named 'red' and 'blue' to exist. Did you forget to create " "them?\n"); /* Create an external link in the source file pointing to the root group of * a file named prefix_target.h5. This file doesn't exist in the current * directory, but the files in the red and blue directories both have this * name. */ H5Lcreate_external("prefix_target.h5", "/", source_file_id, "ext_link", H5P_DEFAULT, H5P_DEFAULT); /* If we tried to traverse the external link now, we would fail (since the * file it points to doesn't exist). Instead, we'll create a group access * property list that will provide a prefix path to the external link. * Group access property lists inherit the properties of link access * property lists. */ gapl_id = H5Pcreate(H5P_GROUP_ACCESS); H5Pset_elink_prefix(gapl_id, "red/"); /* Now if we traverse the external link, HDF5 will look for an external * file named red/prefix_target.h5, which exists. * To pass the group access property list, we need to use H5Gopen2. */ group_id = H5Gopen2(source_file_id, "ext_link", gapl_id); /* Now we can use the open group ID to create a new group inside the * "red" file. */ group2_id = H5Gcreate2(group_id, "pink", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); /* Close both groups. */ H5Gclose(group2_id); H5Gclose(group_id); /* If we change the prefix, the same external link can find a file in the blue * directory. */ H5Pset_elink_prefix(gapl_id, "blue/"); group_id = H5Gopen2(source_file_id, "ext_link", gapl_id); group2_id = H5Gcreate2(group_id, "sky blue", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); /* Close both groups. */ H5Gclose(group2_id); H5Gclose(group_id); /* Each file has had a group created inside it using the same external link. */ group_id = H5Gopen2(red_file_id, "pink", H5P_DEFAULT); group2_id = H5Gopen2(blue_file_id, "sky blue", H5P_DEFAULT); /* Clean up our open IDs */ H5Gclose(group2_id); H5Gclose(group_id); H5Pclose(gapl_id); H5Fclose(blue_file_id); H5Fclose(red_file_id); H5Fclose(source_file_id); /* User-defined links can expand on the ability to pass in parameters * using an access property list; for instance, a user-defined link * might function like an external link but allow the full filename to be * passed in through the access property list. */ } /* Soft Link example * * Create a new class of user-defined links that behave like HDF5's built-in * soft links. * * This isn't very useful by itself (HDF5's soft links already do the same * thing), but it can serve as an example for how to reference objects by * name. */ /* We need to define the callback function that the soft link will use. * It is defined after the example below. * To keep the example simple, these links don't have a query callback. * In general, link classes should always be query-able. * We might also have wanted to supply a creation callback that checks * that a path was supplied in the udata. */ static hid_t UD_soft_traverse(const char *link_name, hid_t cur_group, const void *udata, size_t udata_size, hid_t lapl_id, hid_t dxpl_id); static void soft_link_example(void) { hid_t file_id; hid_t group_id; /* Define the link class that we'll use to register "user-defined soft * links" using the callbacks we defined above. * A link class can have NULL for any callback except its traverse * callback. */ const H5L_class_t UD_soft_class[1] = {{ H5L_LINK_CLASS_T_VERS, /* Version number for this struct. * This field is always H5L_LINK_CLASS_T_VERS */ (H5L_type_t)UD_SOFT_CLASS, /* Link class id number. This can be any * value between H5L_TYPE_UD_MIN (64) and * H5L_TYPE_MAX (255). It should be a * value that isn't already being used by * another kind of link. We'll use 65. */ "UD_soft_link", /* Link class name for debugging */ NULL, /* Creation callback */ NULL, /* Move callback */ NULL, /* Copy callback */ UD_soft_traverse, /* The actual traversal function */ NULL, /* Deletion callback */ NULL /* Query callback */ }}; /* First, create a file and an object within the file for the link to * point to. */ file_id = H5Fcreate(SOFT_LINK_FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); group_id = H5Gcreate2(file_id, TARGET_GROUP, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); H5Gclose(group_id); /* This is how we create a normal soft link to the group. */ H5Lcreate_soft(TARGET_GROUP, file_id, SOFT_LINK_NAME, H5P_DEFAULT, H5P_DEFAULT); /* To do the same thing using a user-defined link, we first have to * register the link class we defined. */ H5Lregister(UD_soft_class); /* Now create a user-defined link. We give it the path to the group * as its udata.1 */ H5Lcreate_ud(file_id, UD_SOFT_LINK_NAME, (H5L_type_t)UD_SOFT_CLASS, TARGET_GROUP, strlen(TARGET_GROUP) + 1, H5P_DEFAULT, H5P_DEFAULT); /* We can access the group through the UD soft link like we would through * a normal soft link. This link will still dangle if the object's * original name is changed or unlinked. */ group_id = H5Gopen2(file_id, UD_SOFT_LINK_NAME, H5P_DEFAULT); /* The group is now open normally. Don't forget to close it! */ H5Gclose(group_id); H5Fclose(file_id); } /* UD_soft_traverse * The actual traversal function simply needs to open the correct object by * name and return its ID. */ static hid_t UD_soft_traverse(const char *link_name, hid_t cur_group, const void *udata, size_t udata_size, hid_t lapl_id, hid_t dxpl_id) { const char *target = (const char *)udata; hid_t ret_value; /* Pass the udata straight through to HDF5. If it's invalid, let HDF5 * return an error. */ ret_value = H5Oopen(cur_group, target, lapl_id); return ret_value; } /* Hard Link example * * Create a new class of user-defined links that behave like HDF5's built-in * hard links. * * This isn't very useful by itself (HDF5's hard links already do the same * thing), but it can serve as an example for how to reference objects by * address. */ /* We need to define the callback functions that the hard link will use. * These are defined after the example below. * To keep the example simple, these links don't have a query callback. * Generally, real link classes should always be query-able. */ static herr_t UD_hard_create(const char *link_name, hid_t loc_group, const void *udata, size_t udata_size, hid_t lcpl_id); static herr_t UD_hard_delete(const char *link_name, hid_t loc_group, const void *udata, size_t udata_size); static hid_t UD_hard_traverse(const char *link_name, hid_t cur_group, const void *udata, size_t udata_size, hid_t lapl_id, hid_t dxpl_id); static void hard_link_example(void) { hid_t file_id; hid_t group_id; H5L_info2_t li; /* Define the link class that we'll use to register "user-defined hard * links" using the callbacks we defined above. * A link class can have NULL for any callback except its traverse * callback. */ const H5L_class_t UD_hard_class[1] = {{ H5L_LINK_CLASS_T_VERS, /* Version number for this struct. * This field is always H5L_LINK_CLASS_T_VERS */ (H5L_type_t)UD_HARD_CLASS, /* Link class id number. This can be any * value between H5L_TYPE_UD_MIN (64) and * H5L_TYPE_MAX (255). It should be a * value that isn't already being used by * another kind of link. We'll use 66. */ "UD_hard_link", /* Link class name for debugging */ UD_hard_create, /* Creation callback */ NULL, /* Move callback */ NULL, /* Copy callback */ UD_hard_traverse, /* The actual traversal function */ UD_hard_delete, /* Deletion callback */ NULL /* Query callback */ }}; /* First, create a file and an object within the file for the link to * point to. */ file_id = H5Fcreate(HARD_LINK_FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); group_id = H5Gcreate2(file_id, TARGET_GROUP, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); H5Gclose(group_id); /* This is how we create a normal hard link to the group. This * creates a second "name" for the group. */ H5Lcreate_hard(file_id, TARGET_GROUP, file_id, HARD_LINK_NAME, H5P_DEFAULT, H5P_DEFAULT); /* To do the same thing using a user-defined link, we first have to * register the link class we defined. */ H5Lregister(UD_hard_class); /* Since hard links link by object address, we'll need to retrieve * the target group's address. We do this by calling H5Lget_info * on a hard link to the object. */ H5Lget_info2(file_id, TARGET_GROUP, &li, H5P_DEFAULT); /* Now create a user-defined link. We give it the group's address * as its udata. */ H5Lcreate_ud(file_id, UD_HARD_LINK_NAME, (H5L_type_t)UD_HARD_CLASS, &(li.u.token), sizeof(H5O_token_t), H5P_DEFAULT, H5P_DEFAULT); /* The UD hard link has now incremented the group's reference count * like a normal hard link would. This means that we can unlink the * other two links to that group and it won't be deleted until the * UD hard link is deleted. */ H5Ldelete(file_id, TARGET_GROUP, H5P_DEFAULT); H5Ldelete(file_id, HARD_LINK_NAME, H5P_DEFAULT); /* The group is still accessible through the UD hard link. If this were * a soft link instead, the object would have been deleted when the last * hard link to it was unlinked. */ group_id = H5Gopen2(file_id, UD_HARD_LINK_NAME, H5P_DEFAULT); /* The group is now open normally. Don't forget to close it! */ H5Gclose(group_id); /* Removing the user-defined hard link will delete the group. */ H5Ldelete(file_id, UD_HARD_LINK_NAME, H5P_DEFAULT); H5Fclose(file_id); } /* Callbacks for User-defined hard links. */ /* UD_hard_create * The most important thing this callback does is to increment the reference * count on the target object. Without this step, the object could be * deleted while this link still pointed to it, resulting in possible data * corruption! * The create callback also checks the arguments used to create this link. * If this function returns a negative value, the call to H5Lcreate_ud() * will also return failure and the link will not be created. */ static herr_t UD_hard_create(const char *link_name, hid_t loc_group, const void *udata, size_t udata_size, hid_t lcpl_id) { H5O_token_t token; hid_t target_obj = H5I_INVALID_HID; herr_t ret_value = 0; /* Make sure that the address passed in looks valid */ if (udata_size != sizeof(H5O_token_t)) { ret_value = -1; goto done; } token = *((const H5O_token_t *)udata); //! [H5Oopen_by_token_snip] /* Open the object this link points to so that we can increment * its reference count. This also ensures that the token passed * in points to a real object (although this check is not perfect!) */ target_obj = H5Oopen_by_token(loc_group, token); //! [H5Oopen_by_token_snip] if (target_obj < 0) { ret_value = -1; goto done; } /* Increment the reference count of the target object */ if (H5Oincr_refcount(target_obj) < 0) { ret_value = -1; goto done; } done: /* Close the target object if we opened it */ if (target_obj >= 0) H5Oclose(target_obj); return ret_value; } /* UD_hard_delete * Since the creation function increments the object's reference count, it's * important to decrement it again when the link is deleted. */ static herr_t UD_hard_delete(const char *link_name, hid_t loc_group, const void *udata, size_t udata_size) { H5O_token_t token; hid_t target_obj = H5I_INVALID_HID; herr_t ret_value = 0; /* Sanity check; we have already verified the udata's size in the creation * callback. */ if (udata_size != sizeof(H5O_token_t)) { ret_value = -1; goto done; } token = *((const H5O_token_t *)udata); /* Open the object this link points to */ target_obj = H5Oopen_by_token(loc_group, token); if (target_obj < 0) { ret_value = -1; goto done; } /* Decrement the reference count of the target object */ if (H5Odecr_refcount(target_obj) < 0) { ret_value = -1; goto done; } done: /* Close the target object if we opened it */ if (target_obj >= 0) H5Oclose(target_obj); return ret_value; } /* UD_hard_traverse * The actual traversal function simply needs to open the correct object and * return its ID. */ static hid_t UD_hard_traverse(const char *link_name, hid_t cur_group, const void *udata, size_t udata_size, hid_t lapl_id, hid_t dxpl_id) { H5O_token_t token; hid_t ret_value = H5I_INVALID_HID; /* Sanity check; we have already verified the udata's size in the creation * callback. */ if (udata_size != sizeof(H5O_token_t)) return H5I_INVALID_HID; token = *((const H5O_token_t *)udata); /* Open the object by token. If H5Oopen_by_token fails, ret_value will * be negative to indicate that the traversal function failed. */ ret_value = H5Oopen_by_token(cur_group, token); return ret_value; } /* Plist example * * Create a new class of user-defined links that open objects within a file * based on a value passed in through a link access property list. * * Group, dataset, and datatype access property lists all inherit from link * access property lists, so they can be used instead of LAPLs. */ /* We need to define the callback functions that this link type will use. * These are defined after the example below. * These links have no udata, so they don't need a query function. */ static hid_t UD_plist_traverse(const char *link_name, hid_t cur_group, const void *udata, size_t udata_size, hid_t lapl_id, hid_t dxpl_id); static void plist_link_example(void) { hid_t file_id; hid_t group_id, group2_id; hid_t gapl_id; char *path = NULL; /* Define the link class that we'll use to register "plist * links" using the callback we defined above. * A link class can have NULL for any callback except its traverse * callback. */ const H5L_class_t UD_plist_class[1] = {{ H5L_LINK_CLASS_T_VERS, /* Version number for this struct. * This field is always H5L_LINK_CLASS_T_VERS */ (H5L_type_t)UD_PLIST_CLASS, /* Link class id number. This can be any * value between H5L_TYPE_UD_MIN (64) and * H5L_TYPE_MAX (255). It should be a * value that isn't already being used by * another kind of link. We'll use 67. */ "UD_plist_link", /* Link class name for debugging */ NULL, /* Creation callback */ NULL, /* Move callback */ NULL, /* Copy callback */ UD_plist_traverse, /* The actual traversal function */ NULL, /* Deletion callback */ NULL /* Query callback */ }}; /* First, create a file and two objects within the file for the link to * point to. */ file_id = H5Fcreate(HARD_LINK_FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); group_id = H5Gcreate2(file_id, "group_1", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); H5Gclose(group_id); group_id = H5Gcreate2(file_id, "group_1/group_2", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); H5Gclose(group_id); /* Register "plist links" and create one. It has no udata at all. */ H5Lregister(UD_plist_class); H5Lcreate_ud(file_id, "plist_link", (H5L_type_t)UD_PLIST_CLASS, NULL, 0, H5P_DEFAULT, H5P_DEFAULT); /* Create a group access property list to pass in the target for the * plist link. */ gapl_id = H5Pcreate(H5P_GROUP_ACCESS); /* There is no HDF5 API for setting the property that controls these * links, so we have to add the property manually */ H5Pinsert2(gapl_id, PLIST_LINK_PROP, sizeof(const char *), &(path), NULL, NULL, NULL, NULL, NULL, NULL); /* Set the property to point to the first group. */ path = "group_1"; H5Pset(gapl_id, PLIST_LINK_PROP, &path); /* Open the first group through the plist link using the GAPL we just * created */ group_id = H5Gopen2(file_id, "plist_link", gapl_id); /* If we change the value set on the property list, it will change where * the plist link points. */ path = "group_1/group_2"; H5Pset(gapl_id, PLIST_LINK_PROP, &path); group2_id = H5Gopen2(file_id, "plist_link", gapl_id); /* group_id points to group_1 and group2_id points to group_2, both opened * through the same link. * Using more than one of this type of link could quickly become confusing, * since they will all use the same property list; however, there is * nothing to prevent the links from changing the property list in their * traverse callbacks. */ /* Clean up */ H5Pclose(gapl_id); H5Gclose(group_id); H5Gclose(group2_id); H5Fclose(file_id); } /* Traversal callback for User-defined plist links. */ /* UD_plist_traverse * Open a path passed in through the property list. */ static hid_t UD_plist_traverse(const char *link_name, hid_t cur_group, const void *udata, size_t udata_size, hid_t lapl_id, hid_t dxpl_id) { char *path; hid_t ret_value = H5I_INVALID_HID; /* If the link property isn't set or can't be found, traversal fails. */ if (H5Pexist(lapl_id, PLIST_LINK_PROP) < 0) goto error; if (H5Pget(lapl_id, PLIST_LINK_PROP, &path) < 0) goto error; /* Open the object by address. If H5Oopen_by_addr fails, ret_value will * be negative to indicate that the traversal function failed. */ ret_value = H5Oopen(cur_group, path, lapl_id); return ret_value; error: return H5I_INVALID_HID; } /* Main function * * Invokes the example functions. */ int main(void) { printf("Testing basic external links.\n"); extlink_example(); printf("Testing external link prefixes.\n"); extlink_prefix_example(); printf("Testing user-defined soft links.\n"); soft_link_example(); printf("Testing user-defined hard links.\n"); hard_link_example(); printf("Testing user-defined property list links.\n"); plist_link_example(); return 0; }