summaryrefslogtreecommitdiffstats
path: root/src/H5Iprivate.h
Commit message (Expand)AuthorAgeFilesLines
* Convert hbool_t --> bool in src (#3496)Dana Robinson2023-09-051-7/+6
* Update copyright headers (#2184)Larry Knox2022-11-011-1/+0
* Develop clang 13 format (#1933)Allen Byrne2022-07-261-4/+4
* Combo set of async and other changes (#161)Quincey Koziol2021-06-021-1/+2
* Fixed many -Wreserved-id-macro warnings by fixing header guard spelling (#361)Sean McBride2021-02-231-3/+3
* Update license url (#332)Larry Knox2021-02-171-1/+1
* Bring async branch to develop (#166)Quincey Koziol2020-12-141-0/+2
* Minor/id code cleanup (#114)Dana Robinson2020-11-211-2/+2
* Clang-format of source filesAllen Byrne2020-09-301-33/+30
* Converted H5O MD cache cork calls to use the VOL.Dana Robinson2019-09-271-0/+1
* Updates to the VOL ID and object API calls.Dana Robinson2018-10-171-6/+20
* VOL FEATUREDana Robinson2018-10-101-1/+3
* The H5I debug ID dump function is now always available insteadDana Robinson2018-10-061-0/+3
* Normalization with vol_integration (property lists, file drivers,Dana Robinson2018-09-211-1/+0
* Normalization with the vol_integration branch.Dana Robinson2018-06-061-2/+2
* Merge pull request #426 in HDFFV/hdf5 from ~LRKNOX/hdf5_lrk:hdf5_1_10 to hdf5...Larry Knox2017-04-251-6/+4
* [svn-r30075] Description:Quincey Koziol2016-06-141-0/+1
* [svn-r25496] Description:Quincey Koziol2014-07-301-1/+1
* [svn-r24255] Description:Quincey Koziol2013-10-041-5/+0
* [svn-r23685] Issue 8380 H5Zunregister caused seg fault. I simplied the itera...Raymond Lu2013-05-081-1/+0
* [svn-r23656] Issue 8380 H5Zunregister caused seg fault: I added some protecti...Raymond Lu2013-04-291-0/+1
* [svn-r22582] Description:Quincey Koziol2012-07-161-20/+36
* [svn-r22219] Description:Quincey Koziol2012-03-311-1/+1
* [svn-r19398] Descriptino:Quincey Koziol2010-09-161-1/+3
* [svn-r19363] When mandatory filter failed to write data chunks, the datasetRaymond Lu2010-09-091-1/+1
* [svn-r18346] Description:Quincey Koziol2010-02-271-0/+1
* [svn-r17230] Description:Quincey Koziol2009-07-231-1/+1
* [svn-r15796] Purpose: Close bug #1322Neil Fortner2008-10-071-1/+1
* [svn-r15485] Purpose: Allow library to shut down properly when objects have r...Neil Fortner2008-08-191-6/+6
* [svn-r13253] Updated all C and C++ style source code files with the THG copyr...Albert Cheng2007-02-071-2/+3
* [svn-r12852] Add the declaration of H5I_get_file_id to avoid a bad windows wa...MuQun Yang2006-11-031-0/+1
* [svn-r12842] Description:Quincey Koziol2006-11-021-1/+2
* [svn-r11093] Purpose:Quincey Koziol2005-07-211-0/+1
* [svn-r8892] Purpose:Quincey Koziol2004-07-161-1/+0
* [svn-r8879] Raymond Lu2004-07-141-0/+2
* [svn-r8707] Changed the way HDF5 handles hid_t's and added API functions to a...James Laird2004-06-181-20/+14
* [svn-r7988] Purpose:Quincey Koziol2003-12-291-1/+0
* [svn-r7984] Purpose: bug fixRaymond Lu2003-12-291-0/+1
* [svn-r7871] Purpose:Quincey Koziol2003-11-211-1/+0
* [svn-r7868] Purpose: new featureRaymond Lu2003-11-211-0/+1
* [svn-r7803] Purpose:Quincey Koziol2003-10-311-1/+0
* [svn-r7801] Purpose: added featureRaymond Lu2003-10-311-0/+1
* [svn-r7786] Purpose:Quincey Koziol2003-10-291-1/+0
* [svn-r7784] *** empty log message ***Raymond Lu2003-10-291-0/+1
* [svn-r7302] Purpose:Quincey Koziol2003-08-081-1/+0
* [svn-r7225] Purpose: error API gradual checkinRaymond Lu2003-07-141-3/+3
* [svn-r7212] Purpose: New error API design is being checked gradually.Raymond Lu2003-07-111-0/+3
* [svn-r7071] Purpose:Quincey Koziol2003-06-201-3/+2
* [svn-r6878] Purpose:Quincey Koziol2003-05-151-71/+2
* [svn-r6546] Purpose:Bill Wendling2003-03-311-11/+13
' href='#n670'>670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the COPYING file, which can be found at the root of the source code       *
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*
 * Onion Virtual File Driver (VFD)
 *
 * Purpose:     Provide in-file provenance and revision/version control.
 */

/* This source code file is part of the H5FD driver module */
#include "H5FDdrvr_module.h"

#include "H5private.h"      /* Generic Functions           */
#include "H5Eprivate.h"     /* Error handling              */
#include "H5Fprivate.h"     /* Files                       */
#include "H5FDprivate.h"    /* File drivers                */
#include "H5FDonion.h"      /* Onion file driver           */
#include "H5FDonion_priv.h" /* Onion file driver internals */
#include "H5FDsec2.h"       /* Sec2 file driver         */
#include "H5FLprivate.h"    /* Free Lists                  */
#include "H5Iprivate.h"     /* IDs                         */
#include "H5MMprivate.h"    /* Memory management           */

/* The driver identification number, initialized at runtime */
static hid_t H5FD_ONION_g = 0;

/******************************************************************************
 *
 * Structure:   H5FD_onion_t
 *
 * Purpose:     Store information required to manage an onionized file.
 *              This structure is created when such a file is "opened" and
 *              discarded when it is "closed".
 *
 * pu
 *
 *      Instance of H5FD_t which contains fields common to all VFDs.
 *      It must be the first item in this structure, since at higher levels,
 *      this structure will be treated as an instance of H5FD_t.
 *
 * fa
 *
 *      Instance of `H5FD_onion_fapl_info_t` containing the configuration data
 *      needed to "open" the HDF5 file.
 *
 * original_file
 *
 *      VFD handle for the original HDF5 file.
 *
 * onion_file
 *
 *      VFD handle for the onion file.
 *      NULL if not set to use the single, separate storage target.
 *
 * recovery_file
 *
 *      VFD handle for the history recovery file. This file is a backup of
 *      the existing history when an existing onion file is opened in RW mode.
 *
 * recovery_file_name
 *
 *      String allocated and populated on file-open in write mode and freed on
 *      file-close, stores the path/name of the 'recovery' file. The file
 *      created at this location is to be removed upon successful file-close
 *      from write mode.
 *
 * is_open_rw
 *
 *      Remember whether the file was opened in a read-write mode.
 *
 * align_history_on_pages
 *
 *      Remember whether onion-writes must be aligned to page boundaries.
 *
 * header
 *
 *      In-memory copy of the onion history data header.
 *
 * history
 *
 *      In-memory copy of the onion history.
 *
 * curr_rev_record
 *
 *      Record for the currently open revision.
 *
 * rev_index
 *
 *      Index for maintaining modified pages (RW mode only).
 *      Pointer is NULL when the file is not opened in write mode.
 *      Pointer is allocated on open and must be freed on close.
 *      Contents must be merged with the revision record's archival index prior
 *      to commitment of history to backing store.
 *
 * onion_eof
 *
 *      Last byte in the onion file.
 *
 * origin_eof
 *
 *     Size of the original HDF5 file.
 *
 * logical_eoa
 *
 *     Address of first byte past addressed space in the logical 'file'
 *     presented by this VFD.
 *
 * logical_eof
 *
 *     Address of first byte past last byte in the logical 'file' presented
 *     by this VFD.
 *     Must be copied into the revision record on close to write onion data.
 *
 ******************************************************************************
 */
typedef struct H5FD_onion_t {
    H5FD_t                 pub;
    H5FD_onion_fapl_info_t fa;
    bool                   is_open_rw;
    bool                   align_history_on_pages;

    /* Onion-related files */
    H5FD_t *original_file;
    H5FD_t *onion_file;
    H5FD_t *recovery_file;
    char   *recovery_file_name;

    /* Onion data structures */
    H5FD_onion_header_t          header;
    H5FD_onion_history_t         history;
    H5FD_onion_revision_record_t curr_rev_record;
    H5FD_onion_revision_index_t *rev_index;

    /* End of addresses and files */
    haddr_t onion_eof;
    haddr_t origin_eof;
    haddr_t logical_eoa;
    haddr_t logical_eof;
} H5FD_onion_t;

H5FL_DEFINE_STATIC(H5FD_onion_t);

#define MAXADDR (((haddr_t)1 << (8 * sizeof(HDoff_t) - 1)) - 1)

#define H5FD_CTL_GET_NUM_REVISIONS 20001

/* Prototypes */
static herr_t  H5FD__onion_close(H5FD_t *);
static haddr_t H5FD__onion_get_eoa(const H5FD_t *, H5FD_mem_t);
static haddr_t H5FD__onion_get_eof(const H5FD_t *, H5FD_mem_t);
static H5FD_t *H5FD__onion_open(const char *, unsigned int, hid_t, haddr_t);
static herr_t  H5FD__onion_read(H5FD_t *, H5FD_mem_t, hid_t, haddr_t, size_t, void *);
static herr_t  H5FD__onion_set_eoa(H5FD_t *, H5FD_mem_t, haddr_t);
static herr_t  H5FD__onion_term(void);
static herr_t  H5FD__onion_write(H5FD_t *, H5FD_mem_t, hid_t, haddr_t, size_t, const void *);

static herr_t  H5FD__onion_open_rw(H5FD_onion_t *, unsigned int, haddr_t, bool new_open);
static herr_t  H5FD__onion_sb_encode(H5FD_t *_file, char *name /*out*/, unsigned char *buf /*out*/);
static herr_t  H5FD__onion_sb_decode(H5FD_t *_file, const char *name, const unsigned char *buf);
static hsize_t H5FD__onion_sb_size(H5FD_t *_file);
static herr_t  H5FD__onion_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags,
                               const void H5_ATTR_UNUSED *input, void H5_ATTR_UNUSED **output);
static herr_t  H5FD__get_onion_revision_count(H5FD_t *file, uint64_t *revision_count);

/* Temporary */
H5_DLL herr_t H5FD__onion_write_final_history(H5FD_onion_t *file);

static const H5FD_class_t H5FD_onion_g = {
    H5FD_CLASS_VERSION,             /* struct version       */
    H5FD_ONION_VALUE,               /* value                */
    "onion",                        /* name                 */
    MAXADDR,                        /* maxaddr              */
    H5F_CLOSE_WEAK,                 /* fc_degree            */
    H5FD__onion_term,               /* terminate            */
    H5FD__onion_sb_size,            /* sb_size              */
    H5FD__onion_sb_encode,          /* sb_encode            */
    H5FD__onion_sb_decode,          /* sb_decode            */
    sizeof(H5FD_onion_fapl_info_t), /* fapl_size            */
    NULL,                           /* fapl_get             */
    NULL,                           /* fapl_copy            */
    NULL,                           /* fapl_free            */
    0,                              /* dxpl_size            */
    NULL,                           /* dxpl_copy            */
    NULL,                           /* dxpl_free            */
    H5FD__onion_open,               /* open                 */
    H5FD__onion_close,              /* close                */
    NULL,                           /* cmp                  */
    NULL,                           /* query                */
    NULL,                           /* get_type_map         */
    NULL,                           /* alloc                */
    NULL,                           /* free                 */
    H5FD__onion_get_eoa,            /* get_eoa              */
    H5FD__onion_set_eoa,            /* set_eoa              */
    H5FD__onion_get_eof,            /* get_eof              */
    NULL,                           /* get_handle           */
    H5FD__onion_read,               /* read                 */
    H5FD__onion_write,              /* write                */
    NULL,                           /* read_vector          */
    NULL,                           /* write_vector         */
    NULL,                           /* read_selection       */
    NULL,                           /* write_selection      */
    NULL,                           /* flush                */
    NULL,                           /* truncate             */
    NULL,                           /* lock                 */
    NULL,                           /* unlock               */
    NULL,                           /* del                  */
    H5FD__onion_ctl,                /* ctl                  */
    H5FD_FLMAP_DICHOTOMY            /* fl_map               */
};

/*-----------------------------------------------------------------------------
 * Function:    H5FD_onion_init
 *
 * Purpose:     Initialize this driver by registering the driver with the
 *              library.
 *
 * Return:      Success:    The driver ID for the onion driver.
 *              Failure:    Negative
 *-----------------------------------------------------------------------------
 */
hid_t
H5FD_onion_init(void)
{
    hid_t ret_value = H5I_INVALID_HID;

    FUNC_ENTER_NOAPI_NOERR

    if (H5I_VFL != H5I_get_type(H5FD_ONION_g))
        H5FD_ONION_g = H5FD_register(&H5FD_onion_g, sizeof(H5FD_class_t), false);

    /* Set return value */
    ret_value = H5FD_ONION_g;

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD_onion_init() */

/*-----------------------------------------------------------------------------
 * Function:    H5FD__onion_term
 *
 * Purpose:     Shut down the Onion VFD.
 *
 * Returns:     SUCCEED (Can't fail)
 *-----------------------------------------------------------------------------
 */
static herr_t
H5FD__onion_term(void)
{
    FUNC_ENTER_PACKAGE_NOERR

    /* Reset VFL ID */
    H5FD_ONION_g = 0;

    FUNC_LEAVE_NOAPI(SUCCEED)

} /* end H5FD__onion_term() */

/*-----------------------------------------------------------------------------
 * Function:    H5Pget_fapl_onion
 *
 * Purpose:     Copy the Onion configuration information from the FAPL at
 *              `fapl_id` to the destination pointer `fa_out`.
 *
 * Return:      Success: Non-negative value (SUCCEED).
 *              Failure: Negative value (FAIL).
 *-----------------------------------------------------------------------------
 */
herr_t
H5Pget_fapl_onion(hid_t fapl_id, H5FD_onion_fapl_info_t *fa_out)
{
    const H5FD_onion_fapl_info_t *info_ptr  = NULL;
    H5P_genplist_t               *plist     = NULL;
    herr_t                        ret_value = SUCCEED;

    FUNC_ENTER_API(FAIL)
    H5TRACE2("e", "i*!", fapl_id, fa_out);

    if (NULL == fa_out)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "NULL info-out pointer");

    if (NULL == (plist = H5P_object_verify(fapl_id, H5P_FILE_ACCESS)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Not a valid FAPL ID");

    if (H5FD_ONION != H5P_peek_driver(plist))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Incorrect VFL driver");

    if (NULL == (info_ptr = (const H5FD_onion_fapl_info_t *)H5P_peek_driver_info(plist)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "bad VFL driver info");

    H5MM_memcpy(fa_out, info_ptr, sizeof(H5FD_onion_fapl_info_t));

done:
    FUNC_LEAVE_API(ret_value)

} /* end H5Pget_fapl_onion() */

/*-----------------------------------------------------------------------------
 * Function:    H5Pset_fapl_onion
 *
 * Purpose      Set the file access property list at `fapl_id` to use the
 *              Onion virtual file driver with the given configuration.
 *              The info structure may be modified or deleted after this call,
 *              as its contents are copied into the FAPL.
 *
 * Return:      Success: Non-negative value (SUCCEED).
 *              Failure: Negative value (FAIL).
 *-----------------------------------------------------------------------------
 */
herr_t
H5Pset_fapl_onion(hid_t fapl_id, const H5FD_onion_fapl_info_t *fa)
{
    H5P_genplist_t *fapl           = NULL;
    H5P_genplist_t *backing_fapl   = NULL;
    hid_t           backing_vfd_id = H5I_INVALID_HID;
    herr_t          ret_value      = SUCCEED;

    FUNC_ENTER_API(FAIL)
    H5TRACE2("e", "i*!", fapl_id, fa);

    if (NULL == (fapl = H5P_object_verify(fapl_id, H5P_FILE_ACCESS)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Not a valid FAPL ID");
    if (NULL == fa)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "NULL info pointer");
    if (H5FD_ONION_FAPL_INFO_VERSION_CURR != fa->version)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid info version");
    if (!POWER_OF_TWO(fa->page_size))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid info page size");
    if (fa->page_size < 1)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid info page size");

    if (H5P_DEFAULT == fa->backing_fapl_id) {
        if (NULL == (backing_fapl = H5P_object_verify(H5P_FILE_ACCESS_DEFAULT, H5P_FILE_ACCESS)))
            HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid backing fapl id");
    }
    else {
        if (NULL == (backing_fapl = H5P_object_verify(fa->backing_fapl_id, H5P_FILE_ACCESS)))
            HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid backing fapl id");
    }

    /* The only backing fapl that is currently supported is sec2 */
    if ((backing_vfd_id = H5P_peek_driver(backing_fapl)) < 0)
        HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "Can't get VFD from fapl");
    if (backing_vfd_id != H5FD_SEC2)
        HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "Onion VFD only supports sec2 backing store");

    if (H5P_set_driver(fapl, H5FD_ONION, (const void *)fa, NULL) < 0)
        HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "Can't set the onion VFD");

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Pset_fapl_onion() */

/*-------------------------------------------------------------------------
 * Function:    H5FD__onion_sb_size
 *
 * Purpose:     Returns the size of the private information to be stored in
 *              the superblock.
 *
 * Return:      Success:    The super block driver data size
 *              Failure:    never fails
 *-------------------------------------------------------------------------
 */
static hsize_t
H5FD__onion_sb_size(H5FD_t *_file)
{
    H5FD_onion_t *file      = (H5FD_onion_t *)_file;
    hsize_t       ret_value = 0;

    FUNC_ENTER_PACKAGE_NOERR

    /* Sanity check */
    assert(file);
    assert(file->original_file);

    if (file->original_file)
        ret_value = H5FD_sb_size(file->original_file);

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_sb_size */

/*-------------------------------------------------------------------------
 * Function:    H5FD__onion_sb_encode
 *
 * Purpose:     Encodes the superblock information for this driver
 *
 * Return:      SUCCEED/FAIL
 *-------------------------------------------------------------------------
 */
static herr_t
H5FD__onion_sb_encode(H5FD_t *_file, char *name /*out*/, unsigned char *buf /*out*/)
{
    H5FD_onion_t *file      = (H5FD_onion_t *)_file;
    herr_t        ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Sanity check */
    assert(file);
    assert(file->original_file);

    if (file->original_file && H5FD_sb_encode(file->original_file, name, buf) < 0)
        HGOTO_ERROR(H5E_VFL, H5E_CANTENCODE, FAIL, "unable to encode the superblock in R/W file");

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_sb_encode */

/*-------------------------------------------------------------------------
 * Function:    H5FD__onion_sb_decode
 *
 * Purpose:     Decodes the superblock information for this driver
 *
 * Return:      SUCCEED/FAIL
 *-------------------------------------------------------------------------
 */
static herr_t
H5FD__onion_sb_decode(H5FD_t *_file, const char *name, const unsigned char *buf)
{
    H5FD_onion_t *file      = (H5FD_onion_t *)_file;
    herr_t        ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Sanity check */
    assert(file);
    assert(file->original_file);

    if (H5FD_sb_load(file->original_file, name, buf) < 0)
        HGOTO_ERROR(H5E_VFL, H5E_CANTDECODE, FAIL, "unable to decode the superblock in R/W file");

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_sb_decode */

/*-----------------------------------------------------------------------------
 * Write in-memory revision record to appropriate backing file.
 * Update information in other in-memory components.
 *-----------------------------------------------------------------------------
 */
static herr_t
H5FD__onion_commit_new_revision_record(H5FD_onion_t *file)
{
    uint32_t                      checksum  = 0; /* required */
    size_t                        size      = 0;
    haddr_t                       phys_addr = 0; /* offset in history file to record start */
    unsigned char                *buf       = NULL;
    herr_t                        ret_value = SUCCEED;
    H5FD_onion_revision_record_t *rec       = &file->curr_rev_record;
    H5FD_onion_history_t         *history   = &file->history;
    H5FD_onion_record_loc_t      *new_list  = NULL;

    time_t     rawtime;
    struct tm *info;

    FUNC_ENTER_PACKAGE

    HDtime(&rawtime);
    info = HDgmtime(&rawtime);
    strftime(rec->time_of_creation, sizeof(rec->time_of_creation), "%Y%m%dT%H%M%SZ", info);

    rec->logical_eof = file->logical_eof;

    if ((true == file->is_open_rw) && (H5FD__onion_merge_revision_index_into_archival_index(
                                           file->rev_index, &file->curr_rev_record.archival_index) < 0))
        HGOTO_ERROR(H5E_VFL, H5E_INTERNAL, FAIL, "unable to update index to write");

    if (NULL == (buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_REVISION_RECORD + (size_t)rec->comment_size +
                                   (H5FD_ONION_ENCODED_SIZE_INDEX_ENTRY * rec->archival_index.n_entries))))
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate buffer for encoded revision record");

    if (0 == (size = H5FD__onion_revision_record_encode(rec, buf, &checksum)))
        HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "problem encoding revision record");

    phys_addr = file->onion_eof;
    if (H5FD_set_eoa(file->onion_file, H5FD_MEM_DRAW, phys_addr + size) < 0)
        HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "can't modify EOA for new revision record");
    if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, phys_addr, size, buf) < 0)
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "can't write new revision record");

    file->onion_eof = phys_addr + size;
    if (true == file->align_history_on_pages)
        file->onion_eof = (file->onion_eof + (file->header.page_size - 1)) & (~(file->header.page_size - 1));

    /* Update history info to accommodate new revision */

    if (history->n_revisions == 0) {
        unsigned char *ptr = buf; /* reuse buffer space to compute checksum */

        assert(history->record_locs == NULL);
        history->n_revisions = 1;
        if (NULL == (history->record_locs = H5MM_calloc(sizeof(H5FD_onion_record_loc_t))))
            HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate temporary record pointer list");

        history->record_locs[0].phys_addr   = phys_addr;
        history->record_locs[0].record_size = size;
        UINT64ENCODE(ptr, phys_addr);
        UINT64ENCODE(ptr, size);
        history->record_locs[0].checksum = H5_checksum_fletcher32(buf, (size_t)(ptr - buf));
        /* TODO: size-reset belongs where? */
        file->header.history_size += H5FD_ONION_ENCODED_SIZE_RECORD_POINTER;
    } /* end if no extant revisions in history */
    else {
        unsigned char *ptr = buf; /* reuse buffer space to compute checksum */

        assert(history->record_locs != NULL);

        if (NULL == (new_list = H5MM_calloc((history->n_revisions + 1) * sizeof(H5FD_onion_record_loc_t))))
            HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "unable to resize record pointer list");
        H5MM_memcpy(new_list, history->record_locs, sizeof(H5FD_onion_record_loc_t) * history->n_revisions);
        H5MM_xfree(history->record_locs);
        history->record_locs                                   = new_list;
        new_list                                               = NULL;
        history->record_locs[history->n_revisions].phys_addr   = phys_addr;
        history->record_locs[history->n_revisions].record_size = size;
        UINT64ENCODE(ptr, phys_addr);
        UINT64ENCODE(ptr, size);
        history->record_locs[history->n_revisions].checksum =
            H5_checksum_fletcher32(buf, (size_t)(ptr - buf));

        file->header.history_size += H5FD_ONION_ENCODED_SIZE_RECORD_POINTER;
        history->n_revisions += 1;
    } /* end if one or more revisions present in history */

    file->header.history_addr = file->onion_eof;

done:
    H5MM_xfree(buf);
    H5MM_xfree(new_list);

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_commit_new_revision_record() */

/*-----------------------------------------------------------------------------
 * Function:    H5FD__onion_close
 *
 * Purpose:     Close an onionized file
 *
 * Return:      SUCCEED/FAIL
 *-----------------------------------------------------------------------------
 */
static herr_t
H5FD__onion_close(H5FD_t *_file)
{
    H5FD_onion_t *file      = (H5FD_onion_t *)_file;
    herr_t        ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE

    assert(file);

    if (H5FD_ONION_STORE_TARGET_ONION == file->fa.store_target) {

        assert(file->onion_file);

        if (file->is_open_rw) {

            assert(file->recovery_file);

            if (H5FD__onion_commit_new_revision_record(file) < 0)
                HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "Can't write revision record to backing store");

            if (H5FD__onion_write_final_history(file) < 0)
                HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "Can't write history to backing store");

            /* Unset write-lock flag and write header */
            if (file->is_open_rw)
                file->header.flags &= (uint32_t)~H5FD_ONION_HEADER_FLAG_WRITE_LOCK;
            if (H5FD__onion_write_header(&(file->header), file->onion_file) < 0)
                HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "Can't write updated header to backing store");
        }
    }
    else
        HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid history target");

done:

    /* Destroy things as best we can, even if there were earlier errors */
    if (file->original_file)
        if (H5FD_close(file->original_file) < 0)
            HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, FAIL, "can't close backing canon file");
    if (file->onion_file)
        if (H5FD_close(file->onion_file) < 0)
            HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, FAIL, "can't close backing onion file");
    if (file->recovery_file) {
        if (H5FD_close(file->recovery_file) < 0)
            HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, FAIL, "can't close backing recovery file");
        /* TODO: Use the VFD's del callback instead of remove (this requires
         *       storing a copy of the fapl that was used to open it)
         */
        HDremove(file->recovery_file_name);
    }
    if (file->rev_index)
        if (H5FD__onion_revision_index_destroy(file->rev_index) < 0)
            HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, FAIL, "can't close revision index");

    H5MM_xfree(file->recovery_file_name);
    H5MM_xfree(file->history.record_locs);
    H5MM_xfree(file->curr_rev_record.comment);
    H5MM_xfree(file->curr_rev_record.archival_index.list);

    file = H5FL_FREE(H5FD_onion_t, file);

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_close() */

/*-----------------------------------------------------------------------------
 * Function:    H5FD__onion_get_eoa
 *
 * Purpose:     Get end-of-address address.
 *
 * Return:      Address of first byte past the addressed space
 *-----------------------------------------------------------------------------
 */
static haddr_t
H5FD__onion_get_eoa(const H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type)
{
    const H5FD_onion_t *file = (const H5FD_onion_t *)_file;

    FUNC_ENTER_PACKAGE_NOERR

    FUNC_LEAVE_NOAPI(file->logical_eoa)
} /* end H5FD__onion_get_eoa() */

/*-----------------------------------------------------------------------------
 * Function:    H5FD__onion_get_eof
 *
 * Purpose:     Get end-of-file address.
 *
 * Return:      Address of first byte past the file-end.
 *-----------------------------------------------------------------------------
 */
static haddr_t
H5FD__onion_get_eof(const H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type)
{
    const H5FD_onion_t *file = (const H5FD_onion_t *)_file;

    FUNC_ENTER_PACKAGE_NOERR

    FUNC_LEAVE_NOAPI(file->logical_eof)
} /* end H5FD__onion_get_eof() */

/*-----------------------------------------------------------------------------
 * Sanitize the backing FAPL ID
 *-----------------------------------------------------------------------------
 */
static inline hid_t
H5FD__onion_get_legit_fapl_id(hid_t fapl_id)
{
    if (H5P_DEFAULT == fapl_id)
        return H5P_FILE_ACCESS_DEFAULT;
    else if (true == H5P_isa_class(fapl_id, H5P_FILE_ACCESS))
        return fapl_id;
    else
        return H5I_INVALID_HID;
}

/*-----------------------------------------------------------------------------
 * Function:    H5FD_onion_create_truncate_onion
 *
 * Purpose:     Create/truncate HDF5 and onion data for a fresh file
 *
 *      Special open operation required to instantiate the canonical file and
 *      history simultaneously. If successful, the required backing files are
 *      craeated and given initial population on the backing store, and the Onion
 *      virtual file handle is set; open effects a write-mode open.
 *
 *      Cannot create 'template' history and proceed with normal write-mode open,
 *      as this would in effect create an empty first revision, making the history
 *      unintuitive. (create file -> initialize and commit empty first revision
 *      (revision 0); any data written to file during the 'create' open, as seen by
 *      the user, would be in the second revision (revision 1).)
 *
 * Return:      SUCCEED/FAIL
 *-----------------------------------------------------------------------------
 */
static herr_t
H5FD__onion_create_truncate_onion(H5FD_onion_t *file, const char *filename, const char *name_onion,
                                  const char *recovery_file_nameery, unsigned int flags, haddr_t maxaddr)
{
    hid_t                         backing_fapl_id = H5I_INVALID_HID;
    H5FD_onion_header_t          *hdr             = NULL;
    H5FD_onion_history_t         *history         = NULL;
    H5FD_onion_revision_record_t *rec             = NULL;
    unsigned char                *buf             = NULL;
    size_t                        size            = 0;
    herr_t                        ret_value       = SUCCEED;

    FUNC_ENTER_PACKAGE

    assert(file != NULL);

    hdr     = &file->header;
    history = &file->history;
    rec     = &file->curr_rev_record;

    hdr->flags = H5FD_ONION_HEADER_FLAG_WRITE_LOCK;
    if (H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT & file->fa.creation_flags)
        hdr->flags |= H5FD_ONION_HEADER_FLAG_PAGE_ALIGNMENT;

    hdr->origin_eof = 0;

    backing_fapl_id = H5FD__onion_get_legit_fapl_id(file->fa.backing_fapl_id);
    if (H5I_INVALID_HID == backing_fapl_id)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid backing FAPL ID");

    /* Create backing files for onion history */

    if (NULL == (file->original_file = H5FD_open(filename, flags, backing_fapl_id, maxaddr)))
        HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "cannot open the backing file");

    if (NULL == (file->onion_file = H5FD_open(name_onion, flags, backing_fapl_id, maxaddr)))
        HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "cannot open the backing onion file");

    if (NULL == (file->recovery_file = H5FD_open(recovery_file_nameery, flags, backing_fapl_id, maxaddr)))
        HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "cannot open the backing file");

    /* Write "empty" .h5 file contents (signature ONIONEOF) */

    if (H5FD_set_eoa(file->original_file, H5FD_MEM_DRAW, 8) < 0)
        HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "can't extend EOA");
    if (H5FD_write(file->original_file, H5FD_MEM_DRAW, 0, 8, "ONIONEOF") < 0)
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "cannot write header to the backing h5 file");

    /* Write nascent history (with no revisions) to "recovery" */

    if (NULL == (buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_HISTORY)))
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate buffer");
    size = H5FD__onion_history_encode(history, buf, &history->checksum);
    if (H5FD_ONION_ENCODED_SIZE_HISTORY != size)
        HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "can't encode history");
    if (H5FD_set_eoa(file->recovery_file, H5FD_MEM_DRAW, size) < 0)
        HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "can't extend EOA");
    if (H5FD_write(file->recovery_file, H5FD_MEM_DRAW, 0, size, buf) < 0)
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "cannot write history to the backing recovery file");
    hdr->history_size = size; /* record for later use */
    H5MM_xfree(buf);
    buf = NULL;

    /* Write history header with "no" history.
     * Size of the "recovery" history recorded for later use on close.
     */

    if (NULL == (buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_HEADER)))
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate buffer");
    size = H5FD__onion_header_encode(hdr, buf, &hdr->checksum);
    if (H5FD_ONION_ENCODED_SIZE_HEADER != size)
        HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "can't encode history header");
    if (H5FD_set_eoa(file->onion_file, H5FD_MEM_DRAW, size) < 0)
        HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "can't extend EOA");
    if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, 0, size, buf) < 0)
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "cannot write header to the backing onion file");
    file->onion_eof = (haddr_t)size;
    if (true == file->align_history_on_pages)
        file->onion_eof = (file->onion_eof + (hdr->page_size - 1)) & (~(hdr->page_size - 1));

    rec->archival_index.list = NULL;

    if (NULL == (file->rev_index = H5FD__onion_revision_index_init(file->fa.page_size)))
        HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "can't initialize revision index");

done:
    H5MM_xfree(buf);

    if (FAIL == ret_value)
        HDremove(recovery_file_nameery); /* destroy new temp file, if 'twas created */

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_create_truncate_onion() */

static herr_t
H5FD__onion_remove_unused_symbols(char *s)
{
    char *d = s;

    FUNC_ENTER_PACKAGE_NOERR

    do {
        while (*d == '{' || *d == '}' || *d == ' ') {
            ++d;
        }
    } while ((*s++ = *d++));

    FUNC_LEAVE_NOAPI(SUCCEED)
}

static herr_t
H5FD__onion_parse_config_str(const char *config_str, H5FD_onion_fapl_info_t *fa)
{
    char  *config_str_copy = NULL;
    herr_t ret_value       = SUCCEED;

    FUNC_ENTER_PACKAGE

    if (!strcmp(config_str, ""))
        HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "configure string can't be empty");

    /* Initialize to the default values */
    fa->version          = H5FD_ONION_FAPL_INFO_VERSION_CURR;
    fa->backing_fapl_id  = H5P_DEFAULT;
    fa->page_size        = 4;
    fa->store_target     = H5FD_ONION_STORE_TARGET_ONION;
    fa->revision_num     = H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST;
    fa->force_write_open = 0;
    fa->creation_flags   = 0;
    strcpy(fa->comment, "initial comment");

    /* If a single integer is passed in as a string, it's a shortcut for the tools
     * (h5repack, h5diff, h5dump).  Otherwise, the string should have curly brackets,
     * e.g. {revision_num: 2; page_size: 4;}
     */
    if (config_str[0] != '{')
        fa->revision_num = (uint64_t)strtoull(config_str, NULL, 10);
    else {
        char *token1 = NULL, *token2 = NULL;

        /* Duplicate the configure string since strtok will mess with it */
        if (NULL == (config_str_copy = H5MM_strdup(config_str)))
            HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't duplicate configure string");

        /* Remove the curly brackets and space from the configure string */
        H5FD__onion_remove_unused_symbols(config_str_copy);

        /* The configure string can't be empty after removing the curly brackets */
        if (!strcmp(config_str_copy, ""))
            HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "configure string can't be empty");

        token1 = strtok(config_str_copy, ":");
        token2 = strtok(NULL, ";");

        do {
            if (token1 && token2) {
                if (!strcmp(token1, "version")) {
                    if (!strcmp(token2, "H5FD_ONION_FAPL_INFO_VERSION_CURR"))
                        fa->version = H5FD_ONION_FAPL_INFO_VERSION_CURR;
                }
                else if (!strcmp(token1, "backing_fapl_id")) {
                    if (!strcmp(token2, "H5P_DEFAULT"))
                        fa->backing_fapl_id = H5P_DEFAULT;
                    else if (!strcmp(token2, "H5I_INVALID_HID"))
                        fa->backing_fapl_id = H5I_INVALID_HID;
                    else
                        fa->backing_fapl_id = strtoll(token2, NULL, 10);
                }
                else if (!strcmp(token1, "page_size")) {
                    fa->page_size = (uint32_t)strtoul(token2, NULL, 10);
                }
                else if (!strcmp(token1, "revision_num")) {
                    if (!strcmp(token2, "H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST"))
                        fa->revision_num = H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST;
                    else
                        fa->revision_num = (uint64_t)strtoull(token2, NULL, 10);
                }
                else if (!strcmp(token1, "force_write_open")) {
                    fa->force_write_open = (uint8_t)strtoul(token2, NULL, 10);
                }
                else if (!strcmp(token1, "creation_flags")) {
                    fa->creation_flags = (uint8_t)strtoul(token2, NULL, 10);
                }
                else if (!strcmp(token1, "comment")) {
                    strcpy(fa->comment, token2);
                }
                else
                    HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "unknown token in the configure string: %s",
                                token1);
            }

            token1 = strtok(NULL, ":");
            token2 = strtok(NULL, ";");
        } while (token1);
    }

    if (H5P_DEFAULT == fa->backing_fapl_id || H5I_INVALID_HID == fa->backing_fapl_id) {
        H5P_genclass_t *pclass; /* Property list class to modify */

        if (NULL == (pclass = (H5P_genclass_t *)H5I_object_verify(H5P_FILE_ACCESS, H5I_GENPROP_CLS)))
            HGOTO_ERROR(H5E_VFL, H5E_BADTYPE, FAIL, "not a property list class");

        /* Create the new property list */
        if ((fa->backing_fapl_id = H5P_create_id(pclass, true)) < 0)
            HGOTO_ERROR(H5E_VFL, H5E_CANTCREATE, FAIL, "unable to create property list");
    }

done:
    H5MM_free(config_str_copy);

    FUNC_LEAVE_NOAPI(ret_value)
}

/*-----------------------------------------------------------------------------
 * Function:    H5FD__onion_open
 *
 * Purpose:     Open an onionized file
 *
 * Return:      Success:    A pointer to a new file data structure
 *              Failure:    NULL
 *-----------------------------------------------------------------------------
 */
static H5FD_t *
H5FD__onion_open(const char *filename, unsigned flags, hid_t fapl_id, haddr_t maxaddr)
{
    H5P_genplist_t               *plist                 = NULL;
    H5FD_onion_t                 *file                  = NULL;
    const H5FD_onion_fapl_info_t *fa                    = NULL;
    H5FD_onion_fapl_info_t       *new_fa                = NULL;
    const char                   *config_str            = NULL;
    double                        log2_page_size        = 0.0;
    hid_t                         backing_fapl_id       = H5I_INVALID_HID;
    char                         *name_onion            = NULL;
    char                         *recovery_file_nameery = NULL;
    bool                          new_open              = false;
    haddr_t                       canon_eof             = 0;
    H5FD_t                       *ret_value             = NULL;

    FUNC_ENTER_PACKAGE

    /* Check arguments */
    if (!filename || !*filename)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid file name");
    if (0 == maxaddr || HADDR_UNDEF == maxaddr)
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, NULL, "bogus maxaddr");
    assert(H5P_DEFAULT != fapl_id);
    if (NULL == (plist = (H5P_genplist_t *)H5I_object(fapl_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "not a file access property list");

    /* This VFD can be invoked by either H5Pset_fapl_onion() or
     * H5Pset_driver_by_name(). When invoked by the former, there will be
     * driver info to peek at.
     */
    fa = (const H5FD_onion_fapl_info_t *)H5P_peek_driver_info(plist);

    if (NULL == fa) {
        if (NULL == (config_str = H5P_peek_driver_config_str(plist)))
            HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "missing VFL driver configure string");

        /* Allocate a new onion fapl info struct and fill it from the
         * configuration string
         */
        if (NULL == (new_fa = H5MM_calloc(sizeof(H5FD_onion_fapl_info_t))))
            HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "can't allocate memory for onion fapl info struct");
        if (H5FD__onion_parse_config_str(config_str, new_fa) < 0)
            HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "failed to parse configure string");

        fa = new_fa;
    }

    /* Check for unsupported target values */
    if (H5FD_ONION_STORE_TARGET_ONION != fa->store_target)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid store target");

    /* Allocate space for the file struct */
    if (NULL == (file = H5FL_CALLOC(H5FD_onion_t)))
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate file struct");

    /* Allocate space for onion VFD file names */
    if (NULL == (name_onion = H5MM_malloc(sizeof(char) * (strlen(filename) + 7))))
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate onion name string");
    snprintf(name_onion, strlen(filename) + 7, "%s.onion", filename);

    if (NULL == (recovery_file_nameery = H5MM_malloc(sizeof(char) * (strlen(name_onion) + 10))))
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate recovery name string");
    snprintf(recovery_file_nameery, strlen(name_onion) + 10, "%s.recovery", name_onion);
    file->recovery_file_name = recovery_file_nameery;

    if (NULL == (file->recovery_file_name = H5MM_malloc(sizeof(char) * (strlen(name_onion) + 10))))
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate recovery name string");
    snprintf(file->recovery_file_name, strlen(name_onion) + 10, "%s.recovery", name_onion);

    /* Translate H5P_DEFAULT to a real fapl ID, if necessary */
    backing_fapl_id = H5FD__onion_get_legit_fapl_id(file->fa.backing_fapl_id);
    if (H5I_INVALID_HID == backing_fapl_id)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid backing FAPL ID");

    /* Initialize file structure fields */

    H5MM_memcpy(&(file->fa), fa, sizeof(H5FD_onion_fapl_info_t));

    file->header.version   = H5FD_ONION_HEADER_VERSION_CURR;
    file->header.page_size = file->fa.page_size; /* guarded on FAPL-set */

    file->history.version = H5FD_ONION_HISTORY_VERSION_CURR;

    file->curr_rev_record.version                = H5FD_ONION_REVISION_RECORD_VERSION_CURR;
    file->curr_rev_record.archival_index.version = H5FD_ONION_ARCHIVAL_INDEX_VERSION_CURR;

    /* Check that the page size is a power of two */
    if ((fa->page_size == 0) || ((fa->page_size & (fa->page_size - 1)) != 0))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "page size is not a power of two");

    /* Assign the page size */
    log2_page_size                                      = log2((double)(fa->page_size));
    file->curr_rev_record.archival_index.page_size_log2 = (uint32_t)log2_page_size;

    /* Proceed with open. */

    if ((H5F_ACC_CREAT | H5F_ACC_TRUNC) & flags) {

        /* Create a new onion file from scratch */

        /* Set flags */
        if (fa->creation_flags & H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT) {
            file->header.flags |= H5FD_ONION_HEADER_FLAG_PAGE_ALIGNMENT;
            file->align_history_on_pages = true;
        }

        /* Truncate and create everything as necessary */
        if (H5FD__onion_create_truncate_onion(file, filename, name_onion, file->recovery_file_name, flags,
                                              maxaddr) < 0)
            HGOTO_ERROR(H5E_VFL, H5E_CANTCREATE, NULL, "unable to create/truncate onionized files");
        file->is_open_rw = true;
    }
    else {

        /* Opening an existing onion file */

        /* Open the existing file using the specified fapl */
        if (NULL == (file->original_file = H5FD_open(filename, flags, backing_fapl_id, maxaddr)))
            HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "unable to open canonical file (does not exist?)");

        /* Try to open any existing onion file */
        H5E_BEGIN_TRY
        {
            file->onion_file = H5FD_open(name_onion, flags, backing_fapl_id, maxaddr);
        }
        H5E_END_TRY

        /* If that didn't work, create a new onion file */
        /* TODO: Move to a new function */
        if (NULL == file->onion_file) {
            if (H5F_ACC_RDWR & flags) {
                H5FD_onion_header_t          *hdr        = NULL;
                H5FD_onion_history_t         *history    = NULL;
                H5FD_onion_revision_record_t *rec        = NULL;
                unsigned char                *head_buf   = NULL;
                unsigned char                *hist_buf   = NULL;
                size_t                        size       = 0;
                size_t                        saved_size = 0;

                assert(file != NULL);

                hdr     = &file->header;
                history = &file->history;
                rec     = &file->curr_rev_record;

                new_open = true;

                if (H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT & file->fa.creation_flags) {
                    hdr->flags |= H5FD_ONION_HEADER_FLAG_PAGE_ALIGNMENT;
                    file->align_history_on_pages = true;
                }

                if (HADDR_UNDEF == (canon_eof = H5FD_get_eof(file->original_file, H5FD_MEM_DEFAULT))) {
                    HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, NULL, "cannot get size of canonical file");
                }
                if (H5FD_set_eoa(file->original_file, H5FD_MEM_DRAW, canon_eof) < 0)
                    HGOTO_ERROR(H5E_VFL, H5E_CANTSET, NULL, "can't extend EOA");
                hdr->origin_eof   = canon_eof;
                file->logical_eof = canon_eof;

                backing_fapl_id = H5FD__onion_get_legit_fapl_id(file->fa.backing_fapl_id);

                if (H5I_INVALID_HID == backing_fapl_id)
                    HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid backing FAPL ID");

                /* Create backing files for onion history */

                if ((file->onion_file = H5FD_open(name_onion, (H5F_ACC_RDWR | H5F_ACC_CREAT | H5F_ACC_TRUNC),
                                                  backing_fapl_id, maxaddr)) == NULL) {
                    HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "cannot open the backing onion file");
                }

                /* Write history header with "no" history */
                hdr->history_size = H5FD_ONION_ENCODED_SIZE_HISTORY; /* record for later use */
                hdr->history_addr =
                    H5FD_ONION_ENCODED_SIZE_HEADER + 1; /* TODO: comment these 2 or do some other way */
                head_buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_HEADER);
                if (NULL == head_buf)
                    HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "can't allocate buffer");
                size = H5FD__onion_header_encode(hdr, head_buf, &hdr->checksum);
                if (H5FD_ONION_ENCODED_SIZE_HEADER != size)
                    HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "can't encode history header");

                hist_buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_HISTORY);
                if (NULL == hist_buf)
                    HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "can't allocate buffer");
                saved_size                = size;
                history->n_revisions      = 0;
                size                      = H5FD__onion_history_encode(history, hist_buf, &history->checksum);
                file->header.history_size = size; /* record for later use */
                if (H5FD_ONION_ENCODED_SIZE_HISTORY != size) {
                    HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "can't encode history");
                }
                if (H5FD_set_eoa(file->onion_file, H5FD_MEM_DRAW, saved_size + size + 1) < 0)
                    HGOTO_ERROR(H5E_VFL, H5E_CANTSET, NULL, "can't extend EOA");

                if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, 0, saved_size, head_buf) < 0) {
                    HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, NULL,
                                "cannot write header to the backing onion file");
                }

                file->onion_eof = (haddr_t)saved_size;
                if (true == file->align_history_on_pages)
                    file->onion_eof = (file->onion_eof + (hdr->page_size - 1)) & (~(hdr->page_size - 1));

                rec->archival_index.list = NULL;

                file->header.history_addr = file->onion_eof;

                /* Write nascent history (with no revisions) to the backing onion file */
                if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, saved_size + 1, size, hist_buf) < 0) {
                    HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, NULL,
                                "cannot write history to the backing onion file");
                }

                file->header.history_size = size; /* record for later use */

                H5MM_xfree(head_buf);
                H5MM_xfree(hist_buf);
            }
            else {
                HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "unable to open onion file (does not exist?).");
            }
        }

        if (HADDR_UNDEF == (canon_eof = H5FD_get_eof(file->original_file, H5FD_MEM_DEFAULT))) {
            HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, NULL, "cannot get size of canonical file");
        }
        if (H5FD_set_eoa(file->original_file, H5FD_MEM_DRAW, canon_eof) < 0)
            HGOTO_ERROR(H5E_VFL, H5E_CANTSET, NULL, "can't extend EOA");

        /* Get the history header from the onion file */
        if (H5FD__onion_ingest_header(&file->header, file->onion_file, 0) < 0)
            HGOTO_ERROR(H5E_VFL, H5E_CANTDECODE, NULL, "can't get history header from backing store");
        file->align_history_on_pages =
            (file->header.flags & H5FD_ONION_HEADER_FLAG_PAGE_ALIGNMENT) ? true : false;

        if (H5FD_ONION_HEADER_FLAG_WRITE_LOCK & file->header.flags) {
            /* Opening a file twice in write mode is an error */
            HGOTO_ERROR(H5E_VFL, H5E_UNSUPPORTED, NULL, "Can't open file already opened in write-mode");
        }
        else {
            /* Read in the history from the onion file */
            if (H5FD__onion_ingest_history(&file->history, file->onion_file, file->header.history_addr,
                                           file->header.history_size) < 0)
                HGOTO_ERROR(H5E_VFL, H5E_CANTDECODE, NULL, "can't get history from backing store");

            /* Sanity check on revision ID */
            if (fa->revision_num > file->history.n_revisions &&
                fa->revision_num != H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST)
                HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "target revision ID out of range");

            if (fa->revision_num == 0) {
                file->curr_rev_record.logical_eof = canon_eof;
            }
            else if (file->history.n_revisions > 0 &&
                     H5FD__onion_ingest_revision_record(
                         &file->curr_rev_record, file->onion_file, &file->history,
                         MIN(fa->revision_num - 1, (file->history.n_revisions - 1))) < 0) {
                HGOTO_ERROR(H5E_VFL, H5E_CANTDECODE, NULL, "can't get revision record from backing store");
            }

            if (H5F_ACC_RDWR & flags)
                if (H5FD__onion_open_rw(file, flags, maxaddr, new_open) < 0)
                    HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "can't write-open write-locked file");
        }

    } /* End if opening existing file */

    /* Copy comment from FAPL info, if one is given */
    if ((H5F_ACC_RDWR | H5F_ACC_CREAT | H5F_ACC_TRUNC) & flags) {
        /* Free the old comment */
        file->curr_rev_record.comment = H5MM_xfree(file->curr_rev_record.comment);

        /* The buffer is of size H5FD_ONION_FAPL_INFO_COMMENT_MAX_LEN + 1
         *
         * We're getting this buffer from a fixed-size array in a struct, which
         * will be garbage and not null-terminated if the user isn't careful.
         * Be careful of this and do strndup first to ensure strdup gets a
         * null-termianted string (HDF5 doesn't provide a strnlen call if you
         * don't have one).
         */
        if (NULL ==
            (file->curr_rev_record.comment = H5MM_strndup(fa->comment, H5FD_ONION_FAPL_INFO_COMMENT_MAX_LEN)))
            HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to duplicate comment string");

        /* TODO: Lengths of strings should be size_t */
        file->curr_rev_record.comment_size = (uint32_t)strlen(fa->comment) + 1;
    }
    file->origin_eof  = file->header.origin_eof;
    file->logical_eof = MAX(file->curr_rev_record.logical_eof, file->logical_eof);
    file->logical_eoa = 0;

    file->onion_eof = H5FD_get_eoa(file->onion_file, H5FD_MEM_DRAW);
    if (true == file->align_history_on_pages)
        file->onion_eof = (file->onion_eof + (file->header.page_size - 1)) & (~(file->header.page_size - 1));

    ret_value = (H5FD_t *)file;

done:
    H5MM_xfree(name_onion);
    H5MM_xfree(recovery_file_nameery);

    if (config_str && new_fa)
        if (fa && fa->backing_fapl_id)
            if (H5I_GENPROP_LST == H5I_get_type(fa->backing_fapl_id))
                H5I_dec_app_ref(fa->backing_fapl_id);

    if ((NULL == ret_value) && file) {

        if (file->original_file)
            if (H5FD_close(file->original_file) < 0)
                HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, NULL, "can't destroy backing canon");
        if (file->onion_file)
            if (H5FD_close(file->onion_file) < 0)
                HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, NULL, "can't destroy backing onion");
        if (file->recovery_file)
            if (H5FD_close(file->recovery_file) < 0)
                HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, NULL, "can't destroy backing recov");

        if (file->rev_index)
            if (H5FD__onion_revision_index_destroy(file->rev_index) < 0)
                HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, NULL, "can't destroy revision index");

        H5MM_xfree(file->history.record_locs);

        H5MM_xfree(file->recovery_file_name);
        H5MM_xfree(file->curr_rev_record.comment);

        H5FL_FREE(H5FD_onion_t, file);
    }

    H5MM_xfree(new_fa);

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_open() */

/*-----------------------------------------------------------------------------
 * Function:    H5FD__onion_open_rw
 *
 * Purpose:     Complete onion file-open, handling process for write mode.
 *
 *              Creates recovery file if one does not exist.
 *              Initializes 'live' revision index.
 *              Force write-open is not yet supported (recovery provision) TODO
 *              Establishes write-lock in history header (sets lock flag).
 *
 * Return:      SUCCEED/FAIL
 *-----------------------------------------------------------------------------
 */
static herr_t
H5FD__onion_open_rw(H5FD_onion_t *file, unsigned int flags, haddr_t maxaddr, bool new_open)
{
    unsigned char *buf       = NULL;
    size_t         size      = 0;
    uint32_t       checksum  = 0;
    herr_t         ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE

    /* Guard against simultaneous write-open.
     * TODO: support recovery open with force-write-open flag in FAPL info.
     */

    if (file->header.flags & H5FD_ONION_HEADER_FLAG_WRITE_LOCK)
        HGOTO_ERROR(H5E_VFL, H5E_UNSUPPORTED, FAIL, "can't write-open write-locked file");

    /* Copy history to recovery file */

    if (NULL ==
        (file->recovery_file = H5FD_open(file->recovery_file_name, (flags | H5F_ACC_CREAT | H5F_ACC_TRUNC),
                                         file->fa.backing_fapl_id, maxaddr)))
        HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "unable to create recovery file");

    if (0 == (size = H5FD__onion_write_history(&file->history, file->recovery_file, 0, 0)))
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "can't write history to recovery file");
    if (size != file->header.history_size)
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "written history differed from expected size");

    /* Set write-lock flag in onion header */

    if (NULL == (buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_HEADER)))
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate space for encoded buffer");

    file->header.flags |= H5FD_ONION_HEADER_FLAG_WRITE_LOCK;

    if (0 == (size = H5FD__onion_header_encode(&file->header, buf, &checksum)))
        HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "problem encoding history header");

    if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, 0, size, buf) < 0)
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "can't write updated history header");

    /* Prepare revision index and finalize write-mode open */

    if (NULL == (file->rev_index = H5FD__onion_revision_index_init(file->fa.page_size)))
        HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "can't initialize revision index");
    file->curr_rev_record.parent_revision_num = file->curr_rev_record.revision_num;
    if (!new_open)
        file->curr_rev_record.revision_num += 1;
    file->is_open_rw = true;

done:
    if (FAIL == ret_value) {
        if (file->recovery_file != NULL) {
            if (H5FD_close(file->recovery_file) < 0)
                HDONE_ERROR(H5E_VFL, H5E_CANTCLOSEFILE, FAIL, "can't close recovery file");
            file->recovery_file = NULL;
        }

        if (file->rev_index != NULL) {
            if (H5FD__onion_revision_index_destroy(file->rev_index) < 0)
                HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, FAIL, "can't destroy revision index");
            file->rev_index = NULL;
        }
    }

    H5MM_xfree(buf);

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_open_rw() */

/*-----------------------------------------------------------------------------
 * Function:    H5FD__onion_read
 *
 * Purpose:     Read bytes from an onionized file
 *
 * Return:      SUCCEED/FAIL
 *-----------------------------------------------------------------------------
 */
static herr_t
H5FD__onion_read(H5FD_t *_file, H5FD_mem_t type, hid_t H5_ATTR_UNUSED dxpl_id, haddr_t offset, size_t len,
                 void *_buf_out)
{
    H5FD_onion_t  *file           = (H5FD_onion_t *)_file;
    uint64_t       page_0         = 0;
    size_t         n_pages        = 0;
    uint32_t       page_size      = 0;
    uint32_t       page_size_log2 = 0;
    size_t         bytes_to_read  = len;
    unsigned char *buf_out        = (unsigned char *)_buf_out;
    herr_t         ret_value      = SUCCEED;

    FUNC_ENTER_PACKAGE

    assert(file != NULL);
    assert(buf_out != NULL);

    if ((uint64_t)(offset + len) > file->logical_eoa)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Read extends beyond addressed space");

    if (0 == len)
        goto done;

    page_size      = file->header.page_size;
    page_size_log2 = file->curr_rev_record.archival_index.page_size_log2;
    page_0         = offset >> page_size_log2;
    n_pages        = (len + page_size - 1) >> page_size_log2;

    /* Read, page-by-page */
    for (size_t i = 0; i < n_pages; i++) {
        const H5FD_onion_index_entry_t *entry_out     = NULL;
        haddr_t                         page_gap_head = 0; /* start of page to start of buffer */
        haddr_t                         page_gap_tail = 0; /* end of buffer to end of page */
        size_t                          page_readsize = 0;
        uint64_t                        page_i        = page_0 + i;

        if (0 == i) {
            page_gap_head = offset & (((uint32_t)1 << page_size_log2) - 1);
            /* Check if we need to add an additional page to make up for the page_gap_head */
            if (page_gap_head > 0 &&
                (page_gap_head + (bytes_to_read % page_size) > page_size || bytes_to_read % page_size == 0)) {
                n_pages++;
            }
        }

        if (n_pages - 1 == i)
            page_gap_tail = page_size - bytes_to_read - page_gap_head;

        page_readsize = (size_t)page_size - page_gap_head - page_gap_tail;

        if (true == file->is_open_rw && file->fa.revision_num != 0 &&
            H5FD__onion_revision_index_find(file->rev_index, page_i, &entry_out)) {
            /* Page exists in 'live' revision index */
            if (H5FD_read(file->onion_file, H5FD_MEM_DRAW, entry_out->phys_addr + page_gap_head,
                          page_readsize, buf_out) < 0)
                HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get working file data");
        }
        else if (file->fa.revision_num != 0 &&
                 H5FD__onion_archival_index_find(&file->curr_rev_record.archival_index, page_i, &entry_out)) {
            /* Page exists in archival index */
            if (H5FD_read(file->onion_file, H5FD_MEM_DRAW, entry_out->phys_addr + page_gap_head,
                          page_readsize, buf_out) < 0)
                HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get previously-amended file data");
        }
        else {
            /* Page does not exist in either index */

            /* Casts prevent truncation */
            haddr_t addr_start   = (haddr_t)page_i * (haddr_t)page_size + (haddr_t)page_gap_head;
            haddr_t overlap_size = (addr_start > file->origin_eof) ? 0 : file->origin_eof - addr_start;
            haddr_t read_size    = MIN(overlap_size, page_readsize);

            /* Get all original bytes in page range */
            if ((read_size > 0) && H5FD_read(file->original_file, type, addr_start, read_size, buf_out) < 0) {
                HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get original file data");
            }

            /* Fill with 0s any gaps after end of original bytes
             * and before end of page.
             */
            for (size_t j = read_size; j < page_readsize; j++)
                buf_out[j] = 0;
        }

        buf_out += page_readsize;
        bytes_to_read -= page_readsize;
    } /* end for each page in range */

    assert(0 == bytes_to_read);

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_read() */

/*-----------------------------------------------------------------------------
 * Function:    H5FD__onion_set_eoa
 *
 * Purpose:     Set end-of-address marker of the logical file.
 *
 * Return:      SUCCEED/FAIL
 *-----------------------------------------------------------------------------
 */
static herr_t
H5FD__onion_set_eoa(H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type, haddr_t addr)
{
    H5FD_onion_t *file = (H5FD_onion_t *)_file;

    FUNC_ENTER_PACKAGE_NOERR

    file->logical_eoa = addr;

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* end H5FD__onion_set_eoa() */

/*-----------------------------------------------------------------------------
 * Function:    H5FD__onion_write
 *
 * Purpose:     Write bytes to an onionized file
 *
 * Return:      SUCCEED/FAIL
 *-----------------------------------------------------------------------------
 */
static herr_t
H5FD__onion_write(H5FD_t *_file, H5FD_mem_t type, hid_t H5_ATTR_UNUSED dxpl_id, haddr_t offset, size_t len,
                  const void *_buf)
{
    H5FD_onion_t        *file           = (H5FD_onion_t *)_file;
    uint64_t             page_0         = 0;
    size_t               n_pages        = 0;
    unsigned char       *page_buf       = NULL;
    uint32_t             page_size      = 0;
    uint32_t             page_size_log2 = 0;
    size_t               bytes_to_write = len;
    const unsigned char *buf            = (const unsigned char *)_buf;
    herr_t               ret_value      = SUCCEED;

    FUNC_ENTER_PACKAGE

    assert(file != NULL);
    assert(buf != NULL);
    assert(file->rev_index != NULL);
    assert((uint64_t)(offset + len) <= file->logical_eoa);

    if (false == file->is_open_rw)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Write not allowed if file not opened in write mode");

    if (0 == len)
        goto done;

    page_size      = file->header.page_size;
    page_size_log2 = file->curr_rev_record.archival_index.page_size_log2;
    page_0         = offset >> page_size_log2;
    n_pages        = (len + page_size - 1) >> page_size_log2;

    if (NULL == (page_buf = H5MM_calloc(page_size * sizeof(unsigned char))))
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "cannot allocate temporary buffer");

    /* Write, page-by-page */
    for (size_t i = 0; i < n_pages; i++) {
        const unsigned char            *write_buf = buf;
        H5FD_onion_index_entry_t        new_entry;
        const H5FD_onion_index_entry_t *entry_out     = NULL;
        haddr_t                         page_gap_head = 0; /* start of page to start of buffer */
        haddr_t                         page_gap_tail = 0; /* end of buffer to end of page */
        size_t                          page_n_used   = 0; /* nbytes from buffer for this page-write */
        uint64_t                        page_i        = page_0 + i;

        if (0 == i) {
            page_gap_head = offset & (((uint32_t)1 << page_size_log2) - 1);
            /* If we have a page_gap_head and the number of bytes to write is
             * evenly divisible by the page size we need to add an additional
             * page to make up for the page_gap_head
             */
            if (page_gap_head > 0 && (page_gap_head + (bytes_to_write % page_size) > page_size ||
                                      bytes_to_write % page_size == 0)) {
                n_pages++;
            }
        }
        if (n_pages - 1 == i)
            page_gap_tail = page_size - bytes_to_write - page_gap_head;
        page_n_used = page_size - page_gap_head - page_gap_tail;

        /* Modify page in revision index, if present */
        if (H5FD__onion_revision_index_find(file->rev_index, page_i, &entry_out)) {
            if (page_gap_head | page_gap_tail) {
                /* Copy existing page verbatim. */
                if (H5FD_read(file->onion_file, H5FD_MEM_DRAW, entry_out->phys_addr, page_size, page_buf) < 0)
                    HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get working file data");
                /* Overlay delta from input buffer onto page buffer. */
                H5MM_memcpy(page_buf + page_gap_head, buf, page_n_used);
                write_buf = page_buf;
            } /* end if partial page */

            if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, entry_out->phys_addr, page_size, write_buf) < 0)
                HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "write amended page data to backing file");

            buf += page_n_used; /* overflow never touched */
            bytes_to_write -= page_n_used;

            continue;
        } /* end if page exists in 'live' revision index */

        if (page_gap_head || page_gap_tail) {
            /* Fill gaps with existing data or zeroes. */
            if (H5FD__onion_archival_index_find(&file->curr_rev_record.archival_index, page_i, &entry_out)) {
                /* Page exists in archival index */

                /* Copy existing page verbatim */
                if (H5FD_read(file->onion_file, H5FD_MEM_DRAW, entry_out->phys_addr, page_size, page_buf) < 0)
                    HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get previously-amended data");
            }
            else {
                haddr_t addr_start   = (haddr_t)(page_i * page_size);
                haddr_t overlap_size = (addr_start > file->origin_eof) ? 0 : file->origin_eof - addr_start;
                haddr_t read_size    = MIN(overlap_size, page_size);

                /* Get all original bytes in page range */
                if ((read_size > 0) &&
                    H5FD_read(file->original_file, type, addr_start, read_size, page_buf) < 0) {
                    HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get original file data");
                }

                /* Fill with 0s any gaps after end of original bytes
                 * or start of page and before start of new data.
                 */
                for (size_t j = read_size; j < page_gap_head; j++)
                    page_buf[j] = 0;

                /* Fill with 0s any gaps after end of original bytes
                 * or end of new data and before end of page.
                 */
                for (size_t j = MAX(read_size, page_size - page_gap_tail); j < page_size; j++)
                    page_buf[j] = 0;
            } /* end if page exists in neither index */

            /* Copy input buffer to temporary page buffer */
            assert((page_size - page_gap_head) >= page_n_used);
            H5MM_memcpy(page_buf + page_gap_head, buf, page_n_used);
            write_buf = page_buf;

        } /* end if data range does not span entire page */

        new_entry.logical_page = page_i;
        new_entry.phys_addr    = file->onion_eof;

        if (H5FD_set_eoa(file->onion_file, H5FD_MEM_DRAW, file->onion_eof + page_size) < 0)
            HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "can't modify EOA for new page amendment");

        if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, file->onion_eof, page_size, write_buf) < 0)
            HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "write amended page data to backing file");

        if (H5FD__onion_revision_index_insert(file->rev_index, &new_entry) < 0)
            HGOTO_ERROR(H5E_VFL, H5E_CANTINSERT, FAIL, "can't insert new index entry into revision index");

        file->onion_eof += page_size;
        buf += page_n_used; /* possible overflow never touched */
        bytes_to_write -= page_n_used;

    } /* end for each page to write */

    assert(0 == bytes_to_write);

    file->logical_eof = MAX(file->logical_eof, (offset + len));

done:
    H5MM_xfree(page_buf);

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_write() */

/*-------------------------------------------------------------------------
 * Function:    H5FD__onion_ctl
 *
 * Purpose:     Onion VFD version of the ctl callback.
 *
 *              The desired operation is specified by the op_code
 *              parameter.
 *
 *              The flags parameter controls management of op_codes that
 *              are unknown to the callback
 *
 *              The input and output parameters allow op_code specific
 *              input and output
 *
 * Return:      SUCCEED/FAIL
 *-------------------------------------------------------------------------
 */
static herr_t
H5FD__onion_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags, const void H5_ATTR_UNUSED *input,
                void **output)
{
    H5FD_onion_t *file      = (H5FD_onion_t *)_file;
    herr_t        ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE

    /* Sanity checks */
    assert(file);

    switch (op_code) {
        case H5FD_CTL_GET_NUM_REVISIONS:
            if (!output || !*output)
                HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, "the output parameter is null");

            **((uint64_t **)output) = file->history.n_revisions;
            break;
        /* Unknown op code */
        default:
            if (flags & H5FD_CTL_FAIL_IF_UNKNOWN_FLAG)
                HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, "unknown op_code and fail if unknown flag is set");
            break;
    }

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_ctl() */

/*-------------------------------------------------------------------------
 * Function:    H5FDget_onion_revision_count
 *
 * Purpose:     Get the number of revisions in an onion file
 *
 * Return:      SUCCEED/FAIL
 *-------------------------------------------------------------------------
 */
herr_t
H5FDonion_get_revision_count(const char *filename, hid_t fapl_id, uint64_t *revision_count /*out*/)
{
    H5P_genplist_t *plist     = NULL;
    H5FD_t         *file      = NULL;
    herr_t          ret_value = SUCCEED;

    FUNC_ENTER_API(FAIL)
    H5TRACE3("e", "*six", filename, fapl_id, revision_count);

    /* Check args */
    if (!filename || !strcmp(filename, ""))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not a valid file name");
    if (!revision_count)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "revision count can't be null");

    /* Make sure using the correct driver */
    if (NULL == (plist = H5P_object_verify(fapl_id, H5P_FILE_ACCESS)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not a valid FAPL ID");
    if (H5FD_ONION != H5P_peek_driver(plist))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not a Onion VFL driver");

    /* Open the file with the driver */
    if (NULL == (file = H5FD_open(filename, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF)))
        HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "unable to open file with onion driver");

    /* Call the private function */
    if (H5FD__get_onion_revision_count(file, revision_count) < 0)
        HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "failed to get the number of revisions");

done:
    /* Close H5FD_t structure pointer */
    if (file && H5FD_close(file) < 0)
        HGOTO_ERROR(H5E_VFL, H5E_CANTCLOSEFILE, FAIL, "unable to close file");

    FUNC_LEAVE_API(ret_value)
}

/*-------------------------------------------------------------------------
 * Function:    H5FD__get_onion_revision_count
 *
 * Purpose:     Private version of H5FDget_onion_revision_count()
 *
 * Return:      SUCCEED/FAIL
 *-------------------------------------------------------------------------
 */
static herr_t
H5FD__get_onion_revision_count(H5FD_t *file, uint64_t *revision_count)
{
    uint64_t op_code;
    uint64_t flags;
    herr_t   ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE

    assert(file);
    assert(revision_count);

    op_code = H5FD_CTL_GET_NUM_REVISIONS;
    flags   = H5FD_CTL_FAIL_IF_UNKNOWN_FLAG;

    /* Get the number of revisions via the ctl callback */
    if (H5FD_ctl(file, op_code, flags, NULL, (void **)&revision_count) < 0)
        HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, "VFD ctl request failed");

done:
    FUNC_LEAVE_NOAPI(ret_value)
}

/*-----------------------------------------------------------------------------
 * Function:    H5FD__onion_write_final_history
 *
 * Purpose:     Write final history to appropriate backing file on file close
 *
 * Return:      SUCCEED/FAIL
 *-----------------------------------------------------------------------------
 */
herr_t
H5FD__onion_write_final_history(H5FD_onion_t *file)
{
    size_t size      = 0;
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_PACKAGE

    /* TODO: history EOF may not be correct (under what circumstances?) */
    if (0 == (size = H5FD__onion_write_history(&(file->history), file->onion_file, file->onion_eof,
                                               file->onion_eof)))
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "can't write final history");

    if (size != file->header.history_size)
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "written history differed from expected size");

    /* Is last write operation to history file; no need to extend to page
     * boundary if set to page-align.
     */
    file->onion_eof += size;

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD__onion_write_final_history() */