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