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