/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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 files COPYING and Copyright.html. COPYING can be found at the root * * of the source code distribution tree; Copyright.html can be found at the * * root level of an installed copy of the electronic HDF5 document set and * * is linked from the top-level documents page. It can also be found at * * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * * access to either file, you may request a copy from help@hdfgroup.org. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * Programmer: Robb Matzke * Wednesday, April 8, 1998 */ #include "h5test.h" const char *FILENAME[] = { "big", "sec2", "stdio", NULL }; #define DNAME "big.data" #define WRT_N 50 #define WRT_SIZE 4*1024 #define FAMILY_SIZE 1024*1024*1024 /* Define big file as 2GB */ #define BIG_FILE (off_t)0x80000000UL #define MAX_TRIES 100 #if H5_SIZEOF_LONG_LONG >= 8 # define GB8LL ((unsigned long_long)8*1024*1024*1024) #else # define GB8LL 0 /*cannot do the test*/ #endif /* Protocols */ static void usage(void); /* Array used to record all addresses at which data has been written */ /* so far. Used to prevent overlapping writes. */ static hsize_t values_used[WRT_N]; /*------------------------------------------------------------------------- * Function: randll * * Purpose: Create a random long_long value. * Ensures that a write at this value doesn't overlap any * previous write. * * Return: Success: Random value * * Failure: Random value which overlaps another write * * Programmer: Robb Matzke * Tuesday, November 24, 1998 * * Modifications: * *------------------------------------------------------------------------- */ static hsize_t randll(hsize_t limit, int current_index) { hsize_t acc; int overlap = 1; int i; int tries = 0; /* Generate up to MAX_TRIES random numbers until one of them */ /* does not overlap with any previous writes */ while(overlap != 0 && tries < MAX_TRIES) { acc = rand (); acc *= rand (); acc = acc % limit; overlap = 0; for(i = 0; i < current_index; i++) { if((acc >= values_used[i]) && (acc < values_used[i]+WRT_SIZE)) overlap = 1; if((acc+WRT_SIZE >= values_used[i]) && (acc+WRT_SIZE < values_used[i]+WRT_SIZE)) overlap = 1; } tries++; } values_used[current_index]=acc; return acc; } /*------------------------------------------------------------------------- * Function: is_sparse * * Purpose: Determines if the file system of the current working * directory supports holes. * * Return: Success: Non-zero if holes are supported; zero * otherwise. * * Failure: zero * * Programmer: Robb Matzke * Wednesday, July 15, 1998 * * Modifications: * *------------------------------------------------------------------------- */ static int is_sparse(void) { int fd; h5_stat_t sb; if ((fd=HDopen("x.h5", O_RDWR|O_TRUNC|O_CREAT, 0666)) < 0) return 0; if (HDlseek(fd, (off_t)(1024*1024), SEEK_SET)!=1024*1024) return 0; if (5!=HDwrite(fd, "hello", (size_t)5)) return 0; if (HDclose(fd) < 0) return 0; if (HDstat("x.h5", &sb) < 0) return 0; if (HDunlink("x.h5") < 0) return 0; #ifdef H5_HAVE_STAT_ST_BLOCKS return ((unsigned long)sb.st_blocks*512 < (unsigned long)sb.st_size); #else return (0); #endif } /*------------------------------------------------------------------------- * Function: supports_big * * Purpose: Determines if the file system of the current working * directory supports big files. * * Return: Success: Non-zero if big files are supported; zero * otherwise. * * Failure: zero * * Programmer: Raymond Lu * Wednesday, April 18, 2007 * * Modifications: * *------------------------------------------------------------------------- */ static int supports_big(void) { int fd; if ((fd=HDopen("y.h5", O_RDWR|O_TRUNC|O_CREAT, 0666)) < 0) return 0; /* Write a few bytes at 2GB */ if (HDlseek(fd, BIG_FILE, SEEK_SET)!=BIG_FILE) return 0; if (5!=HDwrite(fd, "hello", (size_t)5)) return 0; /* Write a few bytes at 4GB */ if (HDlseek(fd, 2*BIG_FILE, SEEK_SET) != 2*BIG_FILE) return 0; if (5!=HDwrite(fd, "hello", (size_t)5)) return 0; if (HDclose(fd) < 0) return 0; if (HDunlink("y.h5") < 0) return 0; return (1); } /*------------------------------------------------------------------------- * Function: enough_room * * Purpose: Tries to create a bunch of sparse files to see if quotas will * get in the way. Some systems also have problems opening * enough files and we'll check that too. * * Return: Success: Non-zero * * Failure: zero * * Programmer: Robb Matzke * Thursday, August 6, 1998 * * Modifications: * *------------------------------------------------------------------------- */ static int enough_room(hid_t fapl) { int ret_value=0; int fd[68]; size_t i, size = (size_t)1 << 30; char filename[1024], name[1024]; /* Initialize file descriptors */ for (i=0; i=0; i++) { HDsnprintf(name, sizeof name, filename, i); if(HDclose(fd[i]) < 0) ret_value=0; HDunlink(name); } return ret_value; } /*------------------------------------------------------------------------- * Function: writer * * Purpose: Creates a *big* dataset. * * Return: Success: 0 * * Failure: >0 * * Programmer: Robb Matzke * Wednesday, April 8, 1998 * * Modifications: * Robb Matzke, 15 Jul 1998 * Addresses are written to the file DNAME instead of stdout. * *------------------------------------------------------------------------- */ static int writer (char* filename, hid_t fapl, int wrt_n) { hsize_t size1[4] = {8, 1024, 1024, 1024}; hsize_t size2[1] = {GB8LL}; hsize_t hs_start[1]; hsize_t hs_size[1]; hid_t file=-1, space1=-1, space2=-1, mem_space=-1, d1=-1, d2=-1; int *buf = (int*)malloc (sizeof(int) * WRT_SIZE); int i, j; FILE *out = fopen(DNAME, "w"); hid_t dcpl; TESTING("large dataset write"); /* * We might be on a machine that has 32-bit files, so create an HDF5 file * which is a family of files. Each member of the family will be 1GB */ if ((file=H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0) { goto error; } /* Create simple data spaces according to the size specified above. */ if ((space1 = H5Screate_simple (4, size1, size1)) < 0 || (space2 = H5Screate_simple (1, size2, size2)) < 0) { goto error; } /* Create the datasets */ /* * The fix below is provided for bug#921 * H5Dcreate with H5P_DEFAULT creation properties * will create a set of solid 1GB files; test will crash if quotas are enforced * or it will take some time to write a file. * We should create a dataset allocating space late and never writing fill values. * EIP 4/8/03 if((d1 = H5Dcreate2(file, "d1", H5T_NATIVE_INT, space1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0 || (d2 = H5Dcreate2(file, "d2", H5T_NATIVE_INT, space2, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) { goto error; } */ dcpl = H5Pcreate(H5P_DATASET_CREATE); H5Pset_alloc_time(dcpl, H5D_ALLOC_TIME_LATE); H5Pset_fill_time(dcpl, H5D_FILL_TIME_NEVER); if((d1 = H5Dcreate2(file, "d1", H5T_NATIVE_INT, space1, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0 || (d2 = H5Dcreate2(file, "d2", H5T_NATIVE_INT, space2, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) { goto error; } /* Write some things to them randomly */ hs_size[0] = WRT_SIZE; if ((mem_space = H5Screate_simple (1, hs_size, hs_size)) < 0) goto error; for (i=0; i0 * * Programmer: Robb Matzke * Friday, April 10, 1998 * * Modifications: * *------------------------------------------------------------------------- */ static int reader(char *filename, hid_t fapl) { FILE *script = NULL; hid_t file = -1, mspace = -1, fspace = -1, d2 = -1; char ln[128], *s; hsize_t hs_offset[1]; hsize_t hs_size[1] = {WRT_SIZE}; int *buf = (int *)malloc(sizeof(int) * WRT_SIZE); int i, j, zero, wrong, nerrors = 0; /* Open script file */ script = fopen(DNAME, "r"); /* Open HDF5 file */ if((file = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0) goto error; /* Open the dataset */ if((d2 = H5Dopen2(file, "d2", H5P_DEFAULT)) < 0) goto error; if((fspace = H5Dget_space(d2)) < 0) goto error; /* Describe `buf' */ if((mspace = H5Screate_simple(1, hs_size, hs_size)) < 0) goto error; /* Read each region */ while(fgets(ln, (int)sizeof(ln), script)) { if('#' != ln[0]) break; i = (int)strtol(ln + 1, &s, 10); hs_offset[0] = HDstrtoll(s, NULL, 0); HDfprintf(stdout, "#%03d 0x%016Hx%47s", i, hs_offset[0], ""); fflush(stdout); if(H5Sselect_hyperslab(fspace, H5S_SELECT_SET, hs_offset, NULL, hs_size, NULL) < 0) goto error; if(H5Dread(d2, H5T_NATIVE_INT, mspace, fspace, H5P_DEFAULT, buf) < 0) goto error; /* Check */ for(j = zero = wrong = 0; j < WRT_SIZE; j++) { if(0 == buf[j]) zero++; else if(buf[j] != i + 1) wrong++; } if(zero) { H5_FAILED(); printf(" %d zero%s\n", zero, 1 == zero ? "" : "s"); } else if(wrong) { SKIPPED(); puts(" Possible overlap with another region."); nerrors++; } else { PASSED(); } } if(H5Dclose(d2) < 0) goto error; if(H5Sclose(mspace) < 0) goto error; if(H5Sclose(fspace) < 0) goto error; if(H5Fclose(file) < 0) goto error; free(buf); fclose(script); return nerrors; error: H5E_BEGIN_TRY { H5Dclose(d2); H5Sclose(mspace); H5Sclose(fspace); H5Fclose(file); } H5E_END_TRY; if(buf) free(buf); if(script) fclose(script); return 1; } /*------------------------------------------------------------------------- * Function: usage * * Purpose: Print command usage * * Return: void * * Programmer: Albert Chent * Mar 28, 2002 * * Modifications: * *------------------------------------------------------------------------- */ static void usage(void) { HDfprintf(stdout, "Usage: big [-h] [-c] [-fsize }\n" "\t-h\tPrint the help page\n" "\t-c\tFile system Checking skipped. Caution: this test generates\n" "\t\tmany big files and may fill up the file system.\n" "\t-fsize\tChange family size default to where is\n" "\t\ta positive float point number. Default value is %Hu.\n" "Examples:\n" "\tbig -fsize 2.1e9 \t# test with file size just under 2GB\n" "\tbig -fsize 2.2e9 \t# test with file size just above 2GB\n" "\tBe sure the file system can support the file size requested\n" , (hsize_t)FAMILY_SIZE); } /*------------------------------------------------------------------------- * Function: main * * Purpose: * * Return: Success: * * Failure: * * Programmer: Robb Matzke * Friday, April 10, 1998 * * Modifications: * Albert Cheng, 2002/03/28 * Added command option -fsize. * Albert Cheng, 2002/04/19 * Added command option -c. * * Raymond Lu, 2007/05/25 * Added similar tests for SEC2 and STDIO drivers. * *------------------------------------------------------------------------- */ int main (int ac, char **av) { hid_t fapl=-1; hsize_t family_size; hsize_t family_size_def; /* default family file size */ double family_size_def_dbl; /* default family file size */ int cflag=1; /* check file system before test */ char filename[1024]; /* parameters setup */ family_size_def = FAMILY_SIZE; while (--ac > 0){ av++; if (strcmp("-fsize", *av)==0){ /* specify a different family file size */ ac--; av++; if (ac > 0){ family_size_def_dbl = atof(*av); H5_ASSIGN_OVERFLOW(family_size_def,family_size_def_dbl,double,hsize_t); if (family_size_def <= 0) family_size_def = (hsize_t)FAMILY_SIZE; } else{ printf("***Missing fsize value***\n"); usage(); return 1; } } else if (strcmp("-c", *av)==0){ /* turn off file system check before test */ cflag=0; } else if (strcmp("-h", *av)==0){ usage(); return 0; }else{ usage(); return 1; } } /* Reset library */ h5_reset(); fapl = h5_fileaccess(); /* Test big file with the family driver */ puts("Testing big file with the Family Driver "); if (H5FD_FAMILY!=H5Pget_driver(fapl)) { HDfprintf(stdout, "Changing file drivers to the family driver, %Hu bytes each\n", family_size_def); if (H5Pset_fapl_family(fapl, family_size_def, H5P_DEFAULT) < 0) goto error; } else if (H5Pget_fapl_family(fapl, &family_size, NULL) < 0) { goto error; } else if (family_size!=family_size_def) { HDfprintf(stdout, "Changing family member size from %Hu to %Hu\n", family_size, family_size_def); if (H5Pset_fapl_family(fapl, family_size_def, H5P_DEFAULT) < 0) goto error; } if (cflag){ /* * We shouldn't run this test if the file system doesn't support holes * because we would generate multi-gigabyte files. */ puts("Checking if file system is adequate for this test..."); if (sizeof(long_long)<8 || 0==GB8LL) { puts("Test skipped because sizeof(long_long) is too small. This"); puts("hardware apparently doesn't support 64-bit integer types."); usage(); goto quit; } if (!is_sparse()) { puts("Test skipped because file system does not support holes."); usage(); goto quit; } if (!enough_room(fapl)) { puts("Test skipped because of quota (file size or num open files)."); usage(); goto quit; } if (sizeof(hsize_t)<=4) { puts("Test skipped because the hdf5 library was configured with the"); puts("--disable-hsizet flag in order to work around a compiler bug."); usage(); goto quit; } } /* Do the test with the Family Driver */ h5_fixname(FILENAME[0], fapl, filename, sizeof filename); if (writer(filename, fapl, WRT_N)) goto error; if (reader(filename, fapl)) goto error; puts("Test passed with the Family Driver."); /* * We shouldn't run this test if the file system doesn't support big files * because we would generate multi-gigabyte files. */ puts("\nChecking if file system supports big files..."); if (!supports_big()) { puts("Tests for sec2 and stdio are skipped because file system does not support big files."); usage(); goto quit; } /* Clean up the test file */ if (h5_cleanup(FILENAME, fapl)) remove(DNAME); /* Test big file with the SEC2 driver */ puts("Testing big file with the SEC2 Driver "); fapl = h5_fileaccess(); if(H5Pset_fapl_sec2(fapl) < 0) HDmemset(filename, 0, sizeof(filename)); h5_fixname(FILENAME[1], fapl, filename, sizeof filename); if (writer(filename, fapl, WRT_N)) goto error; if (reader(filename, fapl)) goto error; puts("Test passed with the SEC2 Driver."); #ifdef H5_HAVE_FSEEKO /* Clean up the test file */ if (h5_cleanup(FILENAME, fapl)) remove(DNAME); /* Test big file with the STDIO driver only if fseeko is supported, * because the OFFSET parameter of fseek has the type LONG, not big * enough to support big files. */ puts("\nTesting big file with the STDIO Driver "); fapl = h5_fileaccess(); if(H5Pset_fapl_stdio(fapl) < 0) HDmemset(filename, 0, sizeof(filename)); h5_fixname(FILENAME[2], fapl, filename, sizeof filename); if (writer(filename, fapl, WRT_N)) goto error; if (reader(filename, fapl)) goto error; puts("Test passed with the STDIO Driver."); #endif quit: /* End with normal exit code */ if (h5_cleanup(FILENAME, fapl)) remove(DNAME); return 0; error: if (fapl>=0) H5Pclose(fapl); puts("*** TEST FAILED ***"); return 1; } 'n296' href='#n296'>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 664 665 666 667 668 669 670 671 672 673
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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 files COPYING and Copyright.html.  COPYING can be found at the root   *
 * of the source code distribution tree; Copyright.html can be found at the  *
 * root level of an installed copy of the electronic HDF5 document set and   *
 * is linked from the top-level documents page.  It can also be found at     *
 * http://hdfgroup.org/HDF5/doc/Copyright.html.  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);

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 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_soft_class[1] = {{
        H5L_LINK_CLASS_T_VERS,      /* Version number for this struct.
                                     * This field is always H5L_LINK_CLASS_T_VERS */
        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, 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)
{
    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);

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 */
        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, 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);

    /* 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);
    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)
{
    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);

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 */
        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", 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)
{
    char *        path;
    hid_t         ret_value = -1;

    /* 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 -1;
}



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