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