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