text: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
   * Copyright by The HDF Group.                                               *
   * Copyright by the Board of Trustees of the University of Illinois.         *
   * 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 <string.h>
  
  #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 = *((H5O_token_t *)udata);
  
      /* 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);
      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 = *((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 = *((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;
  }
  