]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journal-verify.c
journalctl: properly detect empty journal files
[thirdparty/systemd.git] / src / journal / journal-verify.c
CommitLineData
0284adc6
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <unistd.h>
23#include <sys/mman.h>
24#include <fcntl.h>
feb12d3e 25#include <stddef.h>
0284adc6
LP
26
27#include "util.h"
28#include "macro.h"
29#include "journal-def.h"
30#include "journal-file.h"
31#include "journal-authenticate.h"
32#include "journal-verify.h"
f59a5f6b 33#include "lookup3.h"
fd5dc320 34#include "compress.h"
288a74cc 35#include "terminal-util.h"
f59a5f6b 36
54f3ff07
ZJS
37static void draw_progress(uint64_t p, usec_t *last_usec) {
38 unsigned n, i, j, k;
39 usec_t z, x;
40
41 if (!on_tty())
42 return;
43
44 z = now(CLOCK_MONOTONIC);
45 x = *last_usec;
46
47 if (x != 0 && x + 40 * USEC_PER_MSEC > z)
48 return;
49
50 *last_usec = z;
51
52 n = (3 * columns()) / 4;
53 j = (n * (unsigned) p) / 65535ULL;
54 k = n - j;
55
56 fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
57
58 for (i = 0; i < j; i++)
59 fputs("\xe2\x96\x88", stdout);
60
61 fputs(ANSI_HIGHLIGHT_OFF, stdout);
62
63 for (i = 0; i < k; i++)
64 fputs("\xe2\x96\x91", stdout);
65
66 printf(" %3"PRIu64"%%", 100U * p / 65535U);
67
68 fputs("\r\x1B[?25h", stdout);
69 fflush(stdout);
70}
71
45c047b2
LP
72static uint64_t scale_progress(uint64_t scale, uint64_t p, uint64_t m) {
73
74 /* Calculates scale * p / m, but handles m == 0 safely, and saturates */
75
76 if (p >= m || m == 0)
77 return scale;
78
79 return scale * p / m;
80}
81
54f3ff07
ZJS
82static void flush_progress(void) {
83 unsigned n, i;
84
85 if (!on_tty())
86 return;
87
88 n = (3 * columns()) / 4;
89
90 putchar('\r');
91
92 for (i = 0; i < n + 5; i++)
93 putchar(' ');
94
95 putchar('\r');
96 fflush(stdout);
97}
98
99#define debug(_offset, _fmt, ...) do{ \
100 flush_progress(); \
101 log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
102 } while(0)
103
104#define warning(_offset, _fmt, ...) do{ \
105 flush_progress(); \
106 log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
107 } while(0)
108
109#define error(_offset, _fmt, ...) do{ \
110 flush_progress(); \
111 log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
112 } while(0)
113
92fba83e 114static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) {
db11ac1a
LP
115 uint64_t i;
116
0284adc6 117 assert(f);
92fba83e 118 assert(offset);
0284adc6
LP
119 assert(o);
120
121 /* This does various superficial tests about the length an
122 * possible field values. It does not follow any references to
123 * other objects. */
124
d89c8fdf 125 if ((o->object.flags & OBJECT_COMPRESSED_XZ) &&
bca9e39d
LP
126 o->object.type != OBJECT_DATA) {
127 error(offset, "Found compressed object that isn't of type DATA, which is not allowed.");
f59a5f6b 128 return -EBADMSG;
bca9e39d 129 }
f59a5f6b 130
0284adc6 131 switch (o->object.type) {
f59a5f6b 132
fd5dc320
LP
133 case OBJECT_DATA: {
134 uint64_t h1, h2;
1ec7120e 135 int compression, r;
fd5dc320 136
92fba83e 137 if (le64toh(o->data.entry_offset) == 0)
54f3ff07 138 warning(offset, "unused data (entry_offset==0)");
92fba83e
ZJS
139
140 if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) {
54f3ff07 141 error(offset, "bad n_entries: %"PRIu64, o->data.n_entries);
0284adc6 142 return -EBADMSG;
92fba83e 143 }
0284adc6 144
92fba83e 145 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) {
54f3ff07
ZJS
146 error(offset, "bad object size (<= %zu): %"PRIu64,
147 offsetof(DataObject, payload),
148 le64toh(o->object.size));
0284adc6 149 return -EBADMSG;
92fba83e 150 }
f59a5f6b 151
fd5dc320 152 h1 = le64toh(o->data.hash);
f59a5f6b 153
1ec7120e
ZJS
154 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
155 if (compression) {
d89c8fdf 156 _cleanup_free_ void *b = NULL;
fa1c4b51 157 size_t alloc = 0, b_size;
f59a5f6b 158
1ec7120e
ZJS
159 r = decompress_blob(compression,
160 o->data.payload,
161 le64toh(o->object.size) - offsetof(Object, data.payload),
162 &b, &alloc, &b_size, 0);
163 if (r < 0) {
54f3ff07
ZJS
164 error(offset, "%s decompression failed: %s",
165 object_compressed_to_string(compression), strerror(-r));
1ec7120e 166 return r;
92fba83e 167 }
fd5dc320
LP
168
169 h2 = hash64(b, b_size);
fd5dc320
LP
170 } else
171 h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
172
92fba83e 173 if (h1 != h2) {
54f3ff07 174 error(offset, "invalid hash (%08"PRIx64" vs. %08"PRIx64, h1, h2);
fd5dc320 175 return -EBADMSG;
92fba83e 176 }
f59a5f6b 177
db11ac1a
LP
178 if (!VALID64(o->data.next_hash_offset) ||
179 !VALID64(o->data.next_field_offset) ||
180 !VALID64(o->data.entry_offset) ||
92fba83e 181 !VALID64(o->data.entry_array_offset)) {
54f3ff07
ZJS
182 error(offset, "invalid offset (next_hash_offset="OFSfmt", next_field_offset="OFSfmt", entry_offset="OFSfmt", entry_array_offset="OFSfmt,
183 o->data.next_hash_offset,
184 o->data.next_field_offset,
185 o->data.entry_offset,
186 o->data.entry_array_offset);
db11ac1a 187 return -EBADMSG;
92fba83e 188 }
db11ac1a 189
0284adc6 190 break;
fd5dc320 191 }
0284adc6
LP
192
193 case OBJECT_FIELD:
92fba83e 194 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) {
54f3ff07
ZJS
195 error(offset,
196 "bad field size (<= %zu): %"PRIu64,
197 offsetof(FieldObject, payload),
198 le64toh(o->object.size));
0284adc6 199 return -EBADMSG;
92fba83e 200 }
db11ac1a
LP
201
202 if (!VALID64(o->field.next_hash_offset) ||
92fba83e 203 !VALID64(o->field.head_data_offset)) {
54f3ff07
ZJS
204 error(offset,
205 "invalid offset (next_hash_offset="OFSfmt", head_data_offset="OFSfmt,
206 o->field.next_hash_offset,
207 o->field.head_data_offset);
db11ac1a 208 return -EBADMSG;
92fba83e 209 }
0284adc6
LP
210 break;
211
212 case OBJECT_ENTRY:
92fba83e 213 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) {
54f3ff07
ZJS
214 error(offset,
215 "bad entry size (<= %zu): %"PRIu64,
216 offsetof(EntryObject, items),
217 le64toh(o->object.size));
0284adc6 218 return -EBADMSG;
92fba83e 219 }
0284adc6 220
92fba83e 221 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) {
54f3ff07
ZJS
222 error(offset,
223 "invalid number items in entry: %"PRIu64,
224 (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem));
0284adc6 225 return -EBADMSG;
92fba83e
ZJS
226 }
227
228 if (le64toh(o->entry.seqnum) <= 0) {
54f3ff07
ZJS
229 error(offset,
230 "invalid entry seqnum: %"PRIx64,
231 le64toh(o->entry.seqnum));
92fba83e
ZJS
232 return -EBADMSG;
233 }
0284adc6 234
92fba83e 235 if (!VALID_REALTIME(le64toh(o->entry.realtime))) {
54f3ff07
ZJS
236 error(offset,
237 "invalid entry realtime timestamp: %"PRIu64,
238 le64toh(o->entry.realtime));
0284adc6 239 return -EBADMSG;
92fba83e
ZJS
240 }
241
242 if (!VALID_MONOTONIC(le64toh(o->entry.monotonic))) {
54f3ff07
ZJS
243 error(offset,
244 "invalid entry monotonic timestamp: %"PRIu64,
245 le64toh(o->entry.monotonic));
92fba83e
ZJS
246 return -EBADMSG;
247 }
0284adc6 248
db11ac1a
LP
249 for (i = 0; i < journal_file_entry_n_items(o); i++) {
250 if (o->entry.items[i].object_offset == 0 ||
92fba83e 251 !VALID64(o->entry.items[i].object_offset)) {
54f3ff07
ZJS
252 error(offset,
253 "invalid entry item (%"PRIu64"/%"PRIu64" offset: "OFSfmt,
254 i, journal_file_entry_n_items(o),
255 o->entry.items[i].object_offset);
db11ac1a 256 return -EBADMSG;
92fba83e 257 }
db11ac1a
LP
258 }
259
0284adc6
LP
260 break;
261
262 case OBJECT_DATA_HASH_TABLE:
263 case OBJECT_FIELD_HASH_TABLE:
92fba83e
ZJS
264 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
265 (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) {
54f3ff07
ZJS
266 error(offset,
267 "invalid %s hash table size: %"PRIu64,
268 o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
269 le64toh(o->object.size));
86adf873 270 return -EBADMSG;
92fba83e 271 }
86adf873 272
fb9a24b6
LP
273 for (i = 0; i < journal_file_hash_table_n_items(o); i++) {
274 if (o->hash_table.items[i].head_hash_offset != 0 &&
92fba83e 275 !VALID64(le64toh(o->hash_table.items[i].head_hash_offset))) {
54f3ff07
ZJS
276 error(offset,
277 "invalid %s hash table item (%"PRIu64"/%"PRIu64") head_hash_offset: "OFSfmt,
278 o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
279 i, journal_file_hash_table_n_items(o),
280 le64toh(o->hash_table.items[i].head_hash_offset));
fb9a24b6 281 return -EBADMSG;
92fba83e 282 }
fb9a24b6 283 if (o->hash_table.items[i].tail_hash_offset != 0 &&
92fba83e 284 !VALID64(le64toh(o->hash_table.items[i].tail_hash_offset))) {
54f3ff07
ZJS
285 error(offset,
286 "invalid %s hash table item (%"PRIu64"/%"PRIu64") tail_hash_offset: "OFSfmt,
287 o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
288 i, journal_file_hash_table_n_items(o),
289 le64toh(o->hash_table.items[i].tail_hash_offset));
fb9a24b6 290 return -EBADMSG;
92fba83e 291 }
fb9a24b6
LP
292
293 if ((o->hash_table.items[i].head_hash_offset != 0) !=
92fba83e 294 (o->hash_table.items[i].tail_hash_offset != 0)) {
54f3ff07
ZJS
295 error(offset,
296 "invalid %s hash table item (%"PRIu64"/%"PRIu64"): head_hash_offset="OFSfmt" tail_hash_offset="OFSfmt,
297 o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
298 i, journal_file_hash_table_n_items(o),
299 le64toh(o->hash_table.items[i].head_hash_offset),
300 le64toh(o->hash_table.items[i].tail_hash_offset));
fb9a24b6 301 return -EBADMSG;
92fba83e 302 }
fb9a24b6
LP
303 }
304
0284adc6
LP
305 break;
306
307 case OBJECT_ENTRY_ARRAY:
92fba83e
ZJS
308 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
309 (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) {
54f3ff07
ZJS
310 error(offset,
311 "invalid object entry array size: %"PRIu64,
312 le64toh(o->object.size));
86adf873 313 return -EBADMSG;
92fba83e 314 }
86adf873 315
92fba83e 316 if (!VALID64(o->entry_array.next_entry_array_offset)) {
54f3ff07
ZJS
317 error(offset,
318 "invalid object entry array next_entry_array_offset: "OFSfmt,
319 o->entry_array.next_entry_array_offset);
db11ac1a 320 return -EBADMSG;
92fba83e 321 }
db11ac1a 322
fb9a24b6 323 for (i = 0; i < journal_file_entry_array_n_items(o); i++)
af13a6b0
GM
324 if (le64toh(o->entry_array.items[i]) != 0 &&
325 !VALID64(le64toh(o->entry_array.items[i]))) {
54f3ff07
ZJS
326 error(offset,
327 "invalid object entry array item (%"PRIu64"/%"PRIu64"): "OFSfmt,
328 i, journal_file_entry_array_n_items(o),
329 le64toh(o->entry_array.items[i]));
fb9a24b6 330 return -EBADMSG;
92fba83e 331 }
fb9a24b6 332
0284adc6
LP
333 break;
334
335 case OBJECT_TAG:
92fba83e 336 if (le64toh(o->object.size) != sizeof(TagObject)) {
54f3ff07
ZJS
337 error(offset,
338 "invalid object tag size: %"PRIu64,
339 le64toh(o->object.size));
0284adc6 340 return -EBADMSG;
92fba83e 341 }
fc89a139 342
92fba83e 343 if (!VALID_EPOCH(o->tag.epoch)) {
54f3ff07
ZJS
344 error(offset,
345 "invalid object tag epoch: %"PRIu64,
346 o->tag.epoch);
fc89a139 347 return -EBADMSG;
92fba83e 348 }
fc89a139 349
0284adc6
LP
350 break;
351 }
352
353 return 0;
354}
355
0284adc6
LP
356static int write_uint64(int fd, uint64_t p) {
357 ssize_t k;
358
359 k = write(fd, &p, sizeof(p));
360 if (k < 0)
361 return -errno;
362 if (k != sizeof(p))
363 return -EIO;
364
365 return 0;
366}
367
368static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
369 uint64_t a, b;
370 int r;
371
372 assert(m);
373 assert(fd >= 0);
374
375 /* Bisection ... */
376
377 a = 0; b = n;
378 while (a < b) {
379 uint64_t c, *z;
380
381 c = (a + b) / 2;
382
1b8951e5 383 r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z);
0284adc6
LP
384 if (r < 0)
385 return r;
386
387 if (*z == p)
388 return 1;
389
a2e99cdf
LP
390 if (a + 1 >= b)
391 return 0;
392
0284adc6
LP
393 if (p < *z)
394 b = c;
fcde2389 395 else
0284adc6
LP
396 a = c;
397 }
398
399 return 0;
400}
401
86adf873
LP
402static int entry_points_to_data(
403 JournalFile *f,
404 int entry_fd,
405 uint64_t n_entries,
406 uint64_t entry_p,
407 uint64_t data_p) {
408
409 int r;
410 uint64_t i, n, a;
411 Object *o;
412 bool found = false;
413
414 assert(f);
415 assert(entry_fd >= 0);
416
417 if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
54f3ff07
ZJS
418 error(data_p,
419 "data object references invalid entry at "OFSfmt, entry_p);
86adf873
LP
420 return -EBADMSG;
421 }
422
423 r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
424 if (r < 0)
425 return r;
426
427 n = journal_file_entry_n_items(o);
428 for (i = 0; i < n; i++)
429 if (le64toh(o->entry.items[i].object_offset) == data_p) {
430 found = true;
431 break;
432 }
433
434 if (!found) {
54f3ff07
ZJS
435 error(entry_p,
436 "data object at "OFSfmt" not referenced by linked entry", data_p);
86adf873
LP
437 return -EBADMSG;
438 }
439
440 /* Check if this entry is also in main entry array. Since the
441 * main entry array has already been verified we can rely on
7517e174 442 * its consistency. */
86adf873 443
369f0589 444 i = 0;
86adf873
LP
445 n = le64toh(f->header->n_entries);
446 a = le64toh(f->header->entry_array_offset);
86adf873
LP
447
448 while (i < n) {
369f0589 449 uint64_t m, u;
86adf873
LP
450
451 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
452 if (r < 0)
453 return r;
454
455 m = journal_file_entry_array_n_items(o);
369f0589
LP
456 u = MIN(n - i, m);
457
458 if (entry_p <= le64toh(o->entry_array.items[u-1])) {
459 uint64_t x, y, z;
460
461 x = 0;
462 y = u;
463
464 while (x < y) {
465 z = (x + y) / 2;
466
467 if (le64toh(o->entry_array.items[z]) == entry_p)
468 return 0;
469
470 if (x + 1 >= y)
471 break;
472
473 if (entry_p < le64toh(o->entry_array.items[z]))
474 y = z;
475 else
476 x = z;
477 }
478
54f3ff07 479 error(entry_p, "entry object doesn't exist in main entry array");
369f0589
LP
480 return -EBADMSG;
481 }
86adf873 482
369f0589 483 i += u;
356fe3e6 484 a = le64toh(o->entry_array.next_entry_array_offset);
86adf873
LP
485 }
486
487 return 0;
488}
489
490static int verify_data(
491 JournalFile *f,
492 Object *o, uint64_t p,
493 int entry_fd, uint64_t n_entries,
494 int entry_array_fd, uint64_t n_entry_arrays) {
495
496 uint64_t i, n, a, last, q;
497 int r;
498
499 assert(f);
500 assert(o);
501 assert(entry_fd >= 0);
502 assert(entry_array_fd >= 0);
503
504 n = le64toh(o->data.n_entries);
505 a = le64toh(o->data.entry_array_offset);
506
92fba83e
ZJS
507 /* Entry array means at least two objects */
508 if (a && n < 2) {
54f3ff07
ZJS
509 error(p,
510 "entry array present (entry_array_offset="OFSfmt", but n_entries=%"PRIu64")",
511 a, n);
92fba83e
ZJS
512 return -EBADMSG;
513 }
514
515 if (n == 0)
516 return 0;
517
518 /* We already checked that earlier */
519 assert(o->data.entry_offset);
86adf873
LP
520
521 last = q = le64toh(o->data.entry_offset);
522 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
523 if (r < 0)
524 return r;
525
2a7273ef 526 i = 1;
86adf873
LP
527 while (i < n) {
528 uint64_t next, m, j;
529
530 if (a == 0) {
54f3ff07 531 error(p, "array chain too short");
86adf873
LP
532 return -EBADMSG;
533 }
534
535 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
54f3ff07 536 error(p, "invalid array offset "OFSfmt, a);
86adf873
LP
537 return -EBADMSG;
538 }
539
540 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
541 if (r < 0)
542 return r;
543
544 next = le64toh(o->entry_array.next_entry_array_offset);
545 if (next != 0 && next <= a) {
54f3ff07
ZJS
546 error(p, "array chain has cycle (jumps back from "OFSfmt" to "OFSfmt")",
547 a, next);
86adf873
LP
548 return -EBADMSG;
549 }
550
551 m = journal_file_entry_array_n_items(o);
552 for (j = 0; i < n && j < m; i++, j++) {
553
554 q = le64toh(o->entry_array.items[j]);
555 if (q <= last) {
54f3ff07 556 error(p, "data object's entry array not sorted");
86adf873
LP
557 return -EBADMSG;
558 }
559 last = q;
560
561 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
562 if (r < 0)
563 return r;
564
565 /* Pointer might have moved, reposition */
566 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
567 if (r < 0)
568 return r;
569 }
570
571 a = next;
572 }
573
574 return 0;
575}
576
577static int verify_hash_table(
578 JournalFile *f,
579 int data_fd, uint64_t n_data,
580 int entry_fd, uint64_t n_entries,
581 int entry_array_fd, uint64_t n_entry_arrays,
b72631e5
LP
582 usec_t *last_usec,
583 bool show_progress) {
86adf873
LP
584
585 uint64_t i, n;
586 int r;
587
588 assert(f);
589 assert(data_fd >= 0);
590 assert(entry_fd >= 0);
591 assert(entry_array_fd >= 0);
592 assert(last_usec);
593
594 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
dade37d4
LP
595 if (n <= 0)
596 return 0;
597
598 r = journal_file_map_data_hash_table(f);
599 if (r < 0)
600 return log_error_errno(r, "Failed to map data hash table: %m");
601
86adf873
LP
602 for (i = 0; i < n; i++) {
603 uint64_t last = 0, p;
604
b72631e5 605 if (show_progress)
45c047b2 606 draw_progress(0xC000 + scale_progress(0x3FFF, i, n), last_usec);
86adf873
LP
607
608 p = le64toh(f->data_hash_table[i].head_hash_offset);
609 while (p != 0) {
610 Object *o;
611 uint64_t next;
612
613 if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
54f3ff07
ZJS
614 error(p, "invalid data object at hash entry %"PRIu64" of %"PRIu64,
615 i, n);
86adf873
LP
616 return -EBADMSG;
617 }
618
619 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
620 if (r < 0)
621 return r;
622
623 next = le64toh(o->data.next_hash_offset);
624 if (next != 0 && next <= p) {
54f3ff07
ZJS
625 error(p, "hash chain has a cycle in hash entry %"PRIu64" of %"PRIu64,
626 i, n);
86adf873
LP
627 return -EBADMSG;
628 }
629
630 if (le64toh(o->data.hash) % n != i) {
54f3ff07
ZJS
631 error(p, "hash value mismatch in hash entry %"PRIu64" of %"PRIu64,
632 i, n);
86adf873
LP
633 return -EBADMSG;
634 }
635
636 r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
637 if (r < 0)
638 return r;
639
640 last = p;
641 p = next;
642 }
643
644 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
54f3ff07 645 error(p, "tail hash pointer mismatch in hash table");
86adf873
LP
646 return -EBADMSG;
647 }
648 }
649
650 return 0;
651}
652
653static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
654 uint64_t n, h, q;
655 int r;
656 assert(f);
657
658 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
dade37d4
LP
659 if (n <= 0)
660 return 0;
661
662 r = journal_file_map_data_hash_table(f);
663 if (r < 0)
664 return log_error_errno(r, "Failed to map data hash table: %m");
665
86adf873
LP
666 h = hash % n;
667
668 q = le64toh(f->data_hash_table[h].head_hash_offset);
669 while (q != 0) {
670 Object *o;
671
672 if (p == q)
673 return 1;
674
675 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
676 if (r < 0)
677 return r;
678
679 q = le64toh(o->data.next_hash_offset);
680 }
681
682 return 0;
683}
684
685static int verify_entry(
686 JournalFile *f,
687 Object *o, uint64_t p,
688 int data_fd, uint64_t n_data) {
689
690 uint64_t i, n;
691 int r;
692
693 assert(f);
694 assert(o);
695 assert(data_fd >= 0);
696
697 n = journal_file_entry_n_items(o);
698 for (i = 0; i < n; i++) {
699 uint64_t q, h;
700 Object *u;
701
702 q = le64toh(o->entry.items[i].object_offset);
703 h = le64toh(o->entry.items[i].hash);
704
705 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
54f3ff07 706 error(p, "invalid data object of entry");
86adf873
LP
707 return -EBADMSG;
708 }
709
710 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
711 if (r < 0)
712 return r;
713
714 if (le64toh(u->data.hash) != h) {
54f3ff07 715 error(p, "hash mismatch for data object of entry");
86adf873
LP
716 return -EBADMSG;
717 }
718
719 r = data_object_in_hash_table(f, h, q);
720 if (r < 0)
721 return r;
722 if (r == 0) {
54f3ff07 723 error(p, "data object missing from hash table");
86adf873
LP
724 return -EBADMSG;
725 }
726 }
727
728 return 0;
729}
730
731static int verify_entry_array(
732 JournalFile *f,
733 int data_fd, uint64_t n_data,
734 int entry_fd, uint64_t n_entries,
735 int entry_array_fd, uint64_t n_entry_arrays,
b72631e5
LP
736 usec_t *last_usec,
737 bool show_progress) {
86adf873
LP
738
739 uint64_t i = 0, a, n, last = 0;
740 int r;
741
742 assert(f);
743 assert(data_fd >= 0);
744 assert(entry_fd >= 0);
745 assert(entry_array_fd >= 0);
746 assert(last_usec);
747
748 n = le64toh(f->header->n_entries);
749 a = le64toh(f->header->entry_array_offset);
750 while (i < n) {
751 uint64_t next, m, j;
752 Object *o;
753
b72631e5 754 if (show_progress)
45c047b2 755 draw_progress(0x8000 + scale_progress(0x3FFF, i, n), last_usec);
86adf873
LP
756
757 if (a == 0) {
54f3ff07 758 error(a, "array chain too short at %"PRIu64" of %"PRIu64, i, n);
86adf873
LP
759 return -EBADMSG;
760 }
761
762 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
54f3ff07 763 error(a, "invalid array %"PRIu64" of %"PRIu64, i, n);
86adf873
LP
764 return -EBADMSG;
765 }
766
767 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
768 if (r < 0)
769 return r;
770
771 next = le64toh(o->entry_array.next_entry_array_offset);
772 if (next != 0 && next <= a) {
54f3ff07
ZJS
773 error(a,
774 "array chain has cycle at %"PRIu64" of %"PRIu64" (jumps back from to "OFSfmt")",
775 i, n, next);
86adf873
LP
776 return -EBADMSG;
777 }
778
779 m = journal_file_entry_array_n_items(o);
780 for (j = 0; i < n && j < m; i++, j++) {
781 uint64_t p;
782
783 p = le64toh(o->entry_array.items[j]);
784 if (p <= last) {
54f3ff07
ZJS
785 error(a, "entry array not sorted at %"PRIu64" of %"PRIu64,
786 i, n);
86adf873
LP
787 return -EBADMSG;
788 }
789 last = p;
790
791 if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
54f3ff07
ZJS
792 error(a, "invalid array entry at %"PRIu64" of %"PRIu64,
793 i, n);
86adf873
LP
794 return -EBADMSG;
795 }
796
797 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
798 if (r < 0)
799 return r;
800
801 r = verify_entry(f, o, p, data_fd, n_data);
802 if (r < 0)
803 return r;
804
805 /* Pointer might have moved, reposition */
806 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
807 if (r < 0)
808 return r;
809 }
810
811 a = next;
812 }
813
814 return 0;
815}
816
6c7be122
LP
817int journal_file_verify(
818 JournalFile *f,
819 const char *key,
2a7b539a 820 usec_t *first_contained, usec_t *last_validated, usec_t *last_contained,
b72631e5 821 bool show_progress) {
0284adc6
LP
822 int r;
823 Object *o;
0ab5c3ed
ZJS
824 uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0;
825
14d10188 826 uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
0284adc6
LP
827 sd_id128_t entry_boot_id;
828 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
14d10188 829 uint64_t n_weird = 0, 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;
0284adc6
LP
830 usec_t last_usec = 0;
831 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
97147f8c 832 unsigned i;
e8c108ca 833 bool found_last = false;
0ab5c3ed
ZJS
834#ifdef HAVE_GCRYPT
835 uint64_t last_tag = 0;
836#endif
0284adc6
LP
837 assert(f);
838
baed47c3 839 if (key) {
feb12d3e 840#ifdef HAVE_GCRYPT
baed47c3 841 r = journal_file_parse_verification_key(f, key);
b7c9ae91
LP
842 if (r < 0) {
843 log_error("Failed to parse seed.");
56e81f7c 844 return r;
b7c9ae91 845 }
feb12d3e 846#else
15411c0c 847 return -EOPNOTSUPP;
feb12d3e 848#endif
c586dbf1
LP
849 } else if (f->seal)
850 return -ENOKEY;
b7c9ae91 851
8e33886e 852 data_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
0284adc6 853 if (data_fd < 0) {
56f64d95 854 log_error_errno(errno, "Failed to create data file: %m");
1137e6c7 855 r = -errno;
0284adc6
LP
856 goto fail;
857 }
0284adc6 858
8e33886e 859 entry_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
0284adc6 860 if (entry_fd < 0) {
56f64d95 861 log_error_errno(errno, "Failed to create entry file: %m");
1137e6c7 862 r = -errno;
0284adc6
LP
863 goto fail;
864 }
0284adc6 865
8e33886e 866 entry_array_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
0284adc6 867 if (entry_array_fd < 0) {
56f64d95 868 log_error_errno(errno, "Failed to create entry array file: %m");
1137e6c7 869 r = -errno;
0284adc6
LP
870 goto fail;
871 }
0284adc6 872
54f3ff07 873 if (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SUPPORTED) {
97147f8c 874 log_error("Cannot verify file with unknown extensions.");
15411c0c 875 r = -EOPNOTSUPP;
97147f8c
LP
876 goto fail;
877 }
878
879 for (i = 0; i < sizeof(f->header->reserved); i++)
880 if (f->header->reserved[i] != 0) {
54f3ff07 881 error(offsetof(Header, reserved[i]), "reserved field is non-zero");
97147f8c
LP
882 r = -EBADMSG;
883 goto fail;
884 }
885
0284adc6
LP
886 /* First iteration: we go through all objects, verify the
887 * superficial structure, headers, hashes. */
888
0284adc6 889 p = le64toh(f->header->header_size);
8dc37a85
LP
890 for (;;) {
891 /* Early exit if there are no objects in the file, at all */
892 if (le64toh(f->header->tail_object_offset) == 0)
893 break;
894
b72631e5 895 if (show_progress)
45c047b2 896 draw_progress(scale_progress(0x7FFF, p, le64toh(f->header->tail_object_offset)), &last_usec);
0284adc6 897
d05089d8 898 r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
0284adc6 899 if (r < 0) {
54f3ff07 900 error(p, "invalid object");
0284adc6
LP
901 goto fail;
902 }
903
97147f8c 904 if (p > le64toh(f->header->tail_object_offset)) {
54f3ff07 905 error(offsetof(Header, tail_object_offset), "invalid tail object pointer");
0284adc6
LP
906 r = -EBADMSG;
907 goto fail;
908 }
909
910 n_objects ++;
911
92fba83e 912 r = journal_file_object_verify(f, p, o);
0284adc6 913 if (r < 0) {
54f3ff07 914 error(p, "invalid object contents: %s", strerror(-r));
0284adc6
LP
915 goto fail;
916 }
917
d89c8fdf
ZJS
918 if ((o->object.flags & OBJECT_COMPRESSED_XZ) &&
919 (o->object.flags & OBJECT_COMPRESSED_LZ4)) {
54f3ff07
ZJS
920 error(p, "objected with double compression");
921 r = -EINVAL;
d89c8fdf
ZJS
922 goto fail;
923 }
924
925 if ((o->object.flags & OBJECT_COMPRESSED_XZ) && !JOURNAL_HEADER_COMPRESSED_XZ(f->header)) {
54f3ff07 926 error(p, "XZ compressed object in file without XZ compression");
d89c8fdf
ZJS
927 r = -EBADMSG;
928 goto fail;
929 }
930
931 if ((o->object.flags & OBJECT_COMPRESSED_LZ4) && !JOURNAL_HEADER_COMPRESSED_LZ4(f->header)) {
54f3ff07 932 error(p, "LZ4 compressed object in file without LZ4 compression");
0284adc6
LP
933 r = -EBADMSG;
934 goto fail;
935 }
936
a8e5f514 937 switch (o->object.type) {
0284adc6 938
a8e5f514
LP
939 case OBJECT_DATA:
940 r = write_uint64(data_fd, p);
941 if (r < 0)
0284adc6 942 goto fail;
0284adc6 943
a8e5f514
LP
944 n_data++;
945 break;
0284adc6 946
a8e5f514
LP
947 case OBJECT_FIELD:
948 n_fields++;
949 break;
0284adc6 950
a8e5f514 951 case OBJECT_ENTRY:
8088cbd3 952 if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) {
54f3ff07 953 error(p, "first entry before first tag");
6c7be122
LP
954 r = -EBADMSG;
955 goto fail;
956 }
957
0284adc6
LP
958 r = write_uint64(entry_fd, p);
959 if (r < 0)
960 goto fail;
961
f7fab8a5 962 if (le64toh(o->entry.realtime) < last_tag_realtime) {
54f3ff07 963 error(p, "older entry after newer tag");
7b5fd91c
LP
964 r = -EBADMSG;
965 goto fail;
966 }
967
0284adc6
LP
968 if (!entry_seqnum_set &&
969 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
54f3ff07 970 error(p, "head entry sequence number incorrect");
0284adc6
LP
971 r = -EBADMSG;
972 goto fail;
973 }
974
975 if (entry_seqnum_set &&
976 entry_seqnum >= le64toh(o->entry.seqnum)) {
54f3ff07 977 error(p, "entry sequence number out of synchronization");
0284adc6
LP
978 r = -EBADMSG;
979 goto fail;
980 }
981
982 entry_seqnum = le64toh(o->entry.seqnum);
983 entry_seqnum_set = true;
984
985 if (entry_monotonic_set &&
986 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
987 entry_monotonic > le64toh(o->entry.monotonic)) {
54f3ff07 988 error(p, "entry timestamp out of synchronization");
0284adc6
LP
989 r = -EBADMSG;
990 goto fail;
991 }
992
993 entry_monotonic = le64toh(o->entry.monotonic);
994 entry_boot_id = o->entry.boot_id;
995 entry_monotonic_set = true;
996
997 if (!entry_realtime_set &&
998 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
54f3ff07 999 error(p, "head entry realtime timestamp incorrect");
0284adc6
LP
1000 r = -EBADMSG;
1001 goto fail;
1002 }
1003
1004 entry_realtime = le64toh(o->entry.realtime);
1005 entry_realtime_set = true;
1006
1007 n_entries ++;
a8e5f514 1008 break;
0284adc6 1009
a8e5f514 1010 case OBJECT_DATA_HASH_TABLE:
0284adc6 1011 if (n_data_hash_tables > 1) {
54f3ff07 1012 error(p, "more than one data hash table");
0284adc6
LP
1013 r = -EBADMSG;
1014 goto fail;
1015 }
1016
1017 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
1018 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
54f3ff07 1019 error(p, "header fields for data hash table invalid");
0284adc6
LP
1020 r = -EBADMSG;
1021 goto fail;
1022 }
0284adc6 1023
a8e5f514
LP
1024 n_data_hash_tables++;
1025 break;
1026
1027 case OBJECT_FIELD_HASH_TABLE:
0284adc6 1028 if (n_field_hash_tables > 1) {
54f3ff07 1029 error(p, "more than one field hash table");
0284adc6
LP
1030 r = -EBADMSG;
1031 goto fail;
1032 }
1033
1034 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
1035 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
54f3ff07 1036 error(p, "header fields for field hash table invalid");
0284adc6
LP
1037 r = -EBADMSG;
1038 goto fail;
1039 }
a8e5f514
LP
1040
1041 n_field_hash_tables++;
1042 break;
1043
1044 case OBJECT_ENTRY_ARRAY:
1045 r = write_uint64(entry_array_fd, p);
1046 if (r < 0)
1047 goto fail;
1048
1049 if (p == le64toh(f->header->entry_array_offset)) {
1050 if (found_main_entry_array) {
54f3ff07 1051 error(p, "more than one main entry array");
a8e5f514
LP
1052 r = -EBADMSG;
1053 goto fail;
1054 }
1055
1056 found_main_entry_array = true;
1057 }
1058
1059 n_entry_arrays++;
1060 break;
1061
feb12d3e 1062 case OBJECT_TAG:
8088cbd3 1063 if (!JOURNAL_HEADER_SEALED(f->header)) {
54f3ff07 1064 error(p, "tag object in file without sealing");
a8e5f514
LP
1065 r = -EBADMSG;
1066 goto fail;
1067 }
1068
1069 if (le64toh(o->tag.seqnum) != n_tags + 1) {
54f3ff07 1070 error(p, "tag sequence number out of synchronization");
a8e5f514
LP
1071 r = -EBADMSG;
1072 goto fail;
1073 }
1074
14d10188 1075 if (le64toh(o->tag.epoch) < last_epoch) {
54f3ff07 1076 error(p, "epoch sequence out of synchronization");
7b5fd91c
LP
1077 r = -EBADMSG;
1078 goto fail;
1079 }
1080
feb12d3e 1081#ifdef HAVE_GCRYPT
c586dbf1 1082 if (f->seal) {
feb12d3e
LP
1083 uint64_t q, rt;
1084
54f3ff07 1085 debug(p, "checking tag %"PRIu64"...", le64toh(o->tag.seqnum));
14d10188 1086
f7fab8a5
LP
1087 rt = f->fss_start_usec + o->tag.epoch * f->fss_interval_usec;
1088 if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) {
54f3ff07 1089 error(p, "tag/entry realtime timestamp out of synchronization");
c586dbf1
LP
1090 r = -EBADMSG;
1091 goto fail;
1092 }
14d10188 1093
c586dbf1
LP
1094 /* OK, now we know the epoch. So let's now set
1095 * it, and calculate the HMAC for everything
1096 * since the last tag. */
1097 r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
14d10188
LP
1098 if (r < 0)
1099 goto fail;
1100
c586dbf1 1101 r = journal_file_hmac_start(f);
14d10188
LP
1102 if (r < 0)
1103 goto fail;
1104
c586dbf1
LP
1105 if (last_tag == 0) {
1106 r = journal_file_hmac_put_header(f);
1107 if (r < 0)
1108 goto fail;
1109
1110 q = le64toh(f->header->header_size);
1111 } else
1112 q = last_tag;
1113
1114 while (q <= p) {
d05089d8 1115 r = journal_file_move_to_object(f, OBJECT_UNUSED, q, &o);
c586dbf1
LP
1116 if (r < 0)
1117 goto fail;
1118
d05089d8 1119 r = journal_file_hmac_put_object(f, OBJECT_UNUSED, o, q);
c586dbf1
LP
1120 if (r < 0)
1121 goto fail;
1122
1123 q = q + ALIGN64(le64toh(o->object.size));
1124 }
1125
1126 /* Position might have changed, let's reposition things */
d05089d8 1127 r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
14d10188
LP
1128 if (r < 0)
1129 goto fail;
1130
c586dbf1 1131 if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
54f3ff07 1132 error(p, "tag failed verification");
c586dbf1
LP
1133 r = -EBADMSG;
1134 goto fail;
1135 }
14d10188 1136
c586dbf1
LP
1137 f->hmac_running = false;
1138 last_tag_realtime = rt;
f7fab8a5 1139 last_sealed_realtime = entry_realtime;
14d10188
LP
1140 }
1141
14d10188 1142 last_tag = p + ALIGN64(le64toh(o->object.size));
0ab5c3ed
ZJS
1143#endif
1144
c586dbf1 1145 last_epoch = le64toh(o->tag.epoch);
6c7be122 1146
a8e5f514
LP
1147 n_tags ++;
1148 break;
1149
1150 default:
0284adc6 1151 n_weird ++;
a8e5f514 1152 }
0284adc6 1153
8dc37a85
LP
1154 if (p == le64toh(f->header->tail_object_offset)) {
1155 found_last = true;
1156 break;
1157 }
0284adc6 1158
8dc37a85
LP
1159 p = p + ALIGN64(le64toh(o->object.size));
1160 };
1161
1162 if (!found_last && le64toh(f->header->tail_object_offset) != 0) {
54f3ff07 1163 error(le64toh(f->header->tail_object_offset), "tail object pointer dead");
97147f8c
LP
1164 r = -EBADMSG;
1165 goto fail;
1166 }
1167
0284adc6 1168 if (n_objects != le64toh(f->header->n_objects)) {
54f3ff07 1169 error(offsetof(Header, n_objects), "object number mismatch");
0284adc6
LP
1170 r = -EBADMSG;
1171 goto fail;
1172 }
1173
1174 if (n_entries != le64toh(f->header->n_entries)) {
54f3ff07 1175 error(offsetof(Header, n_entries), "entry number mismatch");
0284adc6
LP
1176 r = -EBADMSG;
1177 goto fail;
1178 }
1179
1180 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
1181 n_data != le64toh(f->header->n_data)) {
54f3ff07 1182 error(offsetof(Header, n_data), "data number mismatch");
0284adc6
LP
1183 r = -EBADMSG;
1184 goto fail;
1185 }
1186
1187 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
1188 n_fields != le64toh(f->header->n_fields)) {
54f3ff07 1189 error(offsetof(Header, n_fields), "field number mismatch");
0284adc6
LP
1190 r = -EBADMSG;
1191 goto fail;
1192 }
1193
1194 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
a8e5f514 1195 n_tags != le64toh(f->header->n_tags)) {
54f3ff07 1196 error(offsetof(Header, n_tags), "tag number mismatch");
0284adc6
LP
1197 r = -EBADMSG;
1198 goto fail;
1199 }
1200
2dee23eb
LP
1201 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
1202 n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
54f3ff07 1203 error(offsetof(Header, n_entry_arrays), "entry array number mismatch");
2dee23eb
LP
1204 r = -EBADMSG;
1205 goto fail;
1206 }
1207
8dc37a85 1208 if (!found_main_entry_array && le64toh(f->header->entry_array_offset) != 0) {
54f3ff07 1209 error(0, "missing entry array");
0284adc6
LP
1210 r = -EBADMSG;
1211 goto fail;
1212 }
1213
1214 if (entry_seqnum_set &&
1215 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
54f3ff07 1216 error(offsetof(Header, tail_entry_seqnum), "invalid tail seqnum");
0284adc6
LP
1217 r = -EBADMSG;
1218 goto fail;
1219 }
1220
1221 if (entry_monotonic_set &&
1222 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
1223 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
54f3ff07 1224 error(0, "invalid tail monotonic timestamp");
0284adc6
LP
1225 r = -EBADMSG;
1226 goto fail;
1227 }
1228
1229 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
54f3ff07 1230 error(0, "invalid tail realtime timestamp");
0284adc6
LP
1231 r = -EBADMSG;
1232 goto fail;
1233 }
1234
86adf873
LP
1235 /* Second iteration: we follow all objects referenced from the
1236 * two entry points: the object hash table and the entry
1237 * array. We also check that everything referenced (directly
1238 * or indirectly) in the data hash table also exists in the
1239 * entry array, and vice versa. Note that we do not care for
1240 * unreferenced objects. We only care that everything that is
1241 * referenced is consistent. */
1242
1243 r = verify_entry_array(f,
1244 data_fd, n_data,
1245 entry_fd, n_entries,
1246 entry_array_fd, n_entry_arrays,
b72631e5
LP
1247 &last_usec,
1248 show_progress);
86adf873
LP
1249 if (r < 0)
1250 goto fail;
0284adc6 1251
86adf873
LP
1252 r = verify_hash_table(f,
1253 data_fd, n_data,
1254 entry_fd, n_entries,
1255 entry_array_fd, n_entry_arrays,
b72631e5
LP
1256 &last_usec,
1257 show_progress);
86adf873
LP
1258 if (r < 0)
1259 goto fail;
0284adc6 1260
b72631e5
LP
1261 if (show_progress)
1262 flush_progress();
0284adc6
LP
1263
1264 mmap_cache_close_fd(f->mmap, data_fd);
1265 mmap_cache_close_fd(f->mmap, entry_fd);
1266 mmap_cache_close_fd(f->mmap, entry_array_fd);
1267
03e334a1
LP
1268 safe_close(data_fd);
1269 safe_close(entry_fd);
1270 safe_close(entry_array_fd);
0284adc6 1271
2a7b539a
LP
1272 if (first_contained)
1273 *first_contained = le64toh(f->header->head_entry_realtime);
6c7be122 1274 if (last_validated)
f7fab8a5 1275 *last_validated = last_sealed_realtime;
6c7be122
LP
1276 if (last_contained)
1277 *last_contained = le64toh(f->header->tail_entry_realtime);
1278
0284adc6
LP
1279 return 0;
1280
1281fail:
b72631e5
LP
1282 if (show_progress)
1283 flush_progress();
0284adc6 1284
92fba83e 1285 log_error("File corruption detected at %s:"OFSfmt" (of %llu bytes, %"PRIu64"%%).",
0284adc6 1286 f->path,
507f22bd 1287 p,
0284adc6 1288 (unsigned long long) f->last_stat.st_size,
507f22bd 1289 100 * p / f->last_stat.st_size);
0284adc6
LP
1290
1291 if (data_fd >= 0) {
1292 mmap_cache_close_fd(f->mmap, data_fd);
03e334a1 1293 safe_close(data_fd);
0284adc6
LP
1294 }
1295
1296 if (entry_fd >= 0) {
1297 mmap_cache_close_fd(f->mmap, entry_fd);
03e334a1 1298 safe_close(entry_fd);
0284adc6
LP
1299 }
1300
1301 if (entry_array_fd >= 0) {
1302 mmap_cache_close_fd(f->mmap, entry_array_fd);
03e334a1 1303 safe_close(entry_array_fd);
0284adc6
LP
1304 }
1305
1306 return r;
1307}