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