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