summaryrefslogtreecommitdiffstats
path: root/examples/h5_extlink.c
blob: 4508fb67cc5ef3ddfe3bd6272377f89d0c079c83 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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_info_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_info(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.address), sizeof(li.u.address),
                 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)
{
    haddr_t addr;
    hid_t   target_obj = -1;
    herr_t  ret_value  = 0;

    /* Make sure that the address passed in looks valid */
    if (udata_size != sizeof(haddr_t)) {
        ret_value = -1;
        goto done;
    }

    addr = *((const haddr_t *)udata);

    //! [H5Oopen_by_addr_snip]

    /* Open the object this link points to so that we can increment
     * its reference count. This also ensures that the address passed
     * in points to a real object (although this check is not perfect!) */
    target_obj = H5Oopen_by_addr(loc_group, addr);

    //! [H5Oopen_by_addr_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)
{
    haddr_t addr;
    hid_t   target_obj = -1;
    herr_t  ret_value  = 0;

    /* Sanity check; we have already verified the udata's size in the creation
     * callback.
     */
    if (udata_size != sizeof(haddr_t)) {
        ret_value = -1;
        goto done;
    }

    addr = *((const haddr_t *)udata);

    /* Open the object this link points to */
    target_obj = H5Oopen_by_addr(loc_group, addr);
    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)
{
    haddr_t addr;
    hid_t   ret_value = -1;

    /* Sanity check; we have already verified the udata's size in the creation
     * callback.
     */
    if (udata_size != sizeof(haddr_t))
        return -1;

    addr = *((const haddr_t *)udata);

    /* 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_by_addr(cur_group, addr);

    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;
}