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