Coverage Report

Created: 2025-06-24 06:40

/src/systemd/src/libsystemd/sd-journal/journal-verify.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3
#include <fcntl.h>
4
#include <sys/mman.h>
5
6
#include "alloc-util.h"
7
#include "ansi-color.h"
8
#include "compress.h"
9
#include "fd-util.h"
10
#include "fileio.h"
11
#include "fs-util.h"
12
#include "gcrypt-util.h"
13
#include "journal-authenticate.h"
14
#include "journal-def.h"
15
#include "journal-file.h"
16
#include "journal-verify.h"
17
#include "log.h"
18
#include "terminal-util.h"
19
#include "time-util.h"
20
#include "tmpfile-util.h"
21
22
0
static void draw_progress(uint64_t p, usec_t *last_usec) {
23
0
        unsigned n, i, j, k;
24
0
        usec_t z, x;
25
26
0
        if (!on_tty())
27
0
                return;
28
29
0
        z = now(CLOCK_MONOTONIC);
30
0
        x = *last_usec;
31
32
0
        if (x != 0 && x + 40 * USEC_PER_MSEC > z)
33
0
                return;
34
35
0
        *last_usec = z;
36
37
0
        n = (3 * columns()) / 4;
38
0
        j = (n * (unsigned) p) / 65535ULL;
39
0
        k = n - j;
40
41
0
        fputs("\r", stdout);
42
0
        if (colors_enabled())
43
0
                fputs("\x1B[?25l", stdout);
44
45
0
        fputs(ansi_highlight_green(), stdout);
46
47
0
        for (i = 0; i < j; i++)
48
0
                fputs("\xe2\x96\x88", stdout);
49
50
0
        fputs(ansi_normal(), stdout);
51
52
0
        for (i = 0; i < k; i++)
53
0
                fputs("\xe2\x96\x91", stdout);
54
55
0
        printf(" %3"PRIu64"%%", 100U * p / 65535U);
56
57
0
        fputs("\r", stdout);
58
0
        if (colors_enabled())
59
0
                fputs("\x1B[?25h", stdout);
60
61
0
        fflush(stdout);
62
0
}
63
64
0
static uint64_t scale_progress(uint64_t scale, uint64_t p, uint64_t m) {
65
        /* Calculates scale * p / m, but handles m == 0 safely, and saturates.
66
         * Currently all callers use m >= 1, but we keep the check to be defensive.
67
         */
68
69
0
        if (p >= m || m == 0)
70
0
                return scale;
71
72
0
        return scale * p / m;
73
0
}
74
75
0
static void flush_progress(void) {
76
0
        unsigned n, i;
77
78
0
        if (!on_tty())
79
0
                return;
80
81
0
        n = (3 * columns()) / 4;
82
83
0
        putchar('\r');
84
85
0
        for (i = 0; i < n + 5; i++)
86
0
                putchar(' ');
87
88
0
        putchar('\r');
89
0
        fflush(stdout);
90
0
}
91
92
0
#define debug(_offset, _fmt, ...) do {                                  \
93
0
                flush_progress();                                       \
94
0
                log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__);     \
95
0
        } while (0)
96
97
0
#define warning(_offset, _fmt, ...) do {                                \
98
0
                flush_progress();                                       \
99
0
                log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__);   \
100
0
        } while (0)
101
102
0
#define error(_offset, _fmt, ...) do {                                  \
103
0
                flush_progress();                                       \
104
0
                log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
105
0
        } while (0)
106
107
0
#define error_errno(_offset, error, _fmt, ...) do {               \
108
0
                flush_progress();                                       \
109
0
                log_error_errno(error, OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
110
0
        } while (0)
111
112
0
static int hash_payload(JournalFile *f, Object *o, uint64_t offset, const uint8_t *src, uint64_t size, uint64_t *res_hash) {
113
0
        Compression c;
114
0
        int r;
115
116
0
        assert(o);
117
0
        assert(src);
118
0
        assert(res_hash);
119
120
0
        c = COMPRESSION_FROM_OBJECT(o);
121
0
        if (c < 0)
122
0
                return -EBADMSG;
123
0
        if (c != COMPRESSION_NONE) {
124
0
                _cleanup_free_ void *b = NULL;
125
0
                size_t b_size;
126
127
0
                r = decompress_blob(c, src, size, &b, &b_size, 0);
128
0
                if (r < 0) {
129
0
                        error_errno(offset, r, "%s decompression failed: %m",
130
0
                                    compression_to_string(c));
131
0
                        return r;
132
0
                }
133
134
0
                *res_hash = journal_file_hash_data(f, b, b_size);
135
0
        } else
136
0
                *res_hash = journal_file_hash_data(f, src, size);
137
138
0
        return 0;
139
0
}
140
141
0
static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) {
142
0
        assert(f);
143
0
        assert(offset);
144
0
        assert(o);
145
146
        /* This does various superficial tests about the length an
147
         * possible field values. It does not follow any references to
148
         * other objects. */
149
150
0
        if ((o->object.flags & _OBJECT_COMPRESSED_MASK) != 0 &&
151
0
            o->object.type != OBJECT_DATA) {
152
0
                error(offset,
153
0
                      "Found compressed object of type %s that isn't of type data, which is not allowed.",
154
0
                      journal_object_type_to_string(o->object.type));
155
0
                return -EBADMSG;
156
0
        }
157
158
0
        switch (o->object.type) {
159
160
0
        case OBJECT_DATA: {
161
0
                uint64_t h1, h2;
162
0
                int r;
163
164
0
                if (le64toh(o->data.entry_offset) == 0)
165
0
                        debug(offset, "Unused data (entry_offset==0)");
166
167
0
                if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) {
168
0
                        error(offset, "Bad n_entries: %"PRIu64, le64toh(o->data.n_entries));
169
0
                        return -EBADMSG;
170
0
                }
171
172
0
                if (le64toh(o->object.size) - journal_file_data_payload_offset(f) <= 0) {
173
0
                        error(offset, "Bad object size (<= %zu): %"PRIu64,
174
0
                              journal_file_data_payload_offset(f),
175
0
                              le64toh(o->object.size));
176
0
                        return -EBADMSG;
177
0
                }
178
179
0
                h1 = le64toh(o->data.hash);
180
0
                r = hash_payload(f, o, offset, journal_file_data_payload_field(f, o),
181
0
                                 le64toh(o->object.size) - journal_file_data_payload_offset(f),
182
0
                                 &h2);
183
0
                if (r < 0)
184
0
                        return r;
185
186
0
                if (h1 != h2) {
187
0
                        error(offset, "Invalid hash (%08" PRIx64 " vs. %08" PRIx64 ")", h1, h2);
188
0
                        return -EBADMSG;
189
0
                }
190
191
0
                if (!VALID64(le64toh(o->data.next_hash_offset)) ||
192
0
                    !VALID64(le64toh(o->data.next_field_offset)) ||
193
0
                    !VALID64(le64toh(o->data.entry_offset)) ||
194
0
                    !VALID64(le64toh(o->data.entry_array_offset))) {
195
0
                        error(offset, "Invalid offset (next_hash_offset="OFSfmt", next_field_offset="OFSfmt", entry_offset="OFSfmt", entry_array_offset="OFSfmt,
196
0
                              le64toh(o->data.next_hash_offset),
197
0
                              le64toh(o->data.next_field_offset),
198
0
                              le64toh(o->data.entry_offset),
199
0
                              le64toh(o->data.entry_array_offset));
200
0
                        return -EBADMSG;
201
0
                }
202
203
0
                break;
204
0
        }
205
206
0
        case OBJECT_FIELD: {
207
0
                uint64_t h1, h2;
208
0
                int r;
209
210
0
                if (le64toh(o->object.size) - offsetof(Object, field.payload) <= 0) {
211
0
                        error(offset,
212
0
                              "Bad field size (<= %zu): %"PRIu64,
213
0
                              offsetof(Object, field.payload),
214
0
                              le64toh(o->object.size));
215
0
                        return -EBADMSG;
216
0
                }
217
218
0
                h1 = le64toh(o->field.hash);
219
0
                r = hash_payload(f, o, offset, o->field.payload,
220
0
                                 le64toh(o->object.size) - offsetof(Object, field.payload),
221
0
                                 &h2);
222
0
                if (r < 0)
223
0
                        return r;
224
225
0
                if (h1 != h2) {
226
0
                        error(offset, "Invalid hash (%08" PRIx64 " vs. %08" PRIx64 ")", h1, h2);
227
0
                        return -EBADMSG;
228
0
                }
229
230
0
                if (!VALID64(le64toh(o->field.next_hash_offset)) ||
231
0
                    !VALID64(le64toh(o->field.head_data_offset))) {
232
0
                        error(offset,
233
0
                              "Invalid offset (next_hash_offset="OFSfmt", head_data_offset="OFSfmt,
234
0
                              le64toh(o->field.next_hash_offset),
235
0
                              le64toh(o->field.head_data_offset));
236
0
                        return -EBADMSG;
237
0
                }
238
0
                break;
239
0
        }
240
241
0
        case OBJECT_ENTRY:
242
0
                if ((le64toh(o->object.size) - offsetof(Object, entry.items)) % journal_file_entry_item_size(f) != 0) {
243
0
                        error(offset,
244
0
                              "Bad entry size (<= %zu): %"PRIu64,
245
0
                              offsetof(Object, entry.items),
246
0
                              le64toh(o->object.size));
247
0
                        return -EBADMSG;
248
0
                }
249
250
0
                if ((le64toh(o->object.size) - offsetof(Object, entry.items)) / journal_file_entry_item_size(f) <= 0) {
251
0
                        error(offset,
252
0
                              "Invalid number items in entry: %"PRIu64,
253
0
                              (le64toh(o->object.size) - offsetof(Object, entry.items)) / journal_file_entry_item_size(f));
254
0
                        return -EBADMSG;
255
0
                }
256
257
0
                if (le64toh(o->entry.seqnum) <= 0) {
258
0
                        error(offset,
259
0
                              "Invalid entry seqnum: %"PRIx64,
260
0
                              le64toh(o->entry.seqnum));
261
0
                        return -EBADMSG;
262
0
                }
263
264
0
                if (!VALID_REALTIME(le64toh(o->entry.realtime))) {
265
0
                        error(offset,
266
0
                              "Invalid entry realtime timestamp: %"PRIu64,
267
0
                              le64toh(o->entry.realtime));
268
0
                        return -EBADMSG;
269
0
                }
270
271
0
                if (!VALID_MONOTONIC(le64toh(o->entry.monotonic))) {
272
0
                        error(offset,
273
0
                              "Invalid entry monotonic timestamp: %"PRIu64,
274
0
                              le64toh(o->entry.monotonic));
275
0
                        return -EBADMSG;
276
0
                }
277
278
0
                for (uint64_t i = 0; i < journal_file_entry_n_items(f, o); i++) {
279
0
                        if (journal_file_entry_item_object_offset(f, o, i) == 0 ||
280
0
                            !VALID64(journal_file_entry_item_object_offset(f, o, i))) {
281
0
                                error(offset,
282
0
                                      "Invalid entry item (%"PRIu64"/%"PRIu64") offset: "OFSfmt,
283
0
                                      i, journal_file_entry_n_items(f, o),
284
0
                                      journal_file_entry_item_object_offset(f, o, i));
285
0
                                return -EBADMSG;
286
0
                        }
287
0
                }
288
289
0
                break;
290
291
0
        case OBJECT_DATA_HASH_TABLE:
292
0
        case OBJECT_FIELD_HASH_TABLE:
293
0
                if ((le64toh(o->object.size) - offsetof(Object, hash_table.items)) % sizeof(HashItem) != 0 ||
294
0
                    (le64toh(o->object.size) - offsetof(Object, hash_table.items)) / sizeof(HashItem) <= 0) {
295
0
                        error(offset,
296
0
                              "Invalid %s size: %"PRIu64,
297
0
                              journal_object_type_to_string(o->object.type),
298
0
                              le64toh(o->object.size));
299
0
                        return -EBADMSG;
300
0
                }
301
302
0
                for (uint64_t i = 0; i < journal_file_hash_table_n_items(o); i++) {
303
0
                        if (o->hash_table.items[i].head_hash_offset != 0 &&
304
0
                            !VALID64(le64toh(o->hash_table.items[i].head_hash_offset))) {
305
0
                                error(offset,
306
0
                                      "Invalid %s hash table item (%"PRIu64"/%"PRIu64") head_hash_offset: "OFSfmt,
307
0
                                      journal_object_type_to_string(o->object.type),
308
0
                                      i, journal_file_hash_table_n_items(o),
309
0
                                      le64toh(o->hash_table.items[i].head_hash_offset));
310
0
                                return -EBADMSG;
311
0
                        }
312
0
                        if (o->hash_table.items[i].tail_hash_offset != 0 &&
313
0
                            !VALID64(le64toh(o->hash_table.items[i].tail_hash_offset))) {
314
0
                                error(offset,
315
0
                                      "Invalid %s hash table item (%"PRIu64"/%"PRIu64") tail_hash_offset: "OFSfmt,
316
0
                                      journal_object_type_to_string(o->object.type),
317
0
                                      i, journal_file_hash_table_n_items(o),
318
0
                                      le64toh(o->hash_table.items[i].tail_hash_offset));
319
0
                                return -EBADMSG;
320
0
                        }
321
322
0
                        if ((o->hash_table.items[i].head_hash_offset != 0) !=
323
0
                            (o->hash_table.items[i].tail_hash_offset != 0)) {
324
0
                                error(offset,
325
0
                                      "Invalid %s hash table item (%"PRIu64"/%"PRIu64"): head_hash_offset="OFSfmt" tail_hash_offset="OFSfmt,
326
0
                                      journal_object_type_to_string(o->object.type),
327
0
                                      i, journal_file_hash_table_n_items(o),
328
0
                                      le64toh(o->hash_table.items[i].head_hash_offset),
329
0
                                      le64toh(o->hash_table.items[i].tail_hash_offset));
330
0
                                return -EBADMSG;
331
0
                        }
332
0
                }
333
334
0
                break;
335
336
0
        case OBJECT_ENTRY_ARRAY:
337
0
                if ((le64toh(o->object.size) - offsetof(Object, entry_array.items)) % journal_file_entry_array_item_size(f) != 0 ||
338
0
                    (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / journal_file_entry_array_item_size(f) <= 0) {
339
0
                        error(offset,
340
0
                              "Invalid object entry array size: %"PRIu64,
341
0
                              le64toh(o->object.size));
342
0
                        return -EBADMSG;
343
0
                }
344
345
0
                if (!VALID64(le64toh(o->entry_array.next_entry_array_offset))) {
346
0
                        error(offset,
347
0
                              "Invalid object entry array next_entry_array_offset: "OFSfmt,
348
0
                              le64toh(o->entry_array.next_entry_array_offset));
349
0
                        return -EBADMSG;
350
0
                }
351
352
0
                for (uint64_t i = 0; i < journal_file_entry_array_n_items(f, o); i++) {
353
0
                        uint64_t q = journal_file_entry_array_item(f, o, i);
354
0
                        if (q != 0 && !VALID64(q)) {
355
0
                                error(offset,
356
0
                                      "Invalid object entry array item (%"PRIu64"/%"PRIu64"): "OFSfmt,
357
0
                                      i, journal_file_entry_array_n_items(f, o), q);
358
0
                                return -EBADMSG;
359
0
                        }
360
0
                }
361
362
0
                break;
363
364
0
        case OBJECT_TAG:
365
0
                if (le64toh(o->object.size) != sizeof(TagObject)) {
366
0
                        error(offset,
367
0
                              "Invalid object tag size: %"PRIu64,
368
0
                              le64toh(o->object.size));
369
0
                        return -EBADMSG;
370
0
                }
371
372
0
                if (!VALID_EPOCH(le64toh(o->tag.epoch))) {
373
0
                        error(offset,
374
0
                              "Invalid object tag epoch: %"PRIu64,
375
0
                              le64toh(o->tag.epoch));
376
0
                        return -EBADMSG;
377
0
                }
378
379
0
                break;
380
0
        }
381
382
0
        return 0;
383
0
}
384
385
0
static int write_uint64(FILE *fp, uint64_t p) {
386
0
        if (fwrite(&p, sizeof(p), 1, fp) != 1)
387
0
                return -EIO;
388
389
0
        return 0;
390
0
}
391
392
0
static int contains_uint64(MMapFileDescriptor *f, uint64_t n, uint64_t p) {
393
0
        uint64_t a, b;
394
0
        int r;
395
396
0
        assert(f);
397
398
        /* Bisection ... */
399
400
0
        a = 0; b = n;
401
0
        while (a < b) {
402
0
                uint64_t c, *z;
403
404
0
                c = (a + b) / 2;
405
406
0
                r = mmap_cache_fd_get(f, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z);
407
0
                if (r < 0)
408
0
                        return r;
409
410
0
                if (*z == p)
411
0
                        return 1;
412
413
0
                if (a + 1 >= b)
414
0
                        return 0;
415
416
0
                if (p < *z)
417
0
                        b = c;
418
0
                else
419
0
                        a = c;
420
0
        }
421
422
0
        return 0;
423
0
}
424
425
static int verify_data(
426
                JournalFile *f,
427
                Object *o, uint64_t p,
428
                MMapFileDescriptor *cache_entry_fd, uint64_t n_entries,
429
0
                MMapFileDescriptor *cache_entry_array_fd, uint64_t n_entry_arrays) {
430
431
0
        uint64_t i, n, a, last, q;
432
0
        int r;
433
434
0
        assert(f);
435
0
        assert(o);
436
0
        assert(cache_entry_fd);
437
0
        assert(cache_entry_array_fd);
438
439
0
        n = le64toh(o->data.n_entries);
440
0
        a = le64toh(o->data.entry_array_offset);
441
442
        /* Entry array means at least two objects */
443
0
        if (a && n < 2) {
444
0
                error(p, "Entry array present (entry_array_offset="OFSfmt", but n_entries=%"PRIu64")", a, n);
445
0
                return -EBADMSG;
446
0
        }
447
448
0
        if (n == 0)
449
0
                return 0;
450
451
        /* We already checked that earlier */
452
0
        assert(o->data.entry_offset);
453
454
0
        last = q = le64toh(o->data.entry_offset);
455
0
        if (!contains_uint64(cache_entry_fd, n_entries, q)) {
456
0
                error(p, "Data object references invalid entry at "OFSfmt, q);
457
0
                return -EBADMSG;
458
0
        }
459
460
0
        r = journal_file_move_to_entry_by_offset(f, q, DIRECTION_DOWN, NULL, NULL);
461
0
        if (r < 0)
462
0
                return r;
463
0
        if (r == 0) {
464
0
                error(q, "Entry object doesn't exist in the main entry array");
465
0
                return -EBADMSG;
466
0
        }
467
468
0
        i = 1;
469
0
        while (i < n) {
470
0
                uint64_t next, m, j;
471
472
0
                if (a == 0) {
473
0
                        error(p, "Array chain too short");
474
0
                        return -EBADMSG;
475
0
                }
476
477
0
                if (!contains_uint64(cache_entry_array_fd, n_entry_arrays, a)) {
478
0
                        error(p, "Invalid array offset "OFSfmt, a);
479
0
                        return -EBADMSG;
480
0
                }
481
482
0
                r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
483
0
                if (r < 0)
484
0
                        return r;
485
486
0
                next = le64toh(o->entry_array.next_entry_array_offset);
487
0
                if (next != 0 && next <= a) {
488
0
                        error(p, "Array chain has cycle (jumps back from "OFSfmt" to "OFSfmt")", a, next);
489
0
                        return -EBADMSG;
490
0
                }
491
492
0
                m = journal_file_entry_array_n_items(f, o);
493
0
                for (j = 0; i < n && j < m; i++, j++) {
494
495
0
                        q = journal_file_entry_array_item(f, o, j);
496
0
                        if (q <= last) {
497
0
                                error(p, "Data object's entry array not sorted (%"PRIu64" <= %"PRIu64")", q, last);
498
0
                                return -EBADMSG;
499
0
                        }
500
0
                        last = q;
501
502
0
                        if (!contains_uint64(cache_entry_fd, n_entries, q)) {
503
0
                                error(p, "Data object references invalid entry at "OFSfmt, q);
504
0
                                return -EBADMSG;
505
0
                        }
506
507
0
                        r = journal_file_move_to_entry_by_offset(f, q, DIRECTION_DOWN, NULL, NULL);
508
0
                        if (r < 0)
509
0
                                return r;
510
0
                        if (r == 0) {
511
0
                                error(q, "Entry object doesn't exist in the main entry array");
512
0
                                return -EBADMSG;
513
0
                        }
514
515
                        /* Pointer might have moved, reposition */
516
0
                        r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
517
0
                        if (r < 0)
518
0
                                return r;
519
0
                }
520
521
0
                a = next;
522
0
        }
523
524
0
        return 0;
525
0
}
526
527
static int verify_data_hash_table(
528
                JournalFile *f,
529
                MMapFileDescriptor *cache_data_fd, uint64_t n_data,
530
                MMapFileDescriptor *cache_entry_fd, uint64_t n_entries,
531
                MMapFileDescriptor *cache_entry_array_fd, uint64_t n_entry_arrays,
532
                usec_t *last_usec,
533
0
                bool show_progress) {
534
535
0
        uint64_t i, n;
536
0
        int r;
537
538
0
        assert(f);
539
0
        assert(cache_data_fd);
540
0
        assert(cache_entry_fd);
541
0
        assert(cache_entry_array_fd);
542
0
        assert(last_usec);
543
544
0
        n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
545
0
        if (n <= 0)
546
0
                return 0;
547
548
0
        r = journal_file_map_data_hash_table(f);
549
0
        if (r < 0)
550
0
                return log_error_errno(r, "Failed to map data hash table: %m");
551
552
0
        for (i = 0; i < n; i++) {
553
0
                uint64_t last = 0, p;
554
555
0
                if (show_progress)
556
0
                        draw_progress(0xC000 + scale_progress(0x3FFF, i, n), last_usec);
557
558
0
                p = le64toh(f->data_hash_table[i].head_hash_offset);
559
0
                while (p != 0) {
560
0
                        Object *o;
561
0
                        uint64_t next;
562
563
0
                        if (!contains_uint64(cache_data_fd, n_data, p)) {
564
0
                                error(p, "Invalid data object at hash entry %"PRIu64" of %"PRIu64, i, n);
565
0
                                return -EBADMSG;
566
0
                        }
567
568
0
                        r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
569
0
                        if (r < 0)
570
0
                                return r;
571
572
0
                        next = le64toh(o->data.next_hash_offset);
573
0
                        if (next != 0 && next <= p) {
574
0
                                error(p, "Hash chain has a cycle in hash entry %"PRIu64" of %"PRIu64, i, n);
575
0
                                return -EBADMSG;
576
0
                        }
577
578
0
                        if (le64toh(o->data.hash) % n != i) {
579
0
                                error(p, "Hash value mismatch in hash entry %"PRIu64" of %"PRIu64, i, n);
580
0
                                return -EBADMSG;
581
0
                        }
582
583
0
                        r = verify_data(f, o, p, cache_entry_fd, n_entries, cache_entry_array_fd, n_entry_arrays);
584
0
                        if (r < 0)
585
0
                                return r;
586
587
0
                        last = p;
588
0
                        p = next;
589
0
                }
590
591
0
                if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
592
0
                        error(p,
593
0
                              "Tail hash pointer mismatch in hash table (%"PRIu64" != %"PRIu64")",
594
0
                              last,
595
0
                              le64toh(f->data_hash_table[i].tail_hash_offset));
596
0
                        return -EBADMSG;
597
0
                }
598
0
        }
599
600
0
        return 0;
601
0
}
602
603
0
static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
604
0
        uint64_t n, h, q;
605
0
        int r;
606
0
        assert(f);
607
608
0
        n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
609
0
        if (n <= 0)
610
0
                return 0;
611
612
0
        r = journal_file_map_data_hash_table(f);
613
0
        if (r < 0)
614
0
                return log_error_errno(r, "Failed to map data hash table: %m");
615
616
0
        h = hash % n;
617
618
0
        q = le64toh(f->data_hash_table[h].head_hash_offset);
619
0
        while (q != 0) {
620
0
                Object *o;
621
622
0
                if (p == q)
623
0
                        return 1;
624
625
0
                r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
626
0
                if (r < 0)
627
0
                        return r;
628
629
0
                q = le64toh(o->data.next_hash_offset);
630
0
        }
631
632
0
        return 0;
633
0
}
634
635
static int verify_entry(
636
                JournalFile *f,
637
                Object *o, uint64_t p,
638
                MMapFileDescriptor *cache_data_fd, uint64_t n_data,
639
0
                bool last) {
640
641
0
        uint64_t i, n;
642
0
        int r;
643
644
0
        assert(f);
645
0
        assert(o);
646
0
        assert(cache_data_fd);
647
648
0
        n = journal_file_entry_n_items(f, o);
649
0
        for (i = 0; i < n; i++) {
650
0
                uint64_t q;
651
0
                Object *u;
652
653
0
                q = journal_file_entry_item_object_offset(f, o, i);
654
655
0
                if (!contains_uint64(cache_data_fd, n_data, q)) {
656
0
                        error(p, "Invalid data object of entry");
657
0
                        return -EBADMSG;
658
0
                }
659
660
0
                r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
661
0
                if (r < 0)
662
0
                        return r;
663
664
0
                r = data_object_in_hash_table(f, le64toh(u->data.hash), q);
665
0
                if (r < 0)
666
0
                        return r;
667
0
                if (r == 0) {
668
0
                        error(p, "Data object missing from hash table");
669
0
                        return -EBADMSG;
670
0
                }
671
672
                /* Pointer might have moved, reposition */
673
0
                r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
674
0
                if (r < 0)
675
0
                        return r;
676
677
0
                r = journal_file_move_to_entry_by_offset_for_data(f, u, p, DIRECTION_DOWN, NULL, NULL);
678
0
                if (r < 0)
679
0
                        return r;
680
681
                /* The last entry object has a very high chance of not being referenced as journal files
682
                 * almost always run out of space during linking of entry items when trying to add a new
683
                 * entry array so let's not error in that scenario. */
684
0
                if (r == 0 && !last) {
685
0
                        error(p, "Entry object not referenced by linked data object at "OFSfmt, q);
686
0
                        return -EBADMSG;
687
0
                }
688
0
        }
689
690
0
        return 0;
691
0
}
692
693
static int verify_entry_array(
694
                JournalFile *f,
695
                MMapFileDescriptor *cache_data_fd, uint64_t n_data,
696
                MMapFileDescriptor *cache_entry_fd, uint64_t n_entries,
697
                MMapFileDescriptor *cache_entry_array_fd, uint64_t n_entry_arrays,
698
                usec_t *last_usec,
699
0
                bool show_progress) {
700
701
0
        uint64_t i = 0, a, n, last = 0;
702
0
        int r;
703
704
0
        assert(f);
705
0
        assert(cache_data_fd);
706
0
        assert(cache_entry_fd);
707
0
        assert(cache_entry_array_fd);
708
0
        assert(last_usec);
709
710
0
        n = le64toh(f->header->n_entries);
711
0
        a = le64toh(f->header->entry_array_offset);
712
0
        while (i < n) {
713
0
                uint64_t next, m, j;
714
0
                Object *o;
715
716
0
                if (show_progress)
717
0
                        draw_progress(0x8000 + scale_progress(0x3FFF, i, n), last_usec);
718
719
0
                if (a == 0) {
720
0
                        error(a, "Array chain too short at %"PRIu64" of %"PRIu64, i, n);
721
0
                        return -EBADMSG;
722
0
                }
723
724
0
                if (!contains_uint64(cache_entry_array_fd, n_entry_arrays, a)) {
725
0
                        error(a, "Invalid array %"PRIu64" of %"PRIu64, i, n);
726
0
                        return -EBADMSG;
727
0
                }
728
729
0
                r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
730
0
                if (r < 0)
731
0
                        return r;
732
733
0
                next = le64toh(o->entry_array.next_entry_array_offset);
734
0
                if (next != 0 && next <= a) {
735
0
                        error(a, "Array chain has cycle at %"PRIu64" of %"PRIu64" (jumps back from to "OFSfmt")", i, n, next);
736
0
                        return -EBADMSG;
737
0
                }
738
739
0
                m = journal_file_entry_array_n_items(f, o);
740
0
                for (j = 0; i < n && j < m; i++, j++) {
741
0
                        uint64_t p;
742
743
0
                        p = journal_file_entry_array_item(f, o, j);
744
0
                        if (p <= last) {
745
0
                                error(a, "Entry array not sorted at %"PRIu64" of %"PRIu64, i, n);
746
0
                                return -EBADMSG;
747
0
                        }
748
0
                        last = p;
749
750
0
                        if (!contains_uint64(cache_entry_fd, n_entries, p)) {
751
0
                                error(a, "Invalid array entry at %"PRIu64" of %"PRIu64, i, n);
752
0
                                return -EBADMSG;
753
0
                        }
754
755
0
                        r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
756
0
                        if (r < 0)
757
0
                                return r;
758
759
0
                        r = verify_entry(f, o, p, cache_data_fd, n_data, /*last=*/ i + 1 == n);
760
0
                        if (r < 0)
761
0
                                return r;
762
763
                        /* Pointer might have moved, reposition */
764
0
                        r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
765
0
                        if (r < 0)
766
0
                                return r;
767
0
                }
768
769
0
                a = next;
770
0
        }
771
772
0
        return 0;
773
0
}
774
775
static int verify_hash_table(
776
0
                Object *o, uint64_t p, uint64_t *n_hash_tables, uint64_t header_offset, uint64_t header_size) {
777
778
0
        assert(o);
779
0
        assert(n_hash_tables);
780
781
0
        if (*n_hash_tables > 1) {
782
0
                error(p,
783
0
                      "More than one %s: %" PRIu64,
784
0
                      journal_object_type_to_string(o->object.type),
785
0
                      *n_hash_tables);
786
0
                return -EBADMSG;
787
0
        }
788
789
0
        if (header_offset != p + offsetof(Object, hash_table.items)) {
790
0
                error(p,
791
0
                      "Header offset for %s invalid (%" PRIu64 " != %" PRIu64 ")",
792
0
                      journal_object_type_to_string(o->object.type),
793
0
                      header_offset,
794
0
                      p + offsetof(Object, hash_table.items));
795
0
                return -EBADMSG;
796
0
        }
797
798
0
        if (header_size != le64toh(o->object.size) - offsetof(Object, hash_table.items)) {
799
0
                error(p,
800
0
                      "Header size for %s invalid (%" PRIu64 " != %" PRIu64 ")",
801
0
                      journal_object_type_to_string(o->object.type),
802
0
                      header_size,
803
0
                      le64toh(o->object.size) - offsetof(Object, hash_table.items));
804
0
                return -EBADMSG;
805
0
        }
806
807
0
        (*n_hash_tables)++;
808
809
0
        return 0;
810
0
}
811
812
int journal_file_verify(
813
                JournalFile *f,
814
                const char *key,
815
                usec_t *first_contained, usec_t *last_validated, usec_t *last_contained,
816
0
                bool show_progress) {
817
0
        int r;
818
0
        Object *o;
819
0
        uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0;
820
821
0
        uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
822
0
        usec_t min_entry_realtime = USEC_INFINITY, max_entry_realtime = 0;
823
0
        sd_id128_t entry_boot_id = {};  /* Unnecessary initialization to appease gcc */
824
0
        bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
825
0
        uint64_t n_objects = 0, n_entries = 0, n_data = 0, n_fields = 0, n_data_hash_tables = 0, n_field_hash_tables = 0, n_entry_arrays = 0, n_tags = 0;
826
0
        usec_t last_usec = 0;
827
0
        _cleanup_close_ int data_fd = -EBADF, entry_fd = -EBADF, entry_array_fd = -EBADF;
828
0
        _cleanup_fclose_ FILE *data_fp = NULL, *entry_fp = NULL, *entry_array_fp = NULL;
829
0
        MMapFileDescriptor *cache_data_fd = NULL, *cache_entry_fd = NULL, *cache_entry_array_fd = NULL;
830
0
        unsigned i;
831
0
        bool found_last = false;
832
0
        const char *tmp_dir = NULL;
833
0
        MMapCache *m;
834
835
#if HAVE_GCRYPT
836
        uint64_t last_tag = 0;
837
#endif
838
0
        assert(f);
839
840
0
        if (key) {
841
#if HAVE_GCRYPT
842
                r = journal_file_parse_verification_key(f, key);
843
                if (r < 0) {
844
                        log_error("Failed to parse seed.");
845
                        return r;
846
                }
847
#else
848
0
                return -EOPNOTSUPP;
849
0
#endif
850
0
        } else if (JOURNAL_HEADER_SEALED(f->header))
851
0
                return -ENOKEY;
852
853
0
        r = var_tmp_dir(&tmp_dir);
854
0
        if (r < 0) {
855
0
                log_error_errno(r, "Failed to determine temporary directory: %m");
856
0
                goto fail;
857
0
        }
858
859
0
        data_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC);
860
0
        if (data_fd < 0) {
861
0
                r = log_error_errno(data_fd, "Failed to create data file: %m");
862
0
                goto fail;
863
0
        }
864
865
0
        entry_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC);
866
0
        if (entry_fd < 0) {
867
0
                r = log_error_errno(entry_fd, "Failed to create entry file: %m");
868
0
                goto fail;
869
0
        }
870
871
0
        entry_array_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC);
872
0
        if (entry_array_fd < 0) {
873
0
                r = log_error_errno(entry_array_fd,
874
0
                                    "Failed to create entry array file: %m");
875
0
                goto fail;
876
0
        }
877
878
0
        m = mmap_cache_fd_cache(f->cache_fd);
879
0
        r = mmap_cache_add_fd(m, data_fd, PROT_READ|PROT_WRITE, &cache_data_fd);
880
0
        if (r < 0) {
881
0
                log_error_errno(r, "Failed to cache data file: %m");
882
0
                goto fail;
883
0
        }
884
885
0
        r = mmap_cache_add_fd(m, entry_fd, PROT_READ|PROT_WRITE, &cache_entry_fd);
886
0
        if (r < 0) {
887
0
                log_error_errno(r, "Failed to cache entry file: %m");
888
0
                goto fail;
889
0
        }
890
891
0
        r = mmap_cache_add_fd(m, entry_array_fd, PROT_READ|PROT_WRITE, &cache_entry_array_fd);
892
0
        if (r < 0) {
893
0
                log_error_errno(r, "Failed to cache entry array file: %m");
894
0
                goto fail;
895
0
        }
896
897
0
        r = take_fdopen_unlocked(&data_fd, "w+", &data_fp);
898
0
        if (r < 0) {
899
0
                log_error_errno(r, "Failed to open data file stream: %m");
900
0
                goto fail;
901
0
        }
902
903
0
        r = take_fdopen_unlocked(&entry_fd, "w+", &entry_fp);
904
0
        if (r < 0) {
905
0
                log_error_errno(r, "Failed to open entry file stream: %m");
906
0
                goto fail;
907
0
        }
908
909
0
        r = take_fdopen_unlocked(&entry_array_fd, "w+", &entry_array_fp);
910
0
        if (r < 0) {
911
0
                log_error_errno(r, "Failed to open entry array file stream: %m");
912
0
                goto fail;
913
0
        }
914
915
0
        if (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SUPPORTED) {
916
0
                log_error("Cannot verify file with unknown extensions.");
917
0
                r = -EOPNOTSUPP;
918
0
                goto fail;
919
0
        }
920
921
0
        for (i = 0; i < sizeof(f->header->reserved); i++)
922
0
                if (f->header->reserved[i] != 0) {
923
0
                        error(offsetof(Header, reserved[i]), "Reserved field is non-zero");
924
0
                        r = -EBADMSG;
925
0
                        goto fail;
926
0
                }
927
928
0
        if (JOURNAL_HEADER_SEALED(f->header) && !JOURNAL_HEADER_SEALED_CONTINUOUS(f->header))
929
0
                warning(p,
930
0
                        "This log file was sealed with an old journald version where the sequence of seals might not be continuous. We cannot guarantee completeness.");
931
932
        /* First iteration: we go through all objects, verify the
933
         * superficial structure, headers, hashes. */
934
935
0
        p = le64toh(f->header->header_size);
936
0
        for (;;) {
937
                /* Early exit if there are no objects in the file, at all */
938
0
                if (le64toh(f->header->tail_object_offset) == 0)
939
0
                        break;
940
941
0
                if (show_progress)
942
0
                        draw_progress(scale_progress(0x7FFF, p, le64toh(f->header->tail_object_offset)), &last_usec);
943
944
0
                r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
945
0
                if (r < 0) {
946
0
                        error_errno(p, r, "Invalid object: %m");
947
0
                        goto fail;
948
0
                }
949
950
0
                if (p > le64toh(f->header->tail_object_offset)) {
951
0
                        error(offsetof(Header, tail_object_offset),
952
0
                              "Invalid tail object pointer (%"PRIu64" > %"PRIu64")",
953
0
                              p,
954
0
                              le64toh(f->header->tail_object_offset));
955
0
                        r = -EBADMSG;
956
0
                        goto fail;
957
0
                }
958
959
0
                n_objects++;
960
961
0
                r = journal_file_object_verify(f, p, o);
962
0
                if (r < 0) {
963
0
                        error_errno(p, r, "Invalid object contents: %m");
964
0
                        goto fail;
965
0
                }
966
967
0
                if (!!(o->object.flags & OBJECT_COMPRESSED_XZ) +
968
0
                    !!(o->object.flags & OBJECT_COMPRESSED_LZ4) +
969
0
                    !!(o->object.flags & OBJECT_COMPRESSED_ZSTD) > 1) {
970
0
                        error(p, "Object has multiple compression flags set (flags: 0x%x)", o->object.flags);
971
0
                        r = -EINVAL;
972
0
                        goto fail;
973
0
                }
974
975
0
                if ((o->object.flags & OBJECT_COMPRESSED_XZ) && !JOURNAL_HEADER_COMPRESSED_XZ(f->header)) {
976
0
                        error(p, "XZ compressed object in file without XZ compression");
977
0
                        r = -EBADMSG;
978
0
                        goto fail;
979
0
                }
980
981
0
                if ((o->object.flags & OBJECT_COMPRESSED_LZ4) && !JOURNAL_HEADER_COMPRESSED_LZ4(f->header)) {
982
0
                        error(p, "LZ4 compressed object in file without LZ4 compression");
983
0
                        r = -EBADMSG;
984
0
                        goto fail;
985
0
                }
986
987
0
                if ((o->object.flags & OBJECT_COMPRESSED_ZSTD) && !JOURNAL_HEADER_COMPRESSED_ZSTD(f->header)) {
988
0
                        error(p, "ZSTD compressed object in file without ZSTD compression");
989
0
                        r = -EBADMSG;
990
0
                        goto fail;
991
0
                }
992
993
0
                switch (o->object.type) {
994
995
0
                case OBJECT_DATA:
996
0
                        r = write_uint64(data_fp, p);
997
0
                        if (r < 0)
998
0
                                goto fail;
999
1000
0
                        n_data++;
1001
0
                        break;
1002
1003
0
                case OBJECT_FIELD:
1004
0
                        n_fields++;
1005
0
                        break;
1006
1007
0
                case OBJECT_ENTRY:
1008
0
                        if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) {
1009
0
                                error(p, "First entry before first tag");
1010
0
                                r = -EBADMSG;
1011
0
                                goto fail;
1012
0
                        }
1013
1014
0
                        r = write_uint64(entry_fp, p);
1015
0
                        if (r < 0)
1016
0
                                goto fail;
1017
1018
0
                        if (le64toh(o->entry.realtime) < last_tag_realtime) {
1019
0
                                error(p,
1020
0
                                      "Older entry after newer tag (%"PRIu64" < %"PRIu64")",
1021
0
                                      le64toh(o->entry.realtime),
1022
0
                                      last_tag_realtime);
1023
0
                                r = -EBADMSG;
1024
0
                                goto fail;
1025
0
                        }
1026
1027
0
                        if (!entry_seqnum_set &&
1028
0
                            le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
1029
0
                                error(p,
1030
0
                                      "Head entry sequence number incorrect (%"PRIu64" != %"PRIu64")",
1031
0
                                      le64toh(o->entry.seqnum),
1032
0
                                      le64toh(f->header->head_entry_seqnum));
1033
0
                                r = -EBADMSG;
1034
0
                                goto fail;
1035
0
                        }
1036
1037
0
                        if (entry_seqnum_set &&
1038
0
                            entry_seqnum >= le64toh(o->entry.seqnum)) {
1039
0
                                error(p,
1040
0
                                      "Entry sequence number out of synchronization (%"PRIu64" >= %"PRIu64")",
1041
0
                                      entry_seqnum,
1042
0
                                      le64toh(o->entry.seqnum));
1043
0
                                r = -EBADMSG;
1044
0
                                goto fail;
1045
0
                        }
1046
1047
0
                        entry_seqnum = le64toh(o->entry.seqnum);
1048
0
                        entry_seqnum_set = true;
1049
1050
0
                        if (entry_monotonic_set &&
1051
0
                            sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
1052
0
                            entry_monotonic > le64toh(o->entry.monotonic)) {
1053
0
                                error(p,
1054
0
                                      "Entry timestamp out of synchronization (%"PRIu64" > %"PRIu64")",
1055
0
                                      entry_monotonic,
1056
0
                                      le64toh(o->entry.monotonic));
1057
0
                                r = -EBADMSG;
1058
0
                                goto fail;
1059
0
                        }
1060
1061
0
                        entry_monotonic = le64toh(o->entry.monotonic);
1062
0
                        entry_boot_id = o->entry.boot_id;
1063
0
                        entry_monotonic_set = true;
1064
1065
0
                        if (!entry_realtime_set &&
1066
0
                            le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
1067
0
                                error(p,
1068
0
                                      "Head entry realtime timestamp incorrect (%"PRIu64" != %"PRIu64")",
1069
0
                                      le64toh(o->entry.realtime),
1070
0
                                      le64toh(f->header->head_entry_realtime));
1071
0
                                r = -EBADMSG;
1072
0
                                goto fail;
1073
0
                        }
1074
1075
0
                        entry_realtime = le64toh(o->entry.realtime);
1076
0
                        entry_realtime_set = true;
1077
1078
0
                        max_entry_realtime = MAX(max_entry_realtime, le64toh(o->entry.realtime));
1079
0
                        min_entry_realtime = MIN(min_entry_realtime, le64toh(o->entry.realtime));
1080
1081
0
                        n_entries++;
1082
0
                        break;
1083
1084
0
                case OBJECT_DATA_HASH_TABLE:
1085
0
                        r = verify_hash_table(o, p, &n_data_hash_tables,
1086
0
                                              le64toh(f->header->data_hash_table_offset),
1087
0
                                              le64toh(f->header->data_hash_table_size));
1088
0
                        if (r < 0)
1089
0
                                goto fail;
1090
0
                        break;
1091
1092
0
                case OBJECT_FIELD_HASH_TABLE:
1093
0
                        r = verify_hash_table(o, p, &n_field_hash_tables,
1094
0
                                              le64toh(f->header->field_hash_table_offset),
1095
0
                                              le64toh(f->header->field_hash_table_size));
1096
0
                        if (r < 0)
1097
0
                                goto fail;
1098
1099
0
                        break;
1100
1101
0
                case OBJECT_ENTRY_ARRAY:
1102
0
                        r = write_uint64(entry_array_fp, p);
1103
0
                        if (r < 0)
1104
0
                                goto fail;
1105
1106
0
                        if (p == le64toh(f->header->entry_array_offset)) {
1107
0
                                if (found_main_entry_array) {
1108
0
                                        error(p, "More than one main entry array");
1109
0
                                        r = -EBADMSG;
1110
0
                                        goto fail;
1111
0
                                }
1112
1113
0
                                found_main_entry_array = true;
1114
0
                        }
1115
1116
0
                        n_entry_arrays++;
1117
0
                        break;
1118
1119
0
                case OBJECT_TAG:
1120
0
                        if (!JOURNAL_HEADER_SEALED(f->header)) {
1121
0
                                error(p, "Tag object in file without sealing");
1122
0
                                r = -EBADMSG;
1123
0
                                goto fail;
1124
0
                        }
1125
1126
0
                        if (le64toh(o->tag.seqnum) != n_tags + 1) {
1127
0
                                error(p,
1128
0
                                      "Tag sequence number out of synchronization (%"PRIu64" != %"PRIu64")",
1129
0
                                      le64toh(o->tag.seqnum),
1130
0
                                      n_tags + 1);
1131
0
                                r = -EBADMSG;
1132
0
                                goto fail;
1133
0
                        }
1134
1135
0
                        if (JOURNAL_HEADER_SEALED_CONTINUOUS(f->header)) {
1136
0
                                if (!(n_tags == 0 || (n_tags == 1 && le64toh(o->tag.epoch) == last_epoch)
1137
0
                                      || le64toh(o->tag.epoch) == last_epoch + 1)) {
1138
0
                                        error(p,
1139
0
                                              "Epoch sequence not continuous (%"PRIu64" vs %"PRIu64")",
1140
0
                                              le64toh(o->tag.epoch),
1141
0
                                              last_epoch);
1142
0
                                        r = -EBADMSG;
1143
0
                                        goto fail;
1144
0
                                }
1145
0
                        } else {
1146
0
                                if (le64toh(o->tag.epoch) < last_epoch) {
1147
0
                                        error(p,
1148
0
                                              "Epoch sequence out of synchronization (%"PRIu64" < %"PRIu64")",
1149
0
                                              le64toh(o->tag.epoch),
1150
0
                                              last_epoch);
1151
0
                                        r = -EBADMSG;
1152
0
                                        goto fail;
1153
0
                                }
1154
0
                        }
1155
1156
#if HAVE_GCRYPT
1157
                        if (JOURNAL_HEADER_SEALED(f->header)) {
1158
                                uint64_t q, rt, rt_end;
1159
1160
                                debug(p, "Checking tag %"PRIu64"...", le64toh(o->tag.seqnum));
1161
1162
                                rt = f->fss_start_usec + le64toh(o->tag.epoch) * f->fss_interval_usec;
1163
                                rt_end = usec_add(rt, f->fss_interval_usec);
1164
                                if (entry_realtime_set && entry_realtime >= rt_end) {
1165
                                        error(p,
1166
                                              "tag/entry realtime timestamp out of synchronization (%"PRIu64" >= %"PRIu64")",
1167
                                              entry_realtime,
1168
                                              rt + f->fss_interval_usec);
1169
                                        r = -EBADMSG;
1170
                                        goto fail;
1171
                                }
1172
                                if (max_entry_realtime >= rt_end) {
1173
                                        error(p,
1174
                                              "Entry realtime (%"PRIu64", %s) is too late with respect to tag (%"PRIu64", %s)",
1175
                                              max_entry_realtime, FORMAT_TIMESTAMP(max_entry_realtime),
1176
                                              rt_end, FORMAT_TIMESTAMP(rt_end));
1177
                                        r = -EBADMSG;
1178
                                        goto fail;
1179
                                }
1180
                                if (min_entry_realtime < rt) {
1181
                                        error(p,
1182
                                              "Entry realtime (%"PRIu64", %s) is too early with respect to tag (%"PRIu64", %s)",
1183
                                              min_entry_realtime, FORMAT_TIMESTAMP(min_entry_realtime),
1184
                                              rt, FORMAT_TIMESTAMP(rt));
1185
                                        r = -EBADMSG;
1186
                                        goto fail;
1187
                                }
1188
                                min_entry_realtime = USEC_INFINITY;
1189
1190
                                /* OK, now we know the epoch. So let's now set
1191
                                 * it, and calculate the HMAC for everything
1192
                                 * since the last tag. */
1193
                                r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
1194
                                if (r < 0)
1195
                                        goto fail;
1196
1197
                                r = journal_file_hmac_start(f);
1198
                                if (r < 0)
1199
                                        goto fail;
1200
1201
                                if (last_tag == 0) {
1202
                                        r = journal_file_hmac_put_header(f);
1203
                                        if (r < 0)
1204
                                                goto fail;
1205
1206
                                        q = le64toh(f->header->header_size);
1207
                                } else
1208
                                        q = last_tag;
1209
1210
                                while (q <= p) {
1211
                                        r = journal_file_move_to_object(f, OBJECT_UNUSED, q, &o);
1212
                                        if (r < 0)
1213
                                                goto fail;
1214
1215
                                        r = journal_file_hmac_put_object(f, OBJECT_UNUSED, o, q);
1216
                                        if (r < 0)
1217
                                                goto fail;
1218
1219
                                        q = q + ALIGN64(le64toh(o->object.size));
1220
                                }
1221
1222
                                /* Position might have changed, let's reposition things */
1223
                                r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
1224
                                if (r < 0)
1225
                                        goto fail;
1226
1227
                                if (memcmp(o->tag.tag, sym_gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
1228
                                        error(p, "Tag failed verification");
1229
                                        r = -EBADMSG;
1230
                                        goto fail;
1231
                                }
1232
1233
                                f->hmac_running = false;
1234
                                last_tag_realtime = rt;
1235
                        }
1236
1237
                        last_tag = p + ALIGN64(le64toh(o->object.size));
1238
#endif
1239
1240
0
                        last_epoch = le64toh(o->tag.epoch);
1241
1242
0
                        n_tags++;
1243
0
                        break;
1244
0
                }
1245
1246
0
                if (p == le64toh(f->header->tail_object_offset)) {
1247
0
                        found_last = true;
1248
0
                        break;
1249
0
                }
1250
1251
0
                p = p + ALIGN64(le64toh(o->object.size));
1252
0
        };
1253
1254
0
        if (!found_last && le64toh(f->header->tail_object_offset) != 0) {
1255
0
                error(le64toh(f->header->tail_object_offset),
1256
0
                      "Tail object pointer dead (%"PRIu64" != 0)",
1257
0
                      le64toh(f->header->tail_object_offset));
1258
0
                r = -EBADMSG;
1259
0
                goto fail;
1260
0
        }
1261
1262
0
        if (n_objects != le64toh(f->header->n_objects)) {
1263
0
                error(offsetof(Header, n_objects),
1264
0
                      "Object number mismatch (%"PRIu64" != %"PRIu64")",
1265
0
                      n_objects,
1266
0
                      le64toh(f->header->n_objects));
1267
0
                r = -EBADMSG;
1268
0
                goto fail;
1269
0
        }
1270
1271
0
        if (n_entries != le64toh(f->header->n_entries)) {
1272
0
                error(offsetof(Header, n_entries),
1273
0
                      "Entry number mismatch (%"PRIu64" != %"PRIu64")",
1274
0
                      n_entries,
1275
0
                      le64toh(f->header->n_entries));
1276
0
                r = -EBADMSG;
1277
0
                goto fail;
1278
0
        }
1279
1280
0
        if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
1281
0
            n_data != le64toh(f->header->n_data)) {
1282
0
                error(offsetof(Header, n_data),
1283
0
                      "Data number mismatch (%"PRIu64" != %"PRIu64")",
1284
0
                      n_data,
1285
0
                      le64toh(f->header->n_data));
1286
0
                r = -EBADMSG;
1287
0
                goto fail;
1288
0
        }
1289
1290
0
        if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
1291
0
            n_fields != le64toh(f->header->n_fields)) {
1292
0
                error(offsetof(Header, n_fields),
1293
0
                      "Field number mismatch (%"PRIu64" != %"PRIu64")",
1294
0
                      n_fields,
1295
0
                      le64toh(f->header->n_fields));
1296
0
                r = -EBADMSG;
1297
0
                goto fail;
1298
0
        }
1299
1300
0
        if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
1301
0
            n_tags != le64toh(f->header->n_tags)) {
1302
0
                error(offsetof(Header, n_tags),
1303
0
                      "Tag number mismatch (%"PRIu64" != %"PRIu64")",
1304
0
                      n_tags,
1305
0
                      le64toh(f->header->n_tags));
1306
0
                r = -EBADMSG;
1307
0
                goto fail;
1308
0
        }
1309
1310
0
        if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
1311
0
            n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
1312
0
                error(offsetof(Header, n_entry_arrays),
1313
0
                      "Entry array number mismatch (%"PRIu64" != %"PRIu64")",
1314
0
                      n_entry_arrays,
1315
0
                      le64toh(f->header->n_entry_arrays));
1316
0
                r = -EBADMSG;
1317
0
                goto fail;
1318
0
        }
1319
1320
0
        if (!found_main_entry_array && le64toh(f->header->entry_array_offset) != 0) {
1321
0
                error(0, "Missing main entry array");
1322
0
                r = -EBADMSG;
1323
0
                goto fail;
1324
0
        }
1325
1326
0
        if (entry_seqnum_set &&
1327
0
            entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
1328
0
                error(offsetof(Header, tail_entry_seqnum),
1329
0
                      "Tail entry sequence number incorrect (%"PRIu64" != %"PRIu64")",
1330
0
                      entry_seqnum,
1331
0
                      le64toh(f->header->tail_entry_seqnum));
1332
0
                r = -EBADMSG;
1333
0
                goto fail;
1334
0
        }
1335
1336
0
        if (entry_monotonic_set &&
1337
0
            (sd_id128_equal(entry_boot_id, f->header->tail_entry_boot_id) &&
1338
0
             JOURNAL_HEADER_TAIL_ENTRY_BOOT_ID(f->header) &&
1339
0
             entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
1340
0
                error(0,
1341
0
                      "Invalid tail monotonic timestamp (%"PRIu64" != %"PRIu64")",
1342
0
                      entry_monotonic,
1343
0
                      le64toh(f->header->tail_entry_monotonic));
1344
0
                r = -EBADMSG;
1345
0
                goto fail;
1346
0
        }
1347
1348
0
        if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1349
0
                error(0,
1350
0
                      "Invalid tail realtime timestamp (%"PRIu64" != %"PRIu64")",
1351
0
                      entry_realtime,
1352
0
                      le64toh(f->header->tail_entry_realtime));
1353
0
                r = -EBADMSG;
1354
0
                goto fail;
1355
0
        }
1356
1357
0
        if (fflush(data_fp) != 0) {
1358
0
                r = log_error_errno(errno, "Failed to flush data file stream: %m");
1359
0
                goto fail;
1360
0
        }
1361
1362
0
        if (fflush(entry_fp) != 0) {
1363
0
                r = log_error_errno(errno, "Failed to flush entry file stream: %m");
1364
0
                goto fail;
1365
0
        }
1366
1367
0
        if (fflush(entry_array_fp) != 0) {
1368
0
                r = log_error_errno(errno, "Failed to flush entry array file stream: %m");
1369
0
                goto fail;
1370
0
        }
1371
1372
        /* Second iteration: we follow all objects referenced from the
1373
         * two entry points: the object hash table and the entry
1374
         * array. We also check that everything referenced (directly
1375
         * or indirectly) in the data hash table also exists in the
1376
         * entry array, and vice versa. Note that we do not care for
1377
         * unreferenced objects. We only care that everything that is
1378
         * referenced is consistent. */
1379
1380
0
        r = verify_entry_array(f,
1381
0
                               cache_data_fd, n_data,
1382
0
                               cache_entry_fd, n_entries,
1383
0
                               cache_entry_array_fd, n_entry_arrays,
1384
0
                               &last_usec,
1385
0
                               show_progress);
1386
0
        if (r < 0)
1387
0
                goto fail;
1388
1389
0
        r = verify_data_hash_table(f,
1390
0
                                   cache_data_fd, n_data,
1391
0
                                   cache_entry_fd, n_entries,
1392
0
                                   cache_entry_array_fd, n_entry_arrays,
1393
0
                                   &last_usec,
1394
0
                                   show_progress);
1395
0
        if (r < 0)
1396
0
                goto fail;
1397
1398
0
        if (show_progress)
1399
0
                flush_progress();
1400
1401
0
        mmap_cache_fd_free(cache_data_fd);
1402
0
        mmap_cache_fd_free(cache_entry_fd);
1403
0
        mmap_cache_fd_free(cache_entry_array_fd);
1404
1405
0
        if (first_contained)
1406
0
                *first_contained = le64toh(f->header->head_entry_realtime);
1407
#if HAVE_GCRYPT
1408
        if (last_validated)
1409
                *last_validated = last_tag_realtime + f->fss_interval_usec;
1410
#endif
1411
0
        if (last_contained)
1412
0
                *last_contained = le64toh(f->header->tail_entry_realtime);
1413
1414
0
        return 0;
1415
1416
0
fail:
1417
0
        if (show_progress)
1418
0
                flush_progress();
1419
1420
0
        log_error("File corruption detected at %s:%"PRIu64" (of %"PRIu64" bytes, %"PRIu64"%%).",
1421
0
                  f->path,
1422
0
                  p,
1423
0
                  (uint64_t) f->last_stat.st_size,
1424
0
                  100U * p / (uint64_t) f->last_stat.st_size);
1425
1426
0
        if (cache_data_fd)
1427
0
                mmap_cache_fd_free(cache_data_fd);
1428
1429
0
        if (cache_entry_fd)
1430
0
                mmap_cache_fd_free(cache_entry_fd);
1431
1432
0
        if (cache_entry_array_fd)
1433
0
                mmap_cache_fd_free(cache_entry_array_fd);
1434
1435
0
        return r;
1436
0
}