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