Coverage Report

Created: 2026-10-28 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hdf5/src/H5FDonion.c
Line
Count
Source
1
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2
 * Copyright by The HDF Group.                                               *
3
 * All rights reserved.                                                      *
4
 *                                                                           *
5
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
6
 * terms governing use, modification, and redistribution, is contained in    *
7
 * the LICENSE file, which can be found at the root of the source code       *
8
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
9
 * If you do not have access to either file, you may request a copy from     *
10
 * help@hdfgroup.org.                                                        *
11
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12
13
/*
14
 * Onion Virtual File Driver (VFD)
15
 *
16
 * Purpose:     Provide in-file provenance and revision/version control.
17
 */
18
19
#include "H5FDmodule.h" /* This source code file is part of the H5FD module */
20
21
#include "H5private.h"      /* Generic Functions           */
22
#include "H5Eprivate.h"     /* Error handling              */
23
#include "H5Fprivate.h"     /* Files                       */
24
#include "H5FDsec2.h"       /* Sec2 file driver            */
25
#include "H5FDpkg.h"        /* File drivers                */
26
#include "H5FDonion_priv.h" /* Onion file driver internals */
27
#include "H5FLprivate.h"    /* Free Lists                  */
28
#include "H5Iprivate.h"     /* IDs                         */
29
#include "H5MMprivate.h"    /* Memory management           */
30
31
/* The driver identification number, initialized at runtime */
32
hid_t H5FD_ONION_id_g = H5I_INVALID_HID;
33
34
/******************************************************************************
35
 *
36
 * Structure:   H5FD_onion_t
37
 *
38
 * Purpose:     Store information required to manage an onionized file.
39
 *              This structure is created when such a file is "opened" and
40
 *              discarded when it is "closed".
41
 *
42
 * pu
43
 *
44
 *      Instance of H5FD_t which contains fields common to all VFDs.
45
 *      It must be the first item in this structure, since at higher levels,
46
 *      this structure will be treated as an instance of H5FD_t.
47
 *
48
 * fa
49
 *
50
 *      Instance of `H5FD_onion_fapl_info_t` containing the configuration data
51
 *      needed to "open" the HDF5 file.
52
 *
53
 * original_file
54
 *
55
 *      VFD handle for the original HDF5 file.
56
 *
57
 * onion_file
58
 *
59
 *      VFD handle for the onion file.
60
 *      NULL if not set to use the single, separate storage target.
61
 *
62
 * recovery_file
63
 *
64
 *      VFD handle for the history recovery file. This file is a backup of
65
 *      the existing history when an existing onion file is opened in RW mode.
66
 *
67
 * recovery_file_name
68
 *
69
 *      String allocated and populated on file-open in write mode and freed on
70
 *      file-close, stores the path/name of the 'recovery' file. The file
71
 *      created at this location is to be removed upon successful file-close
72
 *      from write mode.
73
 *
74
 * is_open_rw
75
 *
76
 *      Remember whether the file was opened in a read-write mode.
77
 *
78
 * align_history_on_pages
79
 *
80
 *      Remember whether onion-writes must be aligned to page boundaries.
81
 *
82
 * header
83
 *
84
 *      In-memory copy of the onion history data header.
85
 *
86
 * history
87
 *
88
 *      In-memory copy of the onion history.
89
 *
90
 * curr_rev_record
91
 *
92
 *      Record for the currently open revision.
93
 *
94
 * rev_index
95
 *
96
 *      Index for maintaining modified pages (RW mode only).
97
 *      Pointer is NULL when the file is not opened in write mode.
98
 *      Pointer is allocated on open and must be freed on close.
99
 *      Contents must be merged with the revision record's archival index prior
100
 *      to commitment of history to backing store.
101
 *
102
 * onion_eof
103
 *
104
 *      Last byte in the onion file.
105
 *
106
 * origin_eof
107
 *
108
 *     Size of the original HDF5 file.
109
 *
110
 * logical_eoa
111
 *
112
 *     Address of first byte past addressed space in the logical 'file'
113
 *     presented by this VFD.
114
 *
115
 * logical_eof
116
 *
117
 *     Address of first byte past last byte in the logical 'file' presented
118
 *     by this VFD.
119
 *     Must be copied into the revision record on close to write onion data.
120
 *
121
 ******************************************************************************
122
 */
123
typedef struct H5FD_onion_t {
124
    H5FD_t                 pub;
125
    H5FD_onion_fapl_info_t fa;
126
    bool                   is_open_rw;
127
    bool                   align_history_on_pages;
128
129
    /* Onion-related files */
130
    H5FD_t *original_file;
131
    H5FD_t *onion_file;
132
    H5FD_t *recovery_file;
133
    char   *recovery_file_name;
134
135
    /* Onion data structures */
136
    H5FD_onion_header_t          header;
137
    H5FD_onion_history_t         history;
138
    H5FD_onion_revision_record_t curr_rev_record;
139
    H5FD_onion_revision_index_t *rev_index;
140
141
    /* End of addresses and files */
142
    haddr_t onion_eof;
143
    haddr_t origin_eof;
144
    haddr_t logical_eoa;
145
    haddr_t logical_eof;
146
} H5FD_onion_t;
147
148
H5FL_DEFINE_STATIC(H5FD_onion_t);
149
150
0
#define H5FD_CTL_GET_NUM_REVISIONS 20001
151
152
/* Prototypes */
153
static herr_t  H5FD__onion_close(H5FD_t *);
154
static herr_t  H5FD__onion_query(const H5FD_t *, unsigned long *);
155
static haddr_t H5FD__onion_get_eoa(const H5FD_t *, H5FD_mem_t);
156
static haddr_t H5FD__onion_get_eof(const H5FD_t *, H5FD_mem_t);
157
static H5FD_t *H5FD__onion_open(const char *, unsigned int, hid_t, haddr_t);
158
static herr_t  H5FD__onion_read(H5FD_t *, H5FD_mem_t, hid_t, haddr_t, size_t, void *);
159
static herr_t  H5FD__onion_set_eoa(H5FD_t *, H5FD_mem_t, haddr_t);
160
static herr_t  H5FD__onion_write(H5FD_t *, H5FD_mem_t, hid_t, haddr_t, size_t, const void *);
161
162
static herr_t  H5FD__onion_open_rw(H5FD_onion_t *, unsigned int, haddr_t, bool new_open);
163
static herr_t  H5FD__onion_sb_encode(H5FD_t *_file, char *name /*out*/, unsigned char *buf /*out*/);
164
static herr_t  H5FD__onion_sb_decode(H5FD_t *_file, const char *name, const unsigned char *buf);
165
static hsize_t H5FD__onion_sb_size(H5FD_t *_file);
166
static herr_t  H5FD__onion_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags,
167
                               const void H5_ATTR_UNUSED *input, void H5_ATTR_UNUSED **output);
168
static herr_t  H5FD__get_onion_revision_count(H5FD_t *file, uint64_t *revision_count);
169
170
/* Temporary */
171
H5_DLL herr_t H5FD__onion_write_final_history(H5FD_onion_t *file);
172
173
static const H5FD_class_t H5FD_onion_g = {
174
    H5FD_CLASS_VERSION,             /* struct version       */
175
    H5FD_ONION_VALUE,               /* value                */
176
    "onion",                        /* name                 */
177
    H5FD_MAXADDR,                   /* maxaddr              */
178
    H5F_CLOSE_WEAK,                 /* fc_degree            */
179
    NULL,                           /* terminate            */
180
    H5FD__onion_sb_size,            /* sb_size              */
181
    H5FD__onion_sb_encode,          /* sb_encode            */
182
    H5FD__onion_sb_decode,          /* sb_decode            */
183
    sizeof(H5FD_onion_fapl_info_t), /* fapl_size            */
184
    NULL,                           /* fapl_get             */
185
    NULL,                           /* fapl_copy            */
186
    NULL,                           /* fapl_free            */
187
    0,                              /* dxpl_size            */
188
    NULL,                           /* dxpl_copy            */
189
    NULL,                           /* dxpl_free            */
190
    H5FD__onion_open,               /* open                 */
191
    H5FD__onion_close,              /* close                */
192
    NULL,                           /* cmp                  */
193
    H5FD__onion_query,              /* query                */
194
    NULL,                           /* get_type_map         */
195
    NULL,                           /* alloc                */
196
    NULL,                           /* free                 */
197
    H5FD__onion_get_eoa,            /* get_eoa              */
198
    H5FD__onion_set_eoa,            /* set_eoa              */
199
    H5FD__onion_get_eof,            /* get_eof              */
200
    NULL,                           /* get_handle           */
201
    H5FD__onion_read,               /* read                 */
202
    H5FD__onion_write,              /* write                */
203
    NULL,                           /* read_vector          */
204
    NULL,                           /* write_vector         */
205
    NULL,                           /* read_selection       */
206
    NULL,                           /* write_selection      */
207
    NULL,                           /* flush                */
208
    NULL,                           /* truncate             */
209
    NULL,                           /* lock                 */
210
    NULL,                           /* unlock               */
211
    NULL,                           /* del                  */
212
    H5FD__onion_ctl,                /* ctl                  */
213
    H5FD_FLMAP_DICHOTOMY            /* fl_map               */
214
};
215
216
/*-----------------------------------------------------------------------------
217
 * Function:    H5FD__onion_register
218
 *
219
 * Purpose:     Register the driver with the library.
220
 *
221
 * Return:      SUCCEED/FAIL
222
 *
223
 *-----------------------------------------------------------------------------
224
 */
225
herr_t
226
H5FD__onion_register(void)
227
1
{
228
1
    herr_t ret_value = SUCCEED; /* Return value */
229
230
1
    FUNC_ENTER_PACKAGE
231
232
1
    if (H5I_VFL != H5I_get_type(H5FD_ONION_id_g))
233
1
        if ((H5FD_ONION_id_g = H5FD_register(&H5FD_onion_g, sizeof(H5FD_class_t), false)) < 0)
234
0
            HGOTO_ERROR(H5E_VFL, H5E_CANTREGISTER, FAIL, "unable to register onion driver");
235
236
1
done:
237
1
    FUNC_LEAVE_NOAPI(ret_value)
238
1
} /* end H5FD__onion_register() */
239
240
/*-----------------------------------------------------------------------------
241
 * Function:    H5FD__onion_unregister
242
 *
243
 * Purpose:     Reset library driver info.
244
 *
245
 * Returns:     SUCCEED (Can't fail)
246
 *
247
 *-----------------------------------------------------------------------------
248
 */
249
herr_t
250
H5FD__onion_unregister(void)
251
1
{
252
1
    FUNC_ENTER_PACKAGE_NOERR
253
254
    /* Reset VFL ID */
255
1
    H5FD_ONION_id_g = H5I_INVALID_HID;
256
257
1
    FUNC_LEAVE_NOAPI(SUCCEED)
258
1
} /* end H5FD__onion_unregister() */
259
260
/*-----------------------------------------------------------------------------
261
 * Function:    H5Pget_fapl_onion
262
 *
263
 * Purpose:     Copy the Onion configuration information from the FAPL at
264
 *              `fapl_id` to the destination pointer `fa_out`.
265
 *
266
 * Return:      Success: Non-negative value (SUCCEED).
267
 *              Failure: Negative value (FAIL).
268
 *-----------------------------------------------------------------------------
269
 */
270
herr_t
271
H5Pget_fapl_onion(hid_t fapl_id, H5FD_onion_fapl_info_t *fa_out)
272
0
{
273
0
    const H5FD_onion_fapl_info_t *info_ptr  = NULL;
274
0
    H5P_genplist_t               *plist     = NULL;
275
0
    herr_t                        ret_value = SUCCEED;
276
277
0
    FUNC_ENTER_API(FAIL)
278
279
0
    if (NULL == fa_out)
280
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "NULL info-out pointer");
281
282
0
    if (NULL == (plist = H5P_object_verify(fapl_id, H5P_FILE_ACCESS, true)))
283
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Not a valid FAPL ID");
284
285
0
    if (H5FD_ONION != H5P_peek_driver(plist))
286
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Incorrect VFL driver");
287
288
0
    if (NULL == (info_ptr = (const H5FD_onion_fapl_info_t *)H5P_peek_driver_info(plist)))
289
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "bad VFL driver info");
290
291
0
    H5MM_memcpy(fa_out, info_ptr, sizeof(H5FD_onion_fapl_info_t));
292
293
0
done:
294
0
    FUNC_LEAVE_API(ret_value)
295
296
0
} /* end H5Pget_fapl_onion() */
297
298
/*-----------------------------------------------------------------------------
299
 * Function:    H5Pset_fapl_onion
300
 *
301
 * Purpose      Set the file access property list at `fapl_id` to use the
302
 *              Onion virtual file driver with the given configuration.
303
 *              The info structure may be modified or deleted after this call,
304
 *              as its contents are copied into the FAPL.
305
 *
306
 * Return:      Success: Non-negative value (SUCCEED).
307
 *              Failure: Negative value (FAIL).
308
 *-----------------------------------------------------------------------------
309
 */
310
herr_t
311
H5Pset_fapl_onion(hid_t fapl_id, const H5FD_onion_fapl_info_t *fa)
312
0
{
313
0
    H5P_genplist_t *fapl           = NULL;
314
0
    H5P_genplist_t *backing_fapl   = NULL;
315
0
    hid_t           backing_vfd_id = H5I_INVALID_HID;
316
0
    herr_t          ret_value      = SUCCEED;
317
318
0
    FUNC_ENTER_API(FAIL)
319
320
0
    if (NULL == (fapl = H5P_object_verify(fapl_id, H5P_FILE_ACCESS, false)))
321
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Not a valid FAPL ID");
322
0
    if (NULL == fa)
323
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "NULL info pointer");
324
0
    if (H5FD_ONION_FAPL_INFO_VERSION_CURR != fa->version)
325
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid info version");
326
0
    if (!POWER_OF_TWO(fa->page_size))
327
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid info page size");
328
0
    if (fa->page_size < 1)
329
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid info page size");
330
331
0
    if (H5P_DEFAULT == fa->backing_fapl_id) {
332
0
        if (NULL == (backing_fapl = H5P_object_verify(H5P_FILE_ACCESS_DEFAULT, H5P_FILE_ACCESS, true)))
333
0
            HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid backing fapl id");
334
0
    }
335
0
    else {
336
0
        if (NULL == (backing_fapl = H5P_object_verify(fa->backing_fapl_id, H5P_FILE_ACCESS, true)))
337
0
            HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid backing fapl id");
338
0
    }
339
340
    /* The only backing fapl that is currently supported is sec2. If this is changed make sure to update the
341
     * query() callback to check if the backing files set H5FD_FEAT_READWRITE_NO_H5_CALLS. */
342
0
    if ((backing_vfd_id = H5P_peek_driver(backing_fapl)) < 0)
343
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "Can't get VFD from fapl");
344
0
    if (backing_vfd_id != H5FD_SEC2)
345
0
        HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "Onion VFD only supports sec2 backing store");
346
347
0
    if (H5P_set_driver(fapl, H5FD_ONION, (const void *)fa, NULL) < 0)
348
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "Can't set the onion VFD");
349
350
0
done:
351
0
    FUNC_LEAVE_API(ret_value)
352
0
} /* end H5Pset_fapl_onion() */
353
354
/*-------------------------------------------------------------------------
355
 * Function:    H5FD__onion_sb_size
356
 *
357
 * Purpose:     Returns the size of the private information to be stored in
358
 *              the superblock.
359
 *
360
 * Return:      Success:    The super block driver data size
361
 *              Failure:    never fails
362
 *-------------------------------------------------------------------------
363
 */
364
static hsize_t
365
H5FD__onion_sb_size(H5FD_t *_file)
366
0
{
367
0
    H5FD_onion_t *file      = (H5FD_onion_t *)_file;
368
0
    hsize_t       ret_value = 0;
369
370
0
    FUNC_ENTER_PACKAGE_NOERR
371
372
    /* Sanity check */
373
0
    assert(file);
374
0
    assert(file->original_file);
375
376
0
    if (file->original_file)
377
0
        ret_value = H5FD_sb_size(file->original_file);
378
379
0
    FUNC_LEAVE_NOAPI(ret_value)
380
0
} /* end H5FD__onion_sb_size */
381
382
/*-------------------------------------------------------------------------
383
 * Function:    H5FD__onion_sb_encode
384
 *
385
 * Purpose:     Encodes the superblock information for this driver
386
 *
387
 * Return:      SUCCEED/FAIL
388
 *-------------------------------------------------------------------------
389
 */
390
static herr_t
391
H5FD__onion_sb_encode(H5FD_t *_file, char *name /*out*/, unsigned char *buf /*out*/)
392
0
{
393
0
    H5FD_onion_t *file      = (H5FD_onion_t *)_file;
394
0
    herr_t        ret_value = SUCCEED; /* Return value */
395
396
0
    FUNC_ENTER_PACKAGE
397
398
    /* Sanity check */
399
0
    assert(file);
400
0
    assert(file->original_file);
401
402
0
    if (file->original_file && H5FD_sb_encode(file->original_file, name, buf) < 0)
403
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTENCODE, FAIL, "unable to encode the superblock in R/W file");
404
405
0
done:
406
0
    FUNC_LEAVE_NOAPI(ret_value)
407
0
} /* end H5FD__onion_sb_encode */
408
409
/*-------------------------------------------------------------------------
410
 * Function:    H5FD__onion_sb_decode
411
 *
412
 * Purpose:     Decodes the superblock information for this driver
413
 *
414
 * Return:      SUCCEED/FAIL
415
 *-------------------------------------------------------------------------
416
 */
417
static herr_t
418
H5FD__onion_sb_decode(H5FD_t *_file, const char *name, const unsigned char *buf)
419
0
{
420
0
    H5FD_onion_t *file      = (H5FD_onion_t *)_file;
421
0
    herr_t        ret_value = SUCCEED; /* Return value */
422
423
0
    FUNC_ENTER_PACKAGE
424
425
    /* Sanity check */
426
0
    assert(file);
427
0
    assert(file->original_file);
428
429
0
    if (H5FD_sb_load(file->original_file, name, buf) < 0)
430
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTDECODE, FAIL, "unable to decode the superblock in R/W file");
431
432
0
done:
433
0
    FUNC_LEAVE_NOAPI(ret_value)
434
0
} /* end H5FD__onion_sb_decode */
435
436
/*-----------------------------------------------------------------------------
437
 * Write in-memory revision record to appropriate backing file.
438
 * Update information in other in-memory components.
439
 *-----------------------------------------------------------------------------
440
 */
441
static herr_t
442
H5FD__onion_commit_new_revision_record(H5FD_onion_t *file)
443
0
{
444
0
    uint32_t                      checksum  = 0; /* required */
445
0
    size_t                        size      = 0;
446
0
    haddr_t                       phys_addr = 0; /* offset in history file to record start */
447
0
    unsigned char                *buf       = NULL;
448
0
    herr_t                        ret_value = SUCCEED;
449
0
    H5FD_onion_revision_record_t *rec       = &file->curr_rev_record;
450
0
    H5FD_onion_history_t         *history   = &file->history;
451
0
    H5FD_onion_record_loc_t      *new_list  = NULL;
452
453
0
    time_t    rawtime;
454
0
    struct tm tm_buf;
455
456
0
    FUNC_ENTER_PACKAGE
457
458
0
    time(&rawtime);
459
0
    if (HDgmtime_r(&rawtime, &tm_buf) == NULL)
460
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "unable to convert time");
461
0
    strftime(rec->time_of_creation, sizeof(rec->time_of_creation), "%Y%m%dT%H%M%SZ", &tm_buf);
462
463
0
    rec->logical_eof = file->logical_eof;
464
465
0
    if ((true == file->is_open_rw) && (H5FD__onion_merge_revision_index_into_archival_index(
466
0
                                           file->rev_index, &file->curr_rev_record.archival_index) < 0))
467
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTUPDATE, FAIL, "unable to update index to write");
468
469
0
    if (NULL == (buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_REVISION_RECORD + (size_t)rec->comment_size +
470
0
                                   (H5FD_ONION_ENCODED_SIZE_INDEX_ENTRY * rec->archival_index.n_entries))))
471
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate buffer for encoded revision record");
472
473
0
    if (0 == (size = H5FD__onion_revision_record_encode(rec, buf, &checksum)))
474
0
        HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "problem encoding revision record");
475
476
0
    phys_addr = file->onion_eof;
477
0
    if (H5FD_set_eoa(file->onion_file, H5FD_MEM_DRAW, phys_addr + size) < 0)
478
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "can't modify EOA for new revision record");
479
0
    if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, phys_addr, size, buf) < 0)
480
0
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "can't write new revision record");
481
482
0
    file->onion_eof = phys_addr + size;
483
0
    if (true == file->align_history_on_pages)
484
0
        file->onion_eof = (file->onion_eof + (file->header.page_size - 1)) & (~(file->header.page_size - 1));
485
486
    /* Update history info to accommodate new revision */
487
488
0
    if (history->n_revisions == 0) {
489
0
        unsigned char *ptr = buf; /* reuse buffer space to compute checksum */
490
491
0
        assert(history->record_locs == NULL);
492
0
        history->n_revisions = 1;
493
0
        if (NULL == (history->record_locs = H5MM_calloc(sizeof(H5FD_onion_record_loc_t))))
494
0
            HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate temporary record pointer list");
495
496
0
        history->record_locs[0].phys_addr   = phys_addr;
497
0
        history->record_locs[0].record_size = size;
498
0
        UINT64ENCODE(ptr, phys_addr);
499
0
        UINT64ENCODE(ptr, size);
500
0
        history->record_locs[0].checksum = H5_checksum_fletcher32(buf, (size_t)(ptr - buf));
501
        /* TODO: size-reset belongs where? */
502
0
        file->header.history_size += H5FD_ONION_ENCODED_SIZE_RECORD_POINTER;
503
0
    } /* end if no extant revisions in history */
504
0
    else {
505
0
        unsigned char *ptr = buf; /* reuse buffer space to compute checksum */
506
507
0
        assert(history->record_locs != NULL);
508
509
0
        if (NULL == (new_list = H5MM_calloc((history->n_revisions + 1) * sizeof(H5FD_onion_record_loc_t))))
510
0
            HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "unable to resize record pointer list");
511
0
        H5MM_memcpy(new_list, history->record_locs, sizeof(H5FD_onion_record_loc_t) * history->n_revisions);
512
0
        H5MM_xfree(history->record_locs);
513
0
        history->record_locs                                   = new_list;
514
0
        new_list                                               = NULL;
515
0
        history->record_locs[history->n_revisions].phys_addr   = phys_addr;
516
0
        history->record_locs[history->n_revisions].record_size = size;
517
0
        UINT64ENCODE(ptr, phys_addr);
518
0
        UINT64ENCODE(ptr, size);
519
0
        history->record_locs[history->n_revisions].checksum =
520
0
            H5_checksum_fletcher32(buf, (size_t)(ptr - buf));
521
522
0
        file->header.history_size += H5FD_ONION_ENCODED_SIZE_RECORD_POINTER;
523
0
        history->n_revisions += 1;
524
0
    } /* end if one or more revisions present in history */
525
526
0
    file->header.history_addr = file->onion_eof;
527
528
0
done:
529
0
    H5MM_xfree(buf);
530
0
    H5MM_xfree(new_list);
531
532
0
    FUNC_LEAVE_NOAPI(ret_value)
533
0
} /* end H5FD__onion_commit_new_revision_record() */
534
535
/*-----------------------------------------------------------------------------
536
 * Function:    H5FD__onion_close
537
 *
538
 * Purpose:     Close an onionized file
539
 *
540
 * Return:      SUCCEED/FAIL
541
 *-----------------------------------------------------------------------------
542
 */
543
static herr_t
544
H5FD__onion_close(H5FD_t *_file)
545
0
{
546
0
    H5FD_onion_t *file      = (H5FD_onion_t *)_file;
547
0
    herr_t        ret_value = SUCCEED;
548
549
0
    FUNC_ENTER_PACKAGE
550
551
0
    assert(file);
552
553
0
    if (H5FD_ONION_STORE_TARGET_ONION == file->fa.store_target) {
554
555
0
        assert(file->onion_file);
556
557
0
        if (file->is_open_rw) {
558
559
0
            assert(file->recovery_file);
560
561
0
            if (H5FD__onion_commit_new_revision_record(file) < 0)
562
0
                HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "Can't write revision record to backing store");
563
564
0
            if (H5FD__onion_write_final_history(file) < 0)
565
0
                HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "Can't write history to backing store");
566
567
            /* Unset write-lock flag and write header */
568
0
            if (file->is_open_rw)
569
0
                file->header.flags &= (uint32_t)~H5FD_ONION_HEADER_FLAG_WRITE_LOCK;
570
0
            if (H5FD__onion_write_header(&(file->header), file->onion_file) < 0)
571
0
                HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "Can't write updated header to backing store");
572
0
        }
573
0
    }
574
0
    else
575
0
        HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid history target");
576
577
0
done:
578
579
    /* Destroy things as best we can, even if there were earlier errors */
580
0
    if (file->original_file)
581
0
        if (H5FD_close(file->original_file) < 0)
582
0
            HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, FAIL, "can't close backing canon file");
583
0
    if (file->onion_file)
584
0
        if (H5FD_close(file->onion_file) < 0)
585
0
            HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, FAIL, "can't close backing onion file");
586
0
    if (file->recovery_file) {
587
0
        if (H5FD_close(file->recovery_file) < 0)
588
0
            HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, FAIL, "can't close backing recovery file");
589
        /* TODO: Use the VFD's del callback instead of remove (this requires
590
         *       storing a copy of the fapl that was used to open it)
591
         */
592
0
        HDremove(file->recovery_file_name);
593
0
    }
594
0
    if (file->rev_index)
595
0
        if (H5FD__onion_revision_index_destroy(file->rev_index) < 0)
596
0
            HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, FAIL, "can't close revision index");
597
598
0
    H5MM_xfree(file->recovery_file_name);
599
0
    H5MM_xfree(file->history.record_locs);
600
0
    H5MM_xfree(file->curr_rev_record.comment);
601
0
    H5MM_xfree(file->curr_rev_record.archival_index.list);
602
603
0
    file = H5FL_FREE(H5FD_onion_t, file);
604
605
0
    FUNC_LEAVE_NOAPI(ret_value)
606
0
} /* end H5FD__onion_close() */
607
608
/*-------------------------------------------------------------------------
609
 * Function:    H5FD__onion_query
610
 *
611
 * Purpose:     Set the flags that this VFL driver is capable of supporting.
612
 *              (listed in H5FDpublic.h)
613
 *
614
 * Return:      SUCCEED (Can't fail)
615
 *
616
 *-------------------------------------------------------------------------
617
 */
618
static herr_t
619
H5FD__onion_query(const H5FD_t H5_ATTR_UNUSED *_file, unsigned long *flags /* out */)
620
0
{
621
0
    FUNC_ENTER_PACKAGE_NOERR
622
623
    /* Set the VFL feature flags that this driver supports */
624
0
    if (flags) {
625
0
        *flags = 0;
626
0
        *flags |=
627
0
            H5FD_FEAT_READWRITE_NO_H5_CALLS; /* read() and write() callbacks do not re-enter the HDF5 API */
628
0
    }
629
630
0
    FUNC_LEAVE_NOAPI(SUCCEED)
631
0
} /* end H5FD__onion_query() */
632
633
/*-----------------------------------------------------------------------------
634
 * Function:    H5FD__onion_get_eoa
635
 *
636
 * Purpose:     Get end-of-address address.
637
 *
638
 * Return:      Address of first byte past the addressed space
639
 *-----------------------------------------------------------------------------
640
 */
641
static haddr_t
642
H5FD__onion_get_eoa(const H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type)
643
0
{
644
0
    const H5FD_onion_t *file = (const H5FD_onion_t *)_file;
645
646
0
    FUNC_ENTER_PACKAGE_NOERR
647
648
0
    FUNC_LEAVE_NOAPI(file->logical_eoa)
649
0
} /* end H5FD__onion_get_eoa() */
650
651
/*-----------------------------------------------------------------------------
652
 * Function:    H5FD__onion_get_eof
653
 *
654
 * Purpose:     Get end-of-file address.
655
 *
656
 * Return:      Address of first byte past the file-end.
657
 *-----------------------------------------------------------------------------
658
 */
659
static haddr_t
660
H5FD__onion_get_eof(const H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type)
661
0
{
662
0
    const H5FD_onion_t *file = (const H5FD_onion_t *)_file;
663
664
0
    FUNC_ENTER_PACKAGE_NOERR
665
666
0
    FUNC_LEAVE_NOAPI(file->logical_eof)
667
0
} /* end H5FD__onion_get_eof() */
668
669
/*-----------------------------------------------------------------------------
670
 * Sanitize the backing FAPL ID
671
 *-----------------------------------------------------------------------------
672
 */
673
static inline hid_t
674
H5FD__onion_get_legit_fapl_id(hid_t fapl_id)
675
0
{
676
0
    if (H5P_DEFAULT == fapl_id)
677
0
        return H5P_FILE_ACCESS_DEFAULT;
678
0
    else if (true == H5P_isa_class(fapl_id, H5P_FILE_ACCESS))
679
0
        return fapl_id;
680
0
    else
681
0
        return H5I_INVALID_HID;
682
0
}
683
684
/*-----------------------------------------------------------------------------
685
 * Function:    H5FD_onion_create_truncate_onion
686
 *
687
 * Purpose:     Create/truncate HDF5 and onion data for a fresh file
688
 *
689
 *      Special open operation required to instantiate the canonical file and
690
 *      history simultaneously. If successful, the required backing files are
691
 *      craeated and given initial population on the backing store, and the Onion
692
 *      virtual file handle is set; open effects a write-mode open.
693
 *
694
 *      Cannot create 'template' history and proceed with normal write-mode open,
695
 *      as this would in effect create an empty first revision, making the history
696
 *      unintuitive. (create file -> initialize and commit empty first revision
697
 *      (revision 0); any data written to file during the 'create' open, as seen by
698
 *      the user, would be in the second revision (revision 1).)
699
 *
700
 * Return:      SUCCEED/FAIL
701
 *-----------------------------------------------------------------------------
702
 */
703
static herr_t
704
H5FD__onion_create_truncate_onion(H5FD_onion_t *file, const char *filename, const char *name_onion,
705
                                  const char *recovery_file_nameery, unsigned int flags, haddr_t maxaddr)
706
0
{
707
0
    hid_t                         backing_fapl_id = H5I_INVALID_HID;
708
0
    H5FD_onion_header_t          *hdr             = NULL;
709
0
    H5FD_onion_history_t         *history         = NULL;
710
0
    H5FD_onion_revision_record_t *rec             = NULL;
711
0
    unsigned char                *buf             = NULL;
712
0
    size_t                        size            = 0;
713
0
    herr_t                        ret_value       = SUCCEED;
714
715
0
    FUNC_ENTER_PACKAGE
716
717
0
    assert(file != NULL);
718
719
0
    hdr     = &file->header;
720
0
    history = &file->history;
721
0
    rec     = &file->curr_rev_record;
722
723
0
    hdr->flags = H5FD_ONION_HEADER_FLAG_WRITE_LOCK;
724
0
    if (H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT & file->fa.creation_flags)
725
0
        hdr->flags |= H5FD_ONION_HEADER_FLAG_PAGE_ALIGNMENT;
726
727
0
    hdr->origin_eof = 0;
728
729
0
    backing_fapl_id = H5FD__onion_get_legit_fapl_id(file->fa.backing_fapl_id);
730
0
    if (H5I_INVALID_HID == backing_fapl_id)
731
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid backing FAPL ID");
732
733
    /* Create backing files for onion history */
734
0
    if (H5FD_open(false, &file->original_file, filename, flags, backing_fapl_id, maxaddr) < 0)
735
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "cannot open the backing file");
736
0
    if (H5FD_open(false, &file->onion_file, name_onion, flags, backing_fapl_id, maxaddr) < 0)
737
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "cannot open the backing onion file");
738
0
    if (H5FD_open(false, &file->recovery_file, recovery_file_nameery, flags, backing_fapl_id, maxaddr) < 0)
739
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "cannot open the backing file");
740
741
    /* Write "empty" .h5 file contents (signature ONIONEOF) */
742
0
    if (H5FD_set_eoa(file->original_file, H5FD_MEM_DRAW, 8) < 0)
743
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "can't extend EOA");
744
0
    if (H5FD_write(file->original_file, H5FD_MEM_DRAW, 0, 8, "ONIONEOF") < 0)
745
0
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "cannot write header to the backing h5 file");
746
747
    /* Write nascent history (with no revisions) to "recovery" */
748
0
    if (NULL == (buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_HISTORY)))
749
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate buffer");
750
0
    size = H5FD__onion_history_encode(history, buf, &history->checksum);
751
0
    if (H5FD_ONION_ENCODED_SIZE_HISTORY != size)
752
0
        HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "can't encode history");
753
0
    if (H5FD_set_eoa(file->recovery_file, H5FD_MEM_DRAW, size) < 0)
754
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "can't extend EOA");
755
0
    if (H5FD_write(file->recovery_file, H5FD_MEM_DRAW, 0, size, buf) < 0)
756
0
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "cannot write history to the backing recovery file");
757
0
    hdr->history_size = size; /* record for later use */
758
0
    H5MM_xfree(buf);
759
0
    buf = NULL;
760
761
    /* Write history header with "no" history.
762
     * Size of the "recovery" history recorded for later use on close.
763
     */
764
0
    if (NULL == (buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_HEADER)))
765
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate buffer");
766
0
    size = H5FD__onion_header_encode(hdr, buf, &hdr->checksum);
767
0
    if (H5FD_ONION_ENCODED_SIZE_HEADER != size)
768
0
        HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "can't encode history header");
769
0
    if (H5FD_set_eoa(file->onion_file, H5FD_MEM_DRAW, size) < 0)
770
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "can't extend EOA");
771
0
    if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, 0, size, buf) < 0)
772
0
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "cannot write header to the backing onion file");
773
0
    file->onion_eof = (haddr_t)size;
774
0
    if (true == file->align_history_on_pages)
775
0
        file->onion_eof = (file->onion_eof + (hdr->page_size - 1)) & (~(hdr->page_size - 1));
776
777
0
    rec->archival_index.list = NULL;
778
779
0
    if (NULL == (file->rev_index = H5FD__onion_revision_index_init(file->fa.page_size)))
780
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "can't initialize revision index");
781
782
0
done:
783
0
    H5MM_xfree(buf);
784
785
0
    if (FAIL == ret_value)
786
0
        HDremove(recovery_file_nameery); /* destroy new temp file, if 'twas created */
787
788
0
    FUNC_LEAVE_NOAPI(ret_value)
789
0
} /* end H5FD__onion_create_truncate_onion() */
790
791
static herr_t
792
H5FD__onion_remove_unused_symbols(char *s)
793
0
{
794
0
    char *d = s;
795
796
0
    FUNC_ENTER_PACKAGE_NOERR
797
798
0
    do {
799
0
        while (*d == '{' || *d == '}' || *d == ' ') {
800
0
            ++d;
801
0
        }
802
0
    } while ((*s++ = *d++));
803
804
0
    FUNC_LEAVE_NOAPI(SUCCEED)
805
0
}
806
807
static herr_t
808
H5FD__onion_parse_config_str(const char *config_str, H5FD_onion_fapl_info_t *fa)
809
0
{
810
0
    char  *config_str_copy = NULL;
811
0
    herr_t ret_value       = SUCCEED;
812
813
0
    FUNC_ENTER_PACKAGE
814
815
0
    if (!strcmp(config_str, ""))
816
0
        HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "configure string can't be empty");
817
818
    /* Initialize to the default values */
819
0
    fa->version          = H5FD_ONION_FAPL_INFO_VERSION_CURR;
820
0
    fa->backing_fapl_id  = H5P_DEFAULT;
821
0
    fa->page_size        = 4;
822
0
    fa->store_target     = H5FD_ONION_STORE_TARGET_ONION;
823
0
    fa->revision_num     = H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST;
824
0
    fa->force_write_open = 0;
825
0
    fa->creation_flags   = 0;
826
0
    strcpy(fa->comment, "initial comment");
827
828
    /* If a single integer is passed in as a string, it's a shortcut for the tools
829
     * (h5repack, h5diff, h5dump).  Otherwise, the string should have curly brackets,
830
     * e.g. {revision_num: 2; page_size: 4;}
831
     */
832
0
    if (config_str[0] != '{')
833
0
        fa->revision_num = (uint64_t)strtoull(config_str, NULL, 10);
834
0
    else {
835
0
        char *token1 = NULL, *token2 = NULL;
836
837
        /* Duplicate the configure string since strtok will mess with it */
838
0
        if (NULL == (config_str_copy = H5MM_strdup(config_str)))
839
0
            HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't duplicate configure string");
840
841
        /* Remove the curly brackets and space from the configure string */
842
0
        H5FD__onion_remove_unused_symbols(config_str_copy);
843
844
        /* The configure string can't be empty after removing the curly brackets */
845
0
        if (!strcmp(config_str_copy, ""))
846
0
            HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "configure string can't be empty");
847
848
0
        token1 = strtok(config_str_copy, ":");
849
0
        token2 = strtok(NULL, ";");
850
851
0
        do {
852
0
            if (token1 && token2) {
853
0
                if (!strcmp(token1, "version")) {
854
0
                    if (!strcmp(token2, "H5FD_ONION_FAPL_INFO_VERSION_CURR"))
855
0
                        fa->version = H5FD_ONION_FAPL_INFO_VERSION_CURR;
856
0
                }
857
0
                else if (!strcmp(token1, "backing_fapl_id")) {
858
0
                    if (!strcmp(token2, "H5P_DEFAULT"))
859
0
                        fa->backing_fapl_id = H5P_DEFAULT;
860
0
                    else if (!strcmp(token2, "H5I_INVALID_HID"))
861
0
                        fa->backing_fapl_id = H5I_INVALID_HID;
862
0
                    else
863
0
                        fa->backing_fapl_id = strtoll(token2, NULL, 10);
864
0
                }
865
0
                else if (!strcmp(token1, "page_size")) {
866
0
                    fa->page_size = (uint32_t)strtoul(token2, NULL, 10);
867
0
                }
868
0
                else if (!strcmp(token1, "revision_num")) {
869
0
                    if (!strcmp(token2, "H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST"))
870
0
                        fa->revision_num = H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST;
871
0
                    else
872
0
                        fa->revision_num = (uint64_t)strtoull(token2, NULL, 10);
873
0
                }
874
0
                else if (!strcmp(token1, "force_write_open")) {
875
0
                    fa->force_write_open = (uint8_t)strtoul(token2, NULL, 10);
876
0
                }
877
0
                else if (!strcmp(token1, "creation_flags")) {
878
0
                    fa->creation_flags = (uint8_t)strtoul(token2, NULL, 10);
879
0
                }
880
0
                else if (!strcmp(token1, "comment")) {
881
0
                    strcpy(fa->comment, token2);
882
0
                }
883
0
                else
884
0
                    HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "unknown token in the configure string: %s",
885
0
                                token1);
886
0
            }
887
888
0
            token1 = strtok(NULL, ":");
889
0
            token2 = strtok(NULL, ";");
890
0
        } while (token1);
891
0
    }
892
893
0
    if (H5P_DEFAULT == fa->backing_fapl_id || H5I_INVALID_HID == fa->backing_fapl_id) {
894
0
        H5P_genclass_t *pclass; /* Property list class to modify */
895
896
0
        if (NULL == (pclass = (H5P_genclass_t *)H5I_object_verify(H5P_FILE_ACCESS, H5I_GENPROP_CLS)))
897
0
            HGOTO_ERROR(H5E_VFL, H5E_BADTYPE, FAIL, "not a property list class");
898
899
        /* Create the new property list */
900
0
        if ((fa->backing_fapl_id = H5P_create_id(pclass, true)) < 0)
901
0
            HGOTO_ERROR(H5E_VFL, H5E_CANTCREATE, FAIL, "unable to create property list");
902
0
    }
903
904
0
done:
905
0
    H5MM_free(config_str_copy);
906
907
0
    FUNC_LEAVE_NOAPI(ret_value)
908
0
}
909
910
/*-----------------------------------------------------------------------------
911
 * Function:    H5FD__onion_open
912
 *
913
 * Purpose:     Open an onionized file
914
 *
915
 * Return:      Success:    A pointer to a new file data structure
916
 *              Failure:    NULL
917
 *-----------------------------------------------------------------------------
918
 */
919
static H5FD_t *
920
H5FD__onion_open(const char *filename, unsigned flags, hid_t fapl_id, haddr_t maxaddr)
921
0
{
922
0
    H5P_genplist_t               *plist                 = NULL;
923
0
    H5FD_onion_t                 *file                  = NULL;
924
0
    const H5FD_onion_fapl_info_t *fa                    = NULL;
925
0
    H5FD_onion_fapl_info_t       *new_fa                = NULL;
926
0
    const char                   *config_str            = NULL;
927
0
    double                        log2_page_size        = 0.0;
928
0
    hid_t                         backing_fapl_id       = H5I_INVALID_HID;
929
0
    char                         *name_onion            = NULL;
930
0
    char                         *recovery_file_nameery = NULL;
931
0
    bool                          new_open              = false;
932
0
    haddr_t                       canon_eof             = 0;
933
0
    H5FD_t                       *ret_value             = NULL;
934
935
0
    FUNC_ENTER_PACKAGE
936
937
    /* Check arguments */
938
0
    if (!filename || !*filename)
939
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid file name");
940
0
    if (0 == maxaddr || HADDR_UNDEF == maxaddr)
941
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, NULL, "bogus maxaddr");
942
0
    assert(H5P_DEFAULT != fapl_id);
943
0
    if (NULL == (plist = (H5P_genplist_t *)H5I_object(fapl_id)))
944
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "not a file access property list");
945
946
    /* This VFD can be invoked by either H5Pset_fapl_onion() or
947
     * H5Pset_driver_by_name(). When invoked by the former, there will be
948
     * driver info to peek at.
949
     */
950
0
    fa = (const H5FD_onion_fapl_info_t *)H5P_peek_driver_info(plist);
951
952
0
    if (NULL == fa) {
953
0
        if (NULL == (config_str = H5P_peek_driver_config_str(plist)))
954
0
            HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "missing VFL driver configure string");
955
956
        /* Allocate a new onion fapl info struct and fill it from the
957
         * configuration string
958
         */
959
0
        if (NULL == (new_fa = H5MM_calloc(sizeof(H5FD_onion_fapl_info_t))))
960
0
            HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "can't allocate memory for onion fapl info struct");
961
0
        if (H5FD__onion_parse_config_str(config_str, new_fa) < 0)
962
0
            HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "failed to parse configure string");
963
964
0
        fa = new_fa;
965
0
    }
966
967
    /* Check for unsupported target values */
968
0
    if (H5FD_ONION_STORE_TARGET_ONION != fa->store_target)
969
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid store target");
970
971
    /* Allocate space for the file struct */
972
0
    if (NULL == (file = H5FL_CALLOC(H5FD_onion_t)))
973
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate file struct");
974
975
    /* Allocate space for onion VFD file names */
976
0
    if (NULL == (name_onion = H5MM_malloc(sizeof(char) * (strlen(filename) + 7))))
977
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate onion name string");
978
0
    snprintf(name_onion, strlen(filename) + 7, "%s.onion", filename);
979
980
0
    if (NULL == (recovery_file_nameery = H5MM_malloc(sizeof(char) * (strlen(name_onion) + 10))))
981
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate recovery name string");
982
0
    snprintf(recovery_file_nameery, strlen(name_onion) + 10, "%s.recovery", name_onion);
983
0
    file->recovery_file_name = recovery_file_nameery;
984
985
0
    if (NULL == (file->recovery_file_name = H5MM_malloc(sizeof(char) * (strlen(name_onion) + 10))))
986
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate recovery name string");
987
0
    snprintf(file->recovery_file_name, strlen(name_onion) + 10, "%s.recovery", name_onion);
988
989
    /* Translate H5P_DEFAULT to a real fapl ID, if necessary */
990
0
    backing_fapl_id = H5FD__onion_get_legit_fapl_id(file->fa.backing_fapl_id);
991
0
    if (H5I_INVALID_HID == backing_fapl_id)
992
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid backing FAPL ID");
993
994
    /* Initialize file structure fields */
995
996
0
    H5MM_memcpy(&(file->fa), fa, sizeof(H5FD_onion_fapl_info_t));
997
998
0
    file->header.version   = H5FD_ONION_HEADER_VERSION_CURR;
999
0
    file->header.page_size = file->fa.page_size; /* guarded on FAPL-set */
1000
1001
0
    file->history.version = H5FD_ONION_HISTORY_VERSION_CURR;
1002
1003
0
    file->curr_rev_record.version                = H5FD_ONION_REVISION_RECORD_VERSION_CURR;
1004
0
    file->curr_rev_record.archival_index.version = H5FD_ONION_ARCHIVAL_INDEX_VERSION_CURR;
1005
1006
    /* Check that the page size is a power of two */
1007
0
    if ((fa->page_size == 0) || ((fa->page_size & (fa->page_size - 1)) != 0))
1008
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "page size is not a power of two");
1009
1010
    /* Assign the page size */
1011
0
    log2_page_size                                      = log2((double)(fa->page_size));
1012
0
    file->curr_rev_record.archival_index.page_size_log2 = (uint32_t)log2_page_size;
1013
1014
    /* Proceed with open. */
1015
1016
0
    if ((H5F_ACC_CREAT | H5F_ACC_TRUNC) & flags) {
1017
1018
        /* Create a new onion file from scratch */
1019
1020
        /* Set flags */
1021
0
        if (fa->creation_flags & H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT) {
1022
0
            file->header.flags |= H5FD_ONION_HEADER_FLAG_PAGE_ALIGNMENT;
1023
0
            file->align_history_on_pages = true;
1024
0
        }
1025
1026
        /* Truncate and create everything as necessary */
1027
0
        if (H5FD__onion_create_truncate_onion(file, filename, name_onion, file->recovery_file_name, flags,
1028
0
                                              maxaddr) < 0)
1029
0
            HGOTO_ERROR(H5E_VFL, H5E_CANTCREATE, NULL, "unable to create/truncate onionized files");
1030
0
        file->is_open_rw = true;
1031
0
    }
1032
0
    else {
1033
1034
        /* Opening an existing onion file */
1035
1036
        /* Open the existing file using the specified fapl */
1037
0
        if (H5FD_open(false, &file->original_file, filename, flags, backing_fapl_id, maxaddr) < 0)
1038
0
            HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "unable to open canonical file (does not exist?)");
1039
1040
        /* Try to open any existing onion file */
1041
0
        if (H5FD_open(true, &file->onion_file, name_onion, flags, backing_fapl_id, maxaddr) < 0)
1042
0
            HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "cannot try opening the backing onion file");
1043
1044
        /* If that didn't work, create a new onion file */
1045
        /* TODO: Move to a new function */
1046
0
        if (NULL == file->onion_file) {
1047
0
            if (H5F_ACC_RDWR & flags) {
1048
0
                H5FD_onion_header_t          *hdr        = NULL;
1049
0
                H5FD_onion_history_t         *history    = NULL;
1050
0
                H5FD_onion_revision_record_t *rec        = NULL;
1051
0
                unsigned char                *head_buf   = NULL;
1052
0
                unsigned char                *hist_buf   = NULL;
1053
0
                size_t                        size       = 0;
1054
0
                size_t                        saved_size = 0;
1055
1056
0
                assert(file != NULL);
1057
1058
0
                hdr     = &file->header;
1059
0
                history = &file->history;
1060
0
                rec     = &file->curr_rev_record;
1061
1062
0
                new_open = true;
1063
1064
0
                if (H5FD_ONION_FAPL_INFO_CREATE_FLAG_ENABLE_PAGE_ALIGNMENT & file->fa.creation_flags) {
1065
0
                    hdr->flags |= H5FD_ONION_HEADER_FLAG_PAGE_ALIGNMENT;
1066
0
                    file->align_history_on_pages = true;
1067
0
                }
1068
1069
0
                if (HADDR_UNDEF == (canon_eof = H5FD_get_eof(file->original_file, H5FD_MEM_DEFAULT)))
1070
0
                    HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, NULL, "cannot get size of canonical file");
1071
0
                if (H5FD_set_eoa(file->original_file, H5FD_MEM_DRAW, canon_eof) < 0)
1072
0
                    HGOTO_ERROR(H5E_VFL, H5E_CANTSET, NULL, "can't extend EOA");
1073
0
                hdr->origin_eof   = canon_eof;
1074
0
                file->logical_eof = canon_eof;
1075
1076
0
                backing_fapl_id = H5FD__onion_get_legit_fapl_id(file->fa.backing_fapl_id);
1077
1078
0
                if (H5I_INVALID_HID == backing_fapl_id)
1079
0
                    HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid backing FAPL ID");
1080
1081
                /* Create backing files for onion history */
1082
0
                if (H5FD_open(false, &file->onion_file, name_onion,
1083
0
                              (H5F_ACC_RDWR | H5F_ACC_CREAT | H5F_ACC_TRUNC), backing_fapl_id, maxaddr) < 0)
1084
0
                    HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "cannot open the backing onion file");
1085
1086
                /* Write history header with "no" history */
1087
0
                hdr->history_size = H5FD_ONION_ENCODED_SIZE_HISTORY; /* record for later use */
1088
0
                hdr->history_addr =
1089
0
                    H5FD_ONION_ENCODED_SIZE_HEADER + 1; /* TODO: comment these 2 or do some other way */
1090
0
                head_buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_HEADER);
1091
0
                if (NULL == head_buf)
1092
0
                    HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "can't allocate buffer");
1093
0
                size = H5FD__onion_header_encode(hdr, head_buf, &hdr->checksum);
1094
0
                if (H5FD_ONION_ENCODED_SIZE_HEADER != size)
1095
0
                    HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "can't encode history header");
1096
1097
0
                hist_buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_HISTORY);
1098
0
                if (NULL == hist_buf)
1099
0
                    HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "can't allocate buffer");
1100
0
                saved_size                = size;
1101
0
                history->n_revisions      = 0;
1102
0
                size                      = H5FD__onion_history_encode(history, hist_buf, &history->checksum);
1103
0
                file->header.history_size = size; /* record for later use */
1104
0
                if (H5FD_ONION_ENCODED_SIZE_HISTORY != size)
1105
0
                    HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "can't encode history");
1106
0
                if (H5FD_set_eoa(file->onion_file, H5FD_MEM_DRAW, saved_size + size + 1) < 0)
1107
0
                    HGOTO_ERROR(H5E_VFL, H5E_CANTSET, NULL, "can't extend EOA");
1108
1109
0
                if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, 0, saved_size, head_buf) < 0)
1110
0
                    HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, NULL,
1111
0
                                "cannot write header to the backing onion file");
1112
1113
0
                file->onion_eof = (haddr_t)saved_size;
1114
0
                if (true == file->align_history_on_pages)
1115
0
                    file->onion_eof = (file->onion_eof + (hdr->page_size - 1)) & (~(hdr->page_size - 1));
1116
1117
0
                rec->archival_index.list = NULL;
1118
1119
0
                file->header.history_addr = file->onion_eof;
1120
1121
                /* Write nascent history (with no revisions) to the backing onion file */
1122
0
                if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, saved_size + 1, size, hist_buf) < 0)
1123
0
                    HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, NULL,
1124
0
                                "cannot write history to the backing onion file");
1125
1126
0
                file->header.history_size = size; /* record for later use */
1127
1128
0
                H5MM_xfree(head_buf);
1129
0
                H5MM_xfree(hist_buf);
1130
0
            }
1131
0
            else
1132
0
                HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "unable to open onion file (does not exist?).");
1133
0
        }
1134
1135
0
        if (HADDR_UNDEF == (canon_eof = H5FD_get_eof(file->original_file, H5FD_MEM_DEFAULT)))
1136
0
            HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, NULL, "cannot get size of canonical file");
1137
0
        if (H5FD_set_eoa(file->original_file, H5FD_MEM_DRAW, canon_eof) < 0)
1138
0
            HGOTO_ERROR(H5E_VFL, H5E_CANTSET, NULL, "can't extend EOA");
1139
1140
        /* Get the history header from the onion file */
1141
0
        if (H5FD__onion_ingest_header(&file->header, file->onion_file, 0) < 0)
1142
0
            HGOTO_ERROR(H5E_VFL, H5E_CANTDECODE, NULL, "can't get history header from backing store");
1143
0
        file->align_history_on_pages =
1144
0
            (file->header.flags & H5FD_ONION_HEADER_FLAG_PAGE_ALIGNMENT) ? true : false;
1145
1146
        /* Opening a file twice in write mode is an error */
1147
0
        if (H5FD_ONION_HEADER_FLAG_WRITE_LOCK & file->header.flags)
1148
0
            HGOTO_ERROR(H5E_VFL, H5E_UNSUPPORTED, NULL, "Can't open file already opened in write-mode");
1149
0
        else {
1150
            /* Read in the history from the onion file */
1151
0
            if (H5FD__onion_ingest_history(&file->history, file->onion_file, file->header.history_addr,
1152
0
                                           file->header.history_size) < 0)
1153
0
                HGOTO_ERROR(H5E_VFL, H5E_CANTDECODE, NULL, "can't get history from backing store");
1154
1155
            /* Sanity check on revision ID */
1156
0
            if (fa->revision_num > file->history.n_revisions &&
1157
0
                fa->revision_num != H5FD_ONION_FAPL_INFO_REVISION_ID_LATEST)
1158
0
                HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "target revision ID out of range");
1159
1160
0
            if (fa->revision_num == 0)
1161
0
                file->curr_rev_record.logical_eof = canon_eof;
1162
0
            else if (file->history.n_revisions > 0 &&
1163
0
                     H5FD__onion_ingest_revision_record(
1164
0
                         &file->curr_rev_record, file->onion_file, &file->history,
1165
0
                         MIN(fa->revision_num - 1, (file->history.n_revisions - 1))) < 0)
1166
0
                HGOTO_ERROR(H5E_VFL, H5E_CANTDECODE, NULL, "can't get revision record from backing store");
1167
1168
0
            if (H5F_ACC_RDWR & flags)
1169
0
                if (H5FD__onion_open_rw(file, flags, maxaddr, new_open) < 0)
1170
0
                    HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "can't write-open write-locked file");
1171
0
        }
1172
0
    } /* End if opening existing file */
1173
1174
    /* Copy comment from FAPL info, if one is given */
1175
0
    if ((H5F_ACC_RDWR | H5F_ACC_CREAT | H5F_ACC_TRUNC) & flags) {
1176
        /* Free the old comment */
1177
0
        file->curr_rev_record.comment = H5MM_xfree(file->curr_rev_record.comment);
1178
1179
        /* The buffer is of size H5FD_ONION_FAPL_INFO_COMMENT_MAX_LEN + 1
1180
         *
1181
         * We're getting this buffer from a fixed-size array in a struct, which
1182
         * will be garbage and not null-terminated if the user isn't careful.
1183
         * Be careful of this and do strndup first to ensure strdup gets a
1184
         * null-terminated string (HDF5 doesn't provide a strnlen call if you
1185
         * don't have one).
1186
         */
1187
0
        if (NULL ==
1188
0
            (file->curr_rev_record.comment = H5MM_strndup(fa->comment, H5FD_ONION_FAPL_INFO_COMMENT_MAX_LEN)))
1189
0
            HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to duplicate comment string");
1190
1191
        /* TODO: Lengths of strings should be size_t */
1192
0
        file->curr_rev_record.comment_size = (uint32_t)strlen(fa->comment) + 1;
1193
0
    }
1194
0
    file->origin_eof  = file->header.origin_eof;
1195
0
    file->logical_eof = MAX(file->curr_rev_record.logical_eof, file->logical_eof);
1196
0
    file->logical_eoa = 0;
1197
1198
0
    file->onion_eof = H5FD_get_eoa(file->onion_file, H5FD_MEM_DRAW);
1199
0
    if (true == file->align_history_on_pages)
1200
0
        file->onion_eof = (file->onion_eof + (file->header.page_size - 1)) & (~(file->header.page_size - 1));
1201
1202
0
    ret_value = (H5FD_t *)file;
1203
1204
0
done:
1205
0
    H5MM_xfree(name_onion);
1206
0
    H5MM_xfree(recovery_file_nameery);
1207
1208
0
    if (config_str && new_fa)
1209
0
        if (fa && fa->backing_fapl_id)
1210
0
            if (H5I_GENPROP_LST == H5I_get_type(fa->backing_fapl_id))
1211
0
                H5I_dec_app_ref(fa->backing_fapl_id);
1212
1213
0
    if ((NULL == ret_value) && file) {
1214
0
        if (file->original_file)
1215
0
            if (H5FD_close(file->original_file) < 0)
1216
0
                HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, NULL, "can't destroy backing canon");
1217
0
        if (file->onion_file)
1218
0
            if (H5FD_close(file->onion_file) < 0)
1219
0
                HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, NULL, "can't destroy backing onion");
1220
0
        if (file->recovery_file)
1221
0
            if (H5FD_close(file->recovery_file) < 0)
1222
0
                HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, NULL, "can't destroy backing recov");
1223
0
        if (file->rev_index)
1224
0
            if (H5FD__onion_revision_index_destroy(file->rev_index) < 0)
1225
0
                HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, NULL, "can't destroy revision index");
1226
1227
0
        H5MM_xfree(file->history.record_locs);
1228
0
        H5MM_xfree(file->recovery_file_name);
1229
0
        H5MM_xfree(file->curr_rev_record.comment);
1230
1231
0
        H5FL_FREE(H5FD_onion_t, file);
1232
0
    }
1233
1234
0
    H5MM_xfree(new_fa);
1235
1236
0
    FUNC_LEAVE_NOAPI(ret_value)
1237
0
} /* end H5FD__onion_open() */
1238
1239
/*-----------------------------------------------------------------------------
1240
 * Function:    H5FD__onion_open_rw
1241
 *
1242
 * Purpose:     Complete onion file-open, handling process for write mode.
1243
 *
1244
 *              Creates recovery file if one does not exist.
1245
 *              Initializes 'live' revision index.
1246
 *              Force write-open is not yet supported (recovery provision) TODO
1247
 *              Establishes write-lock in history header (sets lock flag).
1248
 *
1249
 * Return:      SUCCEED/FAIL
1250
 *-----------------------------------------------------------------------------
1251
 */
1252
static herr_t
1253
H5FD__onion_open_rw(H5FD_onion_t *file, unsigned int flags, haddr_t maxaddr, bool new_open)
1254
0
{
1255
0
    unsigned char *buf       = NULL;
1256
0
    size_t         size      = 0;
1257
0
    uint32_t       checksum  = 0;
1258
0
    herr_t         ret_value = SUCCEED;
1259
1260
0
    FUNC_ENTER_PACKAGE
1261
1262
    /* Guard against simultaneous write-open.
1263
     * TODO: support recovery open with force-write-open flag in FAPL info.
1264
     */
1265
1266
0
    if (file->header.flags & H5FD_ONION_HEADER_FLAG_WRITE_LOCK)
1267
0
        HGOTO_ERROR(H5E_VFL, H5E_UNSUPPORTED, FAIL, "can't write-open write-locked file");
1268
1269
    /* Copy history to recovery file */
1270
0
    if (H5FD_open(false, &file->recovery_file, file->recovery_file_name,
1271
0
                  (flags | H5F_ACC_CREAT | H5F_ACC_TRUNC), file->fa.backing_fapl_id, maxaddr) < 0)
1272
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "unable to create recovery file");
1273
1274
0
    if (0 == (size = H5FD__onion_write_history(&file->history, file->recovery_file, 0, 0)))
1275
0
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "can't write history to recovery file");
1276
0
    if (size != file->header.history_size)
1277
0
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "written history differed from expected size");
1278
1279
    /* Set write-lock flag in onion header */
1280
0
    if (NULL == (buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_HEADER)))
1281
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate space for encoded buffer");
1282
0
    file->header.flags |= H5FD_ONION_HEADER_FLAG_WRITE_LOCK;
1283
0
    if (0 == (size = H5FD__onion_header_encode(&file->header, buf, &checksum)))
1284
0
        HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "problem encoding history header");
1285
0
    if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, 0, size, buf) < 0)
1286
0
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "can't write updated history header");
1287
1288
    /* Prepare revision index and finalize write-mode open */
1289
0
    if (NULL == (file->rev_index = H5FD__onion_revision_index_init(file->fa.page_size)))
1290
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "can't initialize revision index");
1291
0
    file->curr_rev_record.parent_revision_num = file->curr_rev_record.revision_num;
1292
0
    if (!new_open)
1293
0
        file->curr_rev_record.revision_num += 1;
1294
0
    file->is_open_rw = true;
1295
1296
0
done:
1297
0
    if (FAIL == ret_value) {
1298
0
        if (file->recovery_file != NULL) {
1299
0
            if (H5FD_close(file->recovery_file) < 0)
1300
0
                HDONE_ERROR(H5E_VFL, H5E_CANTCLOSEFILE, FAIL, "can't close recovery file");
1301
0
            file->recovery_file = NULL;
1302
0
        }
1303
1304
0
        if (file->rev_index != NULL) {
1305
0
            if (H5FD__onion_revision_index_destroy(file->rev_index) < 0)
1306
0
                HDONE_ERROR(H5E_VFL, H5E_CANTRELEASE, FAIL, "can't destroy revision index");
1307
0
            file->rev_index = NULL;
1308
0
        }
1309
0
    }
1310
1311
0
    H5MM_xfree(buf);
1312
1313
0
    FUNC_LEAVE_NOAPI(ret_value)
1314
0
} /* end H5FD__onion_open_rw() */
1315
1316
/*-----------------------------------------------------------------------------
1317
 * Function:    H5FD__onion_read
1318
 *
1319
 * Purpose:     Read bytes from an onionized file
1320
 *
1321
 * Return:      SUCCEED/FAIL
1322
 *-----------------------------------------------------------------------------
1323
 */
1324
static herr_t
1325
H5FD__onion_read(H5FD_t *_file, H5FD_mem_t type, hid_t H5_ATTR_UNUSED dxpl_id, haddr_t offset, size_t len,
1326
                 void *_buf_out)
1327
0
{
1328
0
    H5FD_onion_t  *file           = (H5FD_onion_t *)_file;
1329
0
    uint64_t       page_0         = 0;
1330
0
    size_t         n_pages        = 0;
1331
0
    uint32_t       page_size      = 0;
1332
0
    uint32_t       page_size_log2 = 0;
1333
0
    size_t         bytes_to_read  = len;
1334
0
    unsigned char *buf_out        = (unsigned char *)_buf_out;
1335
0
    herr_t         ret_value      = SUCCEED;
1336
1337
0
    FUNC_ENTER_PACKAGE
1338
1339
0
    assert(file != NULL);
1340
0
    assert(buf_out != NULL);
1341
1342
0
    if ((uint64_t)(offset + len) > file->logical_eoa)
1343
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Read extends beyond addressed space");
1344
1345
0
    if (0 == len)
1346
0
        goto done;
1347
1348
0
    page_size      = file->header.page_size;
1349
0
    page_size_log2 = file->curr_rev_record.archival_index.page_size_log2;
1350
0
    page_0         = offset >> page_size_log2;
1351
0
    n_pages        = (len + page_size - 1) >> page_size_log2;
1352
1353
    /* Read, page-by-page */
1354
0
    for (size_t i = 0; i < n_pages; i++) {
1355
0
        const H5FD_onion_index_entry_t *entry_out     = NULL;
1356
0
        haddr_t                         page_gap_head = 0; /* start of page to start of buffer */
1357
0
        haddr_t                         page_gap_tail = 0; /* end of buffer to end of page */
1358
0
        size_t                          page_readsize = 0;
1359
0
        uint64_t                        page_i        = page_0 + i;
1360
1361
0
        if (0 == i) {
1362
0
            page_gap_head = offset & (((uint32_t)1 << page_size_log2) - 1);
1363
            /* Check if we need to add an additional page to make up for the page_gap_head */
1364
0
            if (page_gap_head > 0 &&
1365
0
                (page_gap_head + (bytes_to_read % page_size) > page_size || bytes_to_read % page_size == 0)) {
1366
0
                n_pages++;
1367
0
            }
1368
0
        }
1369
1370
0
        if (n_pages - 1 == i)
1371
0
            page_gap_tail = page_size - bytes_to_read - page_gap_head;
1372
1373
0
        page_readsize = (size_t)page_size - page_gap_head - page_gap_tail;
1374
1375
0
        if (true == file->is_open_rw && file->fa.revision_num != 0 &&
1376
0
            H5FD__onion_revision_index_find(file->rev_index, page_i, &entry_out)) {
1377
            /* Page exists in 'live' revision index */
1378
0
            if (H5FD_read(file->onion_file, H5FD_MEM_DRAW, entry_out->phys_addr + page_gap_head,
1379
0
                          page_readsize, buf_out) < 0)
1380
0
                HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get working file data");
1381
0
        }
1382
0
        else if (file->fa.revision_num != 0 &&
1383
0
                 H5FD__onion_archival_index_find(&file->curr_rev_record.archival_index, page_i, &entry_out)) {
1384
            /* Page exists in archival index */
1385
0
            if (H5FD_read(file->onion_file, H5FD_MEM_DRAW, entry_out->phys_addr + page_gap_head,
1386
0
                          page_readsize, buf_out) < 0)
1387
0
                HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get previously-amended file data");
1388
0
        }
1389
0
        else {
1390
            /* Page does not exist in either index */
1391
1392
            /* Casts prevent truncation */
1393
0
            haddr_t addr_start   = (haddr_t)page_i * (haddr_t)page_size + (haddr_t)page_gap_head;
1394
0
            haddr_t overlap_size = (addr_start > file->origin_eof) ? 0 : file->origin_eof - addr_start;
1395
0
            haddr_t read_size    = MIN(overlap_size, page_readsize);
1396
1397
            /* Get all original bytes in page range */
1398
0
            if ((read_size > 0) && H5FD_read(file->original_file, type, addr_start, read_size, buf_out) < 0) {
1399
0
                HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get original file data");
1400
0
            }
1401
1402
            /* Fill with 0s any gaps after end of original bytes
1403
             * and before end of page.
1404
             */
1405
0
            for (size_t j = read_size; j < page_readsize; j++)
1406
0
                buf_out[j] = 0;
1407
0
        }
1408
1409
0
        buf_out += page_readsize;
1410
0
        bytes_to_read -= page_readsize;
1411
0
    } /* end for each page in range */
1412
1413
0
    assert(0 == bytes_to_read);
1414
1415
0
done:
1416
0
    FUNC_LEAVE_NOAPI(ret_value)
1417
0
} /* end H5FD__onion_read() */
1418
1419
/*-----------------------------------------------------------------------------
1420
 * Function:    H5FD__onion_set_eoa
1421
 *
1422
 * Purpose:     Set end-of-address marker of the logical file.
1423
 *
1424
 * Return:      SUCCEED/FAIL
1425
 *-----------------------------------------------------------------------------
1426
 */
1427
static herr_t
1428
H5FD__onion_set_eoa(H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type, haddr_t addr)
1429
0
{
1430
0
    H5FD_onion_t *file = (H5FD_onion_t *)_file;
1431
1432
0
    FUNC_ENTER_PACKAGE_NOERR
1433
1434
0
    file->logical_eoa = addr;
1435
1436
0
    FUNC_LEAVE_NOAPI(SUCCEED)
1437
0
} /* end H5FD__onion_set_eoa() */
1438
1439
/*-----------------------------------------------------------------------------
1440
 * Function:    H5FD__onion_write
1441
 *
1442
 * Purpose:     Write bytes to an onionized file
1443
 *
1444
 * Return:      SUCCEED/FAIL
1445
 *-----------------------------------------------------------------------------
1446
 */
1447
static herr_t
1448
H5FD__onion_write(H5FD_t *_file, H5FD_mem_t type, hid_t H5_ATTR_UNUSED dxpl_id, haddr_t offset, size_t len,
1449
                  const void *_buf)
1450
0
{
1451
0
    H5FD_onion_t        *file           = (H5FD_onion_t *)_file;
1452
0
    uint64_t             page_0         = 0;
1453
0
    size_t               n_pages        = 0;
1454
0
    unsigned char       *page_buf       = NULL;
1455
0
    uint32_t             page_size      = 0;
1456
0
    uint32_t             page_size_log2 = 0;
1457
0
    size_t               bytes_to_write = len;
1458
0
    const unsigned char *buf            = (const unsigned char *)_buf;
1459
0
    herr_t               ret_value      = SUCCEED;
1460
1461
0
    FUNC_ENTER_PACKAGE
1462
1463
0
    assert(file != NULL);
1464
0
    assert(buf != NULL);
1465
0
    assert(file->rev_index != NULL);
1466
0
    assert((uint64_t)(offset + len) <= file->logical_eoa);
1467
1468
0
    if (false == file->is_open_rw)
1469
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Write not allowed if file not opened in write mode");
1470
1471
0
    if (0 == len)
1472
0
        goto done;
1473
1474
0
    page_size      = file->header.page_size;
1475
0
    page_size_log2 = file->curr_rev_record.archival_index.page_size_log2;
1476
0
    page_0         = offset >> page_size_log2;
1477
0
    n_pages        = (len + page_size - 1) >> page_size_log2;
1478
1479
0
    if (NULL == (page_buf = H5MM_calloc(page_size * sizeof(unsigned char))))
1480
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "cannot allocate temporary buffer");
1481
1482
    /* Write, page-by-page */
1483
0
    for (size_t i = 0; i < n_pages; i++) {
1484
0
        const unsigned char            *write_buf = buf;
1485
0
        H5FD_onion_index_entry_t        new_entry;
1486
0
        const H5FD_onion_index_entry_t *entry_out     = NULL;
1487
0
        haddr_t                         page_gap_head = 0; /* start of page to start of buffer */
1488
0
        haddr_t                         page_gap_tail = 0; /* end of buffer to end of page */
1489
0
        size_t                          page_n_used   = 0; /* nbytes from buffer for this page-write */
1490
0
        uint64_t                        page_i        = page_0 + i;
1491
1492
0
        if (0 == i) {
1493
0
            page_gap_head = offset & (((uint32_t)1 << page_size_log2) - 1);
1494
            /* If we have a page_gap_head and the number of bytes to write is
1495
             * evenly divisible by the page size we need to add an additional
1496
             * page to make up for the page_gap_head
1497
             */
1498
0
            if (page_gap_head > 0 && (page_gap_head + (bytes_to_write % page_size) > page_size ||
1499
0
                                      bytes_to_write % page_size == 0)) {
1500
0
                n_pages++;
1501
0
            }
1502
0
        }
1503
0
        if (n_pages - 1 == i)
1504
0
            page_gap_tail = page_size - bytes_to_write - page_gap_head;
1505
0
        page_n_used = page_size - page_gap_head - page_gap_tail;
1506
1507
        /* Modify page in revision index, if present */
1508
0
        if (H5FD__onion_revision_index_find(file->rev_index, page_i, &entry_out)) {
1509
0
            if (page_gap_head | page_gap_tail) {
1510
                /* Copy existing page verbatim. */
1511
0
                if (H5FD_read(file->onion_file, H5FD_MEM_DRAW, entry_out->phys_addr, page_size, page_buf) < 0)
1512
0
                    HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get working file data");
1513
                /* Overlay delta from input buffer onto page buffer. */
1514
0
                H5MM_memcpy(page_buf + page_gap_head, buf, page_n_used);
1515
0
                write_buf = page_buf;
1516
0
            } /* end if partial page */
1517
1518
0
            if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, entry_out->phys_addr, page_size, write_buf) < 0)
1519
0
                HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "write amended page data to backing file");
1520
1521
0
            buf += page_n_used; /* overflow never touched */
1522
0
            bytes_to_write -= page_n_used;
1523
1524
0
            continue;
1525
0
        } /* end if page exists in 'live' revision index */
1526
1527
0
        if (page_gap_head || page_gap_tail) {
1528
            /* Fill gaps with existing data or zeroes. */
1529
0
            if (H5FD__onion_archival_index_find(&file->curr_rev_record.archival_index, page_i, &entry_out)) {
1530
                /* Page exists in archival index */
1531
1532
                /* Copy existing page verbatim */
1533
0
                if (H5FD_read(file->onion_file, H5FD_MEM_DRAW, entry_out->phys_addr, page_size, page_buf) < 0)
1534
0
                    HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get previously-amended data");
1535
0
            }
1536
0
            else {
1537
0
                haddr_t addr_start   = (haddr_t)(page_i * page_size);
1538
0
                haddr_t overlap_size = (addr_start > file->origin_eof) ? 0 : file->origin_eof - addr_start;
1539
0
                haddr_t read_size    = MIN(overlap_size, page_size);
1540
1541
                /* Get all original bytes in page range */
1542
0
                if ((read_size > 0) &&
1543
0
                    H5FD_read(file->original_file, type, addr_start, read_size, page_buf) < 0) {
1544
0
                    HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't get original file data");
1545
0
                }
1546
1547
                /* Fill with 0s any gaps after end of original bytes
1548
                 * or start of page and before start of new data.
1549
                 */
1550
0
                for (size_t j = read_size; j < page_gap_head; j++)
1551
0
                    page_buf[j] = 0;
1552
1553
                /* Fill with 0s any gaps after end of original bytes
1554
                 * or end of new data and before end of page.
1555
                 */
1556
0
                for (size_t j = MAX(read_size, page_size - page_gap_tail); j < page_size; j++)
1557
0
                    page_buf[j] = 0;
1558
0
            } /* end if page exists in neither index */
1559
1560
            /* Copy input buffer to temporary page buffer */
1561
0
            assert((page_size - page_gap_head) >= page_n_used);
1562
0
            H5MM_memcpy(page_buf + page_gap_head, buf, page_n_used);
1563
0
            write_buf = page_buf;
1564
1565
0
        } /* end if data range does not span entire page */
1566
1567
0
        new_entry.logical_page = page_i;
1568
0
        new_entry.phys_addr    = file->onion_eof;
1569
1570
0
        if (H5FD_set_eoa(file->onion_file, H5FD_MEM_DRAW, file->onion_eof + page_size) < 0)
1571
0
            HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "can't modify EOA for new page amendment");
1572
1573
0
        if (H5FD_write(file->onion_file, H5FD_MEM_DRAW, file->onion_eof, page_size, write_buf) < 0)
1574
0
            HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "write amended page data to backing file");
1575
1576
0
        if (H5FD__onion_revision_index_insert(file->rev_index, &new_entry) < 0)
1577
0
            HGOTO_ERROR(H5E_VFL, H5E_CANTINSERT, FAIL, "can't insert new index entry into revision index");
1578
1579
0
        file->onion_eof += page_size;
1580
0
        buf += page_n_used; /* possible overflow never touched */
1581
0
        bytes_to_write -= page_n_used;
1582
1583
0
    } /* end for each page to write */
1584
1585
0
    assert(0 == bytes_to_write);
1586
1587
0
    file->logical_eof = MAX(file->logical_eof, (offset + len));
1588
1589
0
done:
1590
0
    H5MM_xfree(page_buf);
1591
1592
0
    FUNC_LEAVE_NOAPI(ret_value)
1593
0
} /* end H5FD__onion_write() */
1594
1595
/*-------------------------------------------------------------------------
1596
 * Function:    H5FD__onion_ctl
1597
 *
1598
 * Purpose:     Onion VFD version of the ctl callback.
1599
 *
1600
 *              The desired operation is specified by the op_code
1601
 *              parameter.
1602
 *
1603
 *              The flags parameter controls management of op_codes that
1604
 *              are unknown to the callback
1605
 *
1606
 *              The input and output parameters allow op_code specific
1607
 *              input and output
1608
 *
1609
 * Return:      SUCCEED/FAIL
1610
 *-------------------------------------------------------------------------
1611
 */
1612
static herr_t
1613
H5FD__onion_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags, const void H5_ATTR_UNUSED *input,
1614
                void **output)
1615
0
{
1616
0
    H5FD_onion_t *file      = (H5FD_onion_t *)_file;
1617
0
    herr_t        ret_value = SUCCEED;
1618
1619
0
    FUNC_ENTER_PACKAGE
1620
1621
    /* Sanity checks */
1622
0
    assert(file);
1623
1624
0
    switch (op_code) {
1625
0
        case H5FD_CTL_GET_NUM_REVISIONS:
1626
0
            if (!output || !*output)
1627
0
                HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, "the output parameter is null");
1628
1629
0
            **((uint64_t **)output) = file->history.n_revisions;
1630
0
            break;
1631
        /* Unknown op code */
1632
0
        default:
1633
0
            if (flags & H5FD_CTL_FAIL_IF_UNKNOWN_FLAG)
1634
0
                HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, "unknown op_code and fail if unknown flag is set");
1635
0
            break;
1636
0
    }
1637
1638
0
done:
1639
0
    FUNC_LEAVE_NOAPI(ret_value)
1640
0
} /* end H5FD__onion_ctl() */
1641
1642
/*-------------------------------------------------------------------------
1643
 * Function:    H5FDget_onion_revision_count
1644
 *
1645
 * Purpose:     Get the number of revisions in an onion file
1646
 *
1647
 * Return:      SUCCEED/FAIL
1648
 *-------------------------------------------------------------------------
1649
 */
1650
herr_t
1651
H5FDonion_get_revision_count(const char *filename, hid_t fapl_id, uint64_t *revision_count /*out*/)
1652
0
{
1653
0
    H5P_genplist_t *plist     = NULL;
1654
0
    H5FD_t         *file      = NULL;
1655
0
    herr_t          ret_value = SUCCEED;
1656
1657
0
    FUNC_ENTER_API(FAIL)
1658
1659
    /* Check args */
1660
0
    if (!filename || !strcmp(filename, ""))
1661
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not a valid file name");
1662
0
    if (!revision_count)
1663
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "revision count can't be null");
1664
1665
    /* Make sure using the correct driver */
1666
0
    if (NULL == (plist = H5P_object_verify(fapl_id, H5P_FILE_ACCESS, true)))
1667
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not a valid FAPL ID");
1668
0
    if (H5FD_ONION != H5P_peek_driver(plist))
1669
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not a Onion VFL driver");
1670
1671
    /* Open the file with the driver */
1672
0
    if (H5FD_open(false, &file, filename, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF) < 0)
1673
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, FAIL, "unable to open file with onion driver");
1674
1675
    /* Call the private function */
1676
0
    if (H5FD__get_onion_revision_count(file, revision_count) < 0)
1677
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "failed to get the number of revisions");
1678
1679
0
done:
1680
    /* Close H5FD_t structure pointer */
1681
0
    if (file && H5FD_close(file) < 0)
1682
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTCLOSEFILE, FAIL, "unable to close file");
1683
1684
0
    FUNC_LEAVE_API(ret_value)
1685
0
}
1686
1687
/*-------------------------------------------------------------------------
1688
 * Function:    H5FD__get_onion_revision_count
1689
 *
1690
 * Purpose:     Private version of H5FDget_onion_revision_count()
1691
 *
1692
 * Return:      SUCCEED/FAIL
1693
 *-------------------------------------------------------------------------
1694
 */
1695
static herr_t
1696
H5FD__get_onion_revision_count(H5FD_t *file, uint64_t *revision_count)
1697
0
{
1698
0
    uint64_t op_code;
1699
0
    uint64_t flags;
1700
0
    herr_t   ret_value = SUCCEED;
1701
1702
0
    FUNC_ENTER_PACKAGE
1703
1704
0
    assert(file);
1705
0
    assert(revision_count);
1706
1707
0
    op_code = H5FD_CTL_GET_NUM_REVISIONS;
1708
0
    flags   = H5FD_CTL_FAIL_IF_UNKNOWN_FLAG;
1709
1710
    /* Get the number of revisions via the ctl callback */
1711
0
    if (H5FD_ctl(file, op_code, flags, NULL, (void **)&revision_count) < 0)
1712
0
        HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, "VFD ctl request failed");
1713
1714
0
done:
1715
0
    FUNC_LEAVE_NOAPI(ret_value)
1716
0
}
1717
1718
/*-----------------------------------------------------------------------------
1719
 * Function:    H5FD__onion_write_final_history
1720
 *
1721
 * Purpose:     Write final history to appropriate backing file on file close
1722
 *
1723
 * Return:      SUCCEED/FAIL
1724
 *-----------------------------------------------------------------------------
1725
 */
1726
herr_t
1727
H5FD__onion_write_final_history(H5FD_onion_t *file)
1728
0
{
1729
0
    size_t size      = 0;
1730
0
    herr_t ret_value = SUCCEED;
1731
1732
0
    FUNC_ENTER_PACKAGE
1733
1734
    /* TODO: history EOF may not be correct (under what circumstances?) */
1735
0
    if (0 == (size = H5FD__onion_write_history(&(file->history), file->onion_file, file->onion_eof,
1736
0
                                               file->onion_eof)))
1737
0
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "can't write final history");
1738
1739
0
    if (size != file->header.history_size)
1740
0
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "written history differed from expected size");
1741
1742
    /* Is last write operation to history file; no need to extend to page
1743
     * boundary if set to page-align.
1744
     */
1745
0
    file->onion_eof += size;
1746
1747
0
done:
1748
0
    FUNC_LEAVE_NOAPI(ret_value)
1749
0
} /* end H5FD__onion_write_final_history() */