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