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