Coverage Report

Created: 2026-03-16 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hdf5/src/H5FDonion_history.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:     Code for the onion file's history
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 "H5FDpkg.h"        /* File drivers                             */
24
#include "H5FDonion_priv.h" /* Onion file driver internals              */
25
#include "H5MMprivate.h"    /* Memory management                        */
26
27
/*-----------------------------------------------------------------------------
28
 * Function:    H5FD__onion_write_history
29
 *
30
 * Purpose:     Read and decode the history information from `raw_file` at
31
 *              `addr` .. `addr + size` (taken from history header), and store
32
 *              the decoded information in the structure at `history_out`.
33
 *
34
 * Returns:     SUCCEED/FAIL
35
 *-----------------------------------------------------------------------------
36
 */
37
herr_t
38
H5FD__onion_ingest_history(H5FD_onion_history_t *history_out, H5FD_t *raw_file, haddr_t addr, haddr_t size)
39
0
{
40
0
    unsigned char *buf       = NULL;
41
0
    uint32_t       sum       = 0;
42
0
    herr_t         ret_value = SUCCEED;
43
44
0
    FUNC_ENTER_PACKAGE
45
46
0
    assert(history_out);
47
0
    assert(raw_file);
48
49
    /* Set early so we can clean up properly on errors */
50
0
    history_out->record_locs = NULL;
51
52
0
    if (H5FD_get_eof(raw_file, H5FD_MEM_DRAW) < (addr + size))
53
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "header indicates history beyond EOF");
54
55
0
    if (NULL == (buf = H5MM_malloc(sizeof(char) * size)))
56
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate buffer space");
57
58
0
    if (H5FD_set_eoa(raw_file, H5FD_MEM_DRAW, (addr + size)) < 0)
59
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTSET, FAIL, "can't modify EOA");
60
61
0
    if (H5FD_read(raw_file, H5FD_MEM_DRAW, addr, size, buf) < 0)
62
0
        HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't read history from file");
63
64
0
    if (H5FD__onion_history_decode(buf, history_out) != size)
65
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTDECODE, FAIL, "can't decode history (initial)");
66
67
0
    sum = H5_checksum_fletcher32(buf, size - 4);
68
0
    if (history_out->checksum != sum)
69
0
        HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "checksum mismatch between buffer and stored");
70
71
0
    if (history_out->n_revisions > 0)
72
0
        if (NULL == (history_out->record_locs =
73
0
                         H5MM_calloc(history_out->n_revisions * sizeof(H5FD_onion_record_loc_t))))
74
0
            HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate record pointer list");
75
76
0
    if (H5FD__onion_history_decode(buf, history_out) != size)
77
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTDECODE, FAIL, "can't decode history (final)");
78
79
0
done:
80
0
    H5MM_xfree(buf);
81
0
    if (ret_value < 0)
82
0
        H5MM_xfree(history_out->record_locs);
83
84
0
    FUNC_LEAVE_NOAPI(ret_value)
85
0
} /* end H5FD__onion_ingest_history() */
86
87
/*-----------------------------------------------------------------------------
88
 * Function:    H5FD__onion_write_history
89
 *
90
 * Purpose:     Encode and write history to file at the given address.
91
 *
92
 * Returns:     Success:    Number of bytes written to destination file (always non-zero)
93
 *              Failure:    0
94
 *-----------------------------------------------------------------------------
95
 */
96
uint64_t
97
H5FD__onion_write_history(H5FD_onion_history_t *history, H5FD_t *file, haddr_t off_start,
98
                          haddr_t filesize_curr)
99
0
{
100
0
    uint32_t       _sum      = 0; /* Required by the API call but unused here */
101
0
    uint64_t       size      = 0;
102
0
    unsigned char *buf       = NULL;
103
0
    uint64_t       ret_value = 0;
104
105
0
    FUNC_ENTER_PACKAGE
106
107
0
    if (NULL == (buf = H5MM_malloc(H5FD_ONION_ENCODED_SIZE_HISTORY +
108
0
                                   (H5FD_ONION_ENCODED_SIZE_RECORD_POINTER * history->n_revisions))))
109
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, 0, "can't allocate buffer for updated history");
110
111
0
    if (0 == (size = H5FD__onion_history_encode(history, buf, &_sum)))
112
0
        HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, 0, "problem encoding updated history");
113
114
0
    if ((size + off_start > filesize_curr) && (H5FD_set_eoa(file, H5FD_MEM_DRAW, off_start + size) < 0))
115
0
        HGOTO_ERROR(H5E_VFL, H5E_CANTSET, 0, "can't modify EOA for updated history");
116
117
0
    if (H5FD_write(file, H5FD_MEM_DRAW, off_start, size, buf) < 0)
118
0
        HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, 0, "can't write history as intended");
119
120
0
    ret_value = size;
121
122
0
done:
123
0
    H5MM_xfree(buf);
124
125
0
    FUNC_LEAVE_NOAPI(ret_value)
126
0
} /* end H5FD__onion_write_history() */
127
128
/*-----------------------------------------------------------------------------
129
 * Function:    H5FD__onion_history_decode
130
 *
131
 * Purpose:     Attempt to read a buffer and store it as a history
132
 *              structure.
133
 *
134
 *              Implementation must correspond with
135
 *              H5FD__onion_history_encode().
136
 *
137
 *              MUST BE CALLED TWICE:
138
 *              On the first call, n_records in the destination structure must
139
 *              be zero, and record_locs be NULL.
140
 *
141
 *              If the buffer is well-formed, the destination structure is
142
 *              tentatively populated with fixed-size values, and the number of
143
 *              bytes read are returned.
144
 *
145
 *              Prior to the second call, the user must allocate space for
146
 *              record_locs to hold n_records record-pointer structs.
147
 *
148
 *              Then the decode operation is called a second time, and all
149
 *              components will be populated (and again number of bytes read is
150
 *              returned).
151
 *
152
 * Return:      Success:    Number of bytes read from buffer
153
 *              Failure:    0
154
 *-----------------------------------------------------------------------------
155
 */
156
size_t H5_ATTR_NO_OPTIMIZE
157
H5FD__onion_history_decode(unsigned char *buf, H5FD_onion_history_t *history)
158
0
{
159
0
    uint32_t       ui32        = 0;
160
0
    uint32_t       sum         = 0;
161
0
    uint64_t       ui64        = 0;
162
0
    uint64_t       n_revisions = 0;
163
0
    uint8_t       *ui8p        = NULL;
164
0
    unsigned char *ptr         = NULL;
165
0
    size_t         ret_value   = 0;
166
167
0
    FUNC_ENTER_PACKAGE
168
169
0
    assert(buf != NULL);
170
0
    assert(history != NULL);
171
0
    assert(H5FD_ONION_HISTORY_VERSION_CURR == history->version);
172
173
0
    if (strncmp((const char *)buf, H5FD_ONION_HISTORY_SIGNATURE, 4))
174
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, 0, "invalid signature");
175
176
0
    if (H5FD_ONION_HISTORY_VERSION_CURR != buf[4])
177
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, 0, "invalid version");
178
179
0
    ptr = buf + 8;
180
181
0
    H5MM_memcpy(&ui64, ptr, 8);
182
0
    ui8p = (uint8_t *)&ui64;
183
0
    UINT64DECODE(ui8p, n_revisions);
184
0
    ptr += 8;
185
186
0
    if (0 == history->n_revisions) {
187
0
        history->n_revisions = n_revisions;
188
0
        ptr += H5FD_ONION_ENCODED_SIZE_RECORD_POINTER * n_revisions;
189
0
    }
190
0
    else {
191
0
        if (history->n_revisions != n_revisions)
192
0
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, 0,
193
0
                        "history argument suggests different revision count than encoded buffer");
194
0
        if (NULL == history->record_locs)
195
0
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, 0, "list is NULL -- cannot populate");
196
197
0
        for (uint64_t i = 0; i < n_revisions; i++) {
198
0
            H5FD_onion_record_loc_t *rloc = &history->record_locs[i];
199
200
            /* Decode into appropriately sized types, then do a checked
201
             * assignment to the struct value. We don't have access to
202
             * the H5F_t struct for this file, so we can't use the
203
             * offset/length macros in H5Fprivate.h.
204
             */
205
0
            uint64_t record_size;
206
0
            uint64_t phys_addr;
207
208
0
            H5MM_memcpy(&ui64, ptr, 8);
209
0
            ui8p = (uint8_t *)&ui64;
210
0
            UINT64DECODE(ui8p, phys_addr);
211
0
            H5_CHECKED_ASSIGN(rloc->phys_addr, haddr_t, phys_addr, uint64_t);
212
0
            ptr += 8;
213
214
0
            H5MM_memcpy(&ui64, ptr, 8);
215
0
            ui8p = (uint8_t *)&ui64;
216
0
            UINT64DECODE(ui8p, record_size);
217
0
            H5_CHECKED_ASSIGN(rloc->record_size, hsize_t, record_size, uint64_t);
218
0
            ptr += 8;
219
220
0
            H5MM_memcpy(&ui32, ptr, 4);
221
0
            ui8p = (uint8_t *)&ui32;
222
0
            UINT32DECODE(ui8p, rloc->checksum);
223
0
            ptr += 4;
224
0
        }
225
0
    }
226
227
0
    sum = H5_checksum_fletcher32(buf, (size_t)(ptr - buf));
228
229
0
    H5MM_memcpy(&ui32, ptr, 4);
230
0
    ui8p = (uint8_t *)&ui32;
231
0
    UINT32DECODE(ui8p, history->checksum);
232
0
    ptr += 4;
233
234
0
    if (sum != history->checksum)
235
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, 0, "checksum mismatch");
236
237
0
    ret_value = (size_t)(ptr - buf);
238
239
0
done:
240
0
    FUNC_LEAVE_NOAPI(ret_value)
241
0
} /* end H5FD__onion_history_decode() */
242
243
/*-----------------------------------------------------------------------------
244
 * Function:    H5FD__onion_history_encode
245
 *
246
 * Purpose:     Write history structure to the given buffer.
247
 *              All multi-byte elements are stored in little-endian word order.
248
 *
249
 *              Implementation must correspond with
250
 *              H5FD__onion_history_decode().
251
 *
252
 *              The destination buffer must be sufficiently large to hold the
253
 *              encoded contents.
254
 *              (Hint: `sizeof(history struct) +
255
 *              sizeof(record-pointer-struct) * n_records)` guarantees
256
 *              ample/excess space.)
257
 *
258
 * Return:      Number of bytes written to buffer.
259
 *              The checksum of the generated buffer contents (excluding the
260
 *              checksum itself) is stored in the pointer `checksum`).
261
 *-----------------------------------------------------------------------------
262
 */
263
size_t
264
H5FD__onion_history_encode(H5FD_onion_history_t *history, unsigned char *buf, uint32_t *checksum)
265
0
{
266
0
    unsigned char *ptr      = buf;
267
0
    size_t         vers_u32 = (uint32_t)history->version; /* pad out unused bytes */
268
269
0
    FUNC_ENTER_PACKAGE_NOERR
270
271
0
    assert(history != NULL);
272
0
    assert(H5FD_ONION_HISTORY_VERSION_CURR == history->version);
273
0
    assert(buf != NULL);
274
0
    assert(checksum != NULL);
275
276
0
    H5MM_memcpy(ptr, H5FD_ONION_HISTORY_SIGNATURE, 4);
277
0
    ptr += 4;
278
0
    UINT32ENCODE(ptr, vers_u32);
279
0
    UINT64ENCODE(ptr, history->n_revisions);
280
0
    if (history->n_revisions > 0) {
281
0
        assert(history->record_locs != NULL);
282
0
        for (uint64_t i = 0; i < history->n_revisions; i++) {
283
0
            H5FD_onion_record_loc_t *rloc = &history->record_locs[i];
284
285
            /* Do a checked assignment from the struct value into appropriately
286
             * sized types. We don't have access to the H5F_t struct for this
287
             * file, so we can't use the offset/length macros in H5Fprivate.h.
288
             */
289
0
            uint64_t phys_addr;
290
0
            uint64_t record_size;
291
292
0
            H5_CHECKED_ASSIGN(phys_addr, uint64_t, rloc->phys_addr, haddr_t);
293
0
            H5_CHECKED_ASSIGN(record_size, uint64_t, rloc->record_size, hsize_t);
294
295
0
            UINT64ENCODE(ptr, phys_addr);
296
0
            UINT64ENCODE(ptr, record_size);
297
0
            UINT32ENCODE(ptr, rloc->checksum);
298
0
        }
299
0
    }
300
0
    *checksum = H5_checksum_fletcher32(buf, (size_t)(ptr - buf));
301
0
    UINT32ENCODE(ptr, *checksum);
302
303
0
    FUNC_LEAVE_NOAPI((size_t)(ptr - buf))
304
0
} /* end H5FD__onion_history_encode() */