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