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