]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journal-verify.c
6871433402e394fb3998f250d9014fe8def2a147
[thirdparty/systemd.git] / src / journal / journal-verify.c
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
26 #include "util.h"
27 #include "macro.h"
28 #include "journal-def.h"
29 #include "journal-file.h"
30 #include "journal-authenticate.h"
31 #include "journal-verify.h"
32 #include "lookup3.h"
33 #include "compress.h"
34 #include "fsprg.h"
35
36 /* FIXME:
37 *
38 * - verify FSPRG
39 * - Allow building without libgcrypt
40 * - check with sparse
41 * - 64bit conversions
42 *
43 * */
44
45 static int journal_file_object_verify(JournalFile *f, Object *o) {
46 assert(f);
47 assert(o);
48
49 /* This does various superficial tests about the length an
50 * possible field values. It does not follow any references to
51 * other objects. */
52
53 if ((o->object.flags & OBJECT_COMPRESSED) &&
54 o->object.type != OBJECT_DATA)
55 return -EBADMSG;
56
57 switch (o->object.type) {
58
59 case OBJECT_DATA: {
60 uint64_t h1, h2;
61
62 if (le64toh(o->data.entry_offset) <= 0 ||
63 le64toh(o->data.n_entries) <= 0)
64 return -EBADMSG;
65
66 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
67 return -EBADMSG;
68
69 h1 = le64toh(o->data.hash);
70
71 if (o->object.flags & OBJECT_COMPRESSED) {
72 void *b = NULL;
73 uint64_t alloc = 0, b_size;
74
75 if (!uncompress_blob(o->data.payload,
76 le64toh(o->object.size) - offsetof(Object, data.payload),
77 &b, &alloc, &b_size))
78 return -EBADMSG;
79
80 h2 = hash64(b, b_size);
81 free(b);
82 } else
83 h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
84
85 if (h1 != h2)
86 return -EBADMSG;
87
88 break;
89 }
90
91 case OBJECT_FIELD:
92 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
93 return -EBADMSG;
94 break;
95
96 case OBJECT_ENTRY:
97 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
98 return -EBADMSG;
99
100 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
101 return -EBADMSG;
102
103 if (le64toh(o->entry.seqnum) <= 0 ||
104 le64toh(o->entry.realtime) <= 0)
105 return -EBADMSG;
106
107 break;
108
109 case OBJECT_DATA_HASH_TABLE:
110 case OBJECT_FIELD_HASH_TABLE:
111 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0)
112 return -EBADMSG;
113
114 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
115 return -EBADMSG;
116
117 break;
118
119 case OBJECT_ENTRY_ARRAY:
120 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0)
121 return -EBADMSG;
122
123 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
124 return -EBADMSG;
125
126 break;
127
128 case OBJECT_TAG:
129 if (le64toh(o->object.size) != sizeof(TagObject))
130 return -EBADMSG;
131 break;
132 }
133
134 return 0;
135 }
136
137 static void draw_progress(uint64_t p, usec_t *last_usec) {
138 unsigned n, i, j, k;
139 usec_t z, x;
140
141 if (!isatty(STDOUT_FILENO))
142 return;
143
144 z = now(CLOCK_MONOTONIC);
145 x = *last_usec;
146
147 if (x != 0 && x + 40 * USEC_PER_MSEC > z)
148 return;
149
150 *last_usec = z;
151
152 n = (3 * columns()) / 4;
153 j = (n * (unsigned) p) / 65535ULL;
154 k = n - j;
155
156 fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
157
158 for (i = 0; i < j; i++)
159 fputs("\xe2\x96\x88", stdout);
160
161 fputs(ANSI_HIGHLIGHT_OFF, stdout);
162
163 for (i = 0; i < k; i++)
164 fputs("\xe2\x96\x91", stdout);
165
166 printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU);
167
168 fputs("\r\x1B[?25h", stdout);
169 fflush(stdout);
170 }
171
172 static void flush_progress(void) {
173 unsigned n, i;
174
175 if (!isatty(STDOUT_FILENO))
176 return;
177
178 n = (3 * columns()) / 4;
179
180 putchar('\r');
181
182 for (i = 0; i < n + 5; i++)
183 putchar(' ');
184
185 putchar('\r');
186 fflush(stdout);
187 }
188
189 static int write_uint64(int fd, uint64_t p) {
190 ssize_t k;
191
192 k = write(fd, &p, sizeof(p));
193 if (k < 0)
194 return -errno;
195 if (k != sizeof(p))
196 return -EIO;
197
198 return 0;
199 }
200
201 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
202 uint64_t a, b;
203 int r;
204
205 assert(m);
206 assert(fd >= 0);
207
208 /* Bisection ... */
209
210 a = 0; b = n;
211 while (a < b) {
212 uint64_t c, *z;
213
214 c = (a + b) / 2;
215
216 r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z);
217 if (r < 0)
218 return r;
219
220 if (*z == p)
221 return 1;
222
223 if (p < *z)
224 b = c;
225 else
226 a = c;
227 }
228
229 return 0;
230 }
231
232 static int entry_points_to_data(
233 JournalFile *f,
234 int entry_fd,
235 uint64_t n_entries,
236 uint64_t entry_p,
237 uint64_t data_p) {
238
239 int r;
240 uint64_t i, n, a;
241 Object *o;
242 bool found = false;
243
244 assert(f);
245 assert(entry_fd >= 0);
246
247 if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
248 log_error("Data object references invalid entry at %llu", (unsigned long long) data_p);
249 return -EBADMSG;
250 }
251
252 r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
253 if (r < 0)
254 return r;
255
256 n = journal_file_entry_n_items(o);
257 for (i = 0; i < n; i++)
258 if (le64toh(o->entry.items[i].object_offset) == data_p) {
259 found = true;
260 break;
261 }
262
263 if (!found) {
264 log_error("Data object not referenced by linked entry at %llu", (unsigned long long) data_p);
265 return -EBADMSG;
266 }
267
268 /* Check if this entry is also in main entry array. Since the
269 * main entry array has already been verified we can rely on
270 * its consistency.*/
271
272 n = le64toh(f->header->n_entries);
273 a = le64toh(f->header->entry_array_offset);
274 i = 0;
275
276 while (i < n) {
277 uint64_t m, j;
278
279 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
280 if (r < 0)
281 return r;
282
283 m = journal_file_entry_array_n_items(o);
284 for (j = 0; i < n && j < m; i++, j++)
285 if (le64toh(o->entry_array.items[j]) == entry_p)
286 return 0;
287
288 a = le64toh(o->entry_array.next_entry_array_offset);;
289 }
290
291 return 0;
292 }
293
294 static int verify_data(
295 JournalFile *f,
296 Object *o, uint64_t p,
297 int entry_fd, uint64_t n_entries,
298 int entry_array_fd, uint64_t n_entry_arrays) {
299
300 uint64_t i, n, a, last, q;
301 int r;
302
303 assert(f);
304 assert(o);
305 assert(entry_fd >= 0);
306 assert(entry_array_fd >= 0);
307
308 n = le64toh(o->data.n_entries);
309 a = le64toh(o->data.entry_array_offset);
310
311 /* We already checked this earlier */
312 assert(n > 0);
313
314 last = q = le64toh(o->data.entry_offset);
315 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
316 if (r < 0)
317 return r;
318
319 i = 1;
320 while (i < n) {
321 uint64_t next, m, j;
322
323 if (a == 0) {
324 log_error("Array chain too short at %llu.", (unsigned long long) p);
325 return -EBADMSG;
326 }
327
328 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
329 log_error("Invalid array at %llu.", (unsigned long long) p);
330 return -EBADMSG;
331 }
332
333 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
334 if (r < 0)
335 return r;
336
337 next = le64toh(o->entry_array.next_entry_array_offset);
338 if (next != 0 && next <= a) {
339 log_error("Array chain has cycle at %llu.", (unsigned long long) p);
340 return -EBADMSG;
341 }
342
343 m = journal_file_entry_array_n_items(o);
344 for (j = 0; i < n && j < m; i++, j++) {
345
346 q = le64toh(o->entry_array.items[j]);
347 if (q <= last) {
348 log_error("Data object's entry array not sorted at %llu.", (unsigned long long) p);
349 return -EBADMSG;
350 }
351 last = q;
352
353 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
354 if (r < 0)
355 return r;
356
357 /* Pointer might have moved, reposition */
358 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
359 if (r < 0)
360 return r;
361 }
362
363 a = next;
364 }
365
366 return 0;
367 }
368
369 static int verify_hash_table(
370 JournalFile *f,
371 int data_fd, uint64_t n_data,
372 int entry_fd, uint64_t n_entries,
373 int entry_array_fd, uint64_t n_entry_arrays,
374 usec_t *last_usec) {
375
376 uint64_t i, n;
377 int r;
378
379 assert(f);
380 assert(data_fd >= 0);
381 assert(entry_fd >= 0);
382 assert(entry_array_fd >= 0);
383 assert(last_usec);
384
385 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
386 for (i = 0; i < n; i++) {
387 uint64_t last = 0, p;
388
389 draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
390
391 p = le64toh(f->data_hash_table[i].head_hash_offset);
392 while (p != 0) {
393 Object *o;
394 uint64_t next;
395
396 if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
397 log_error("Invalid data object at hash entry %llu of %llu.",
398 (unsigned long long) i, (unsigned long long) n);
399 return -EBADMSG;
400 }
401
402 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
403 if (r < 0)
404 return r;
405
406 next = le64toh(o->data.next_hash_offset);
407 if (next != 0 && next <= p) {
408 log_error("Hash chain has a cycle in hash entry %llu of %llu.",
409 (unsigned long long) i, (unsigned long long) n);
410 return -EBADMSG;
411 }
412
413 if (le64toh(o->data.hash) % n != i) {
414 log_error("Hash value mismatch in hash entry %llu of %llu.",
415 (unsigned long long) i, (unsigned long long) n);
416 return -EBADMSG;
417 }
418
419 r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
420 if (r < 0)
421 return r;
422
423 last = p;
424 p = next;
425 }
426
427 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
428 log_error("Tail hash pointer mismatch in hash table.");
429 return -EBADMSG;
430 }
431 }
432
433 return 0;
434 }
435
436 static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
437 uint64_t n, h, q;
438 int r;
439 assert(f);
440
441 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
442 h = hash % n;
443
444 q = le64toh(f->data_hash_table[h].head_hash_offset);
445 while (q != 0) {
446 Object *o;
447
448 if (p == q)
449 return 1;
450
451 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
452 if (r < 0)
453 return r;
454
455 q = le64toh(o->data.next_hash_offset);
456 }
457
458 return 0;
459 }
460
461 static int verify_entry(
462 JournalFile *f,
463 Object *o, uint64_t p,
464 int data_fd, uint64_t n_data) {
465
466 uint64_t i, n;
467 int r;
468
469 assert(f);
470 assert(o);
471 assert(data_fd >= 0);
472
473 n = journal_file_entry_n_items(o);
474 for (i = 0; i < n; i++) {
475 uint64_t q, h;
476 Object *u;
477
478 q = le64toh(o->entry.items[i].object_offset);
479 h = le64toh(o->entry.items[i].hash);
480
481 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
482 log_error("Invalid data object at entry %llu.",
483 (unsigned long long) o);
484 return -EBADMSG;
485 }
486
487 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
488 if (r < 0)
489 return r;
490
491 if (le64toh(u->data.hash) != h) {
492 log_error("Hash mismatch for data object at entry %llu.",
493 (unsigned long long) p);
494 return -EBADMSG;
495 }
496
497 r = data_object_in_hash_table(f, h, q);
498 if (r < 0)
499 return r;
500 if (r == 0) {
501 log_error("Data object missing from hash at entry %llu.",
502 (unsigned long long) p);
503 return -EBADMSG;
504 }
505 }
506
507 return 0;
508 }
509
510 static int verify_entry_array(
511 JournalFile *f,
512 int data_fd, uint64_t n_data,
513 int entry_fd, uint64_t n_entries,
514 int entry_array_fd, uint64_t n_entry_arrays,
515 usec_t *last_usec) {
516
517 uint64_t i = 0, a, n, last = 0;
518 int r;
519
520 assert(f);
521 assert(data_fd >= 0);
522 assert(entry_fd >= 0);
523 assert(entry_array_fd >= 0);
524 assert(last_usec);
525
526 n = le64toh(f->header->n_entries);
527 a = le64toh(f->header->entry_array_offset);
528 while (i < n) {
529 uint64_t next, m, j;
530 Object *o;
531
532 draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
533
534 if (a == 0) {
535 log_error("Array chain too short at %llu of %llu.",
536 (unsigned long long) i, (unsigned long long) n);
537 return -EBADMSG;
538 }
539
540 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
541 log_error("Invalid array at %llu of %llu.",
542 (unsigned long long) i, (unsigned long long) n);
543 return -EBADMSG;
544 }
545
546 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
547 if (r < 0)
548 return r;
549
550 next = le64toh(o->entry_array.next_entry_array_offset);
551 if (next != 0 && next <= a) {
552 log_error("Array chain has cycle at %llu of %llu.",
553 (unsigned long long) i, (unsigned long long) n);
554 return -EBADMSG;
555 }
556
557 m = journal_file_entry_array_n_items(o);
558 for (j = 0; i < n && j < m; i++, j++) {
559 uint64_t p;
560
561 p = le64toh(o->entry_array.items[j]);
562 if (p <= last) {
563 log_error("Entry array not sorted at %llu of %llu.",
564 (unsigned long long) i, (unsigned long long) n);
565 return -EBADMSG;
566 }
567 last = p;
568
569 if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
570 log_error("Invalid array entry at %llu of %llu.",
571 (unsigned long long) i, (unsigned long long) n);
572 return -EBADMSG;
573 }
574
575 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
576 if (r < 0)
577 return r;
578
579 r = verify_entry(f, o, p, data_fd, n_data);
580 if (r < 0)
581 return r;
582
583 /* Pointer might have moved, reposition */
584 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
585 if (r < 0)
586 return r;
587 }
588
589 a = next;
590 }
591
592 return 0;
593 }
594
595 static int journal_file_parse_seed(JournalFile *f, const char *s) {
596 uint8_t *seed;
597 size_t seed_size, c;
598 const char *k;
599 int r;
600 unsigned long long start, interval;
601
602 seed_size = FSPRG_RECOMMENDED_SEEDLEN;
603 seed = malloc(seed_size);
604 if (!seed)
605 return -ENOMEM;
606
607 k = s;
608 for (c = 0; c < seed_size; c++) {
609 int x, y;
610
611 while (*k == '-')
612 k++;
613
614 x = unhexchar(*k);
615 if (x < 0) {
616 free(seed);
617 return -EINVAL;
618 }
619 k++;
620 y = unhexchar(*k);
621 if (y < 0) {
622 free(seed);
623 return -EINVAL;
624 }
625 k++;
626
627 seed[c] = (uint8_t) (x * 16 + y);
628 }
629
630 if (*k != '/') {
631 free(seed);
632 return -EINVAL;
633 }
634 k++;
635
636 r = sscanf(k, "%llx-%llx", &start, &interval);
637 if (r != 2) {
638 free(seed);
639 return -EINVAL;
640 }
641
642 f->fsprg_seed = seed;
643 f->fsprg_seed_size = seed_size;
644 f->fsprg_start_usec = start;
645 f->fsprg_interval_usec = interval;
646
647 return 0;
648 }
649
650 int journal_file_verify(JournalFile *f, const char *seed) {
651 int r;
652 Object *o;
653 uint64_t p = 0;
654 uint64_t tag_seqnum = 0, entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
655 sd_id128_t entry_boot_id;
656 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
657 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;
658 usec_t last_usec = 0;
659 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
660 char data_path[] = "/var/tmp/journal-data-XXXXXX",
661 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
662 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
663
664 assert(f);
665
666 if (seed) {
667 r = journal_file_parse_seed(f, seed);
668 if (r < 0) {
669 log_error("Failed to parse seed.");
670 return r;
671 }
672 }
673
674 data_fd = mkostemp(data_path, O_CLOEXEC);
675 if (data_fd < 0) {
676 log_error("Failed to create data file: %m");
677 r = -errno;
678 goto fail;
679 }
680 unlink(data_path);
681
682 entry_fd = mkostemp(entry_path, O_CLOEXEC);
683 if (entry_fd < 0) {
684 log_error("Failed to create entry file: %m");
685 r = -errno;
686 goto fail;
687 }
688 unlink(entry_path);
689
690 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
691 if (entry_array_fd < 0) {
692 log_error("Failed to create entry array file: %m");
693 r = -errno;
694 goto fail;
695 }
696 unlink(entry_array_path);
697
698 /* First iteration: we go through all objects, verify the
699 * superficial structure, headers, hashes. */
700
701 r = journal_file_hmac_put_header(f);
702 if (r < 0) {
703 log_error("Failed to calculate HMAC of header.");
704 goto fail;
705 }
706
707 p = le64toh(f->header->header_size);
708 while (p != 0) {
709 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
710
711 r = journal_file_move_to_object(f, -1, p, &o);
712 if (r < 0) {
713 log_error("Invalid object at %llu", (unsigned long long) p);
714 goto fail;
715 }
716
717 if (le64toh(f->header->tail_object_offset) < p) {
718 log_error("Invalid tail object pointer.");
719 r = -EBADMSG;
720 goto fail;
721 }
722
723 n_objects ++;
724
725 r = journal_file_object_verify(f, o);
726 if (r < 0) {
727 log_error("Invalid object contents at %llu", (unsigned long long) p);
728 goto fail;
729 }
730
731 if (o->object.flags & OBJECT_COMPRESSED &&
732 !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
733 log_error("Compressed object without compression at %llu", (unsigned long long) p);
734 r = -EBADMSG;
735 goto fail;
736 }
737
738 r = journal_file_hmac_put_object(f, -1, p);
739 if (r < 0) {
740 log_error("Failed to calculate HMAC at %llu", (unsigned long long) p);
741 goto fail;
742 }
743
744 if (o->object.type == OBJECT_TAG) {
745
746 if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) {
747 log_error("Tag object without authentication at %llu", (unsigned long long) p);
748 r = -EBADMSG;
749 goto fail;
750 }
751
752 if (le64toh(o->tag.seqnum) != tag_seqnum) {
753 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
754 r = -EBADMSG;
755 goto fail;
756 }
757
758 } else if (o->object.type == OBJECT_ENTRY) {
759
760 r = write_uint64(entry_fd, p);
761 if (r < 0)
762 goto fail;
763
764 if (!entry_seqnum_set &&
765 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
766 log_error("Head entry sequence number incorrect");
767 r = -EBADMSG;
768 goto fail;
769 }
770
771 if (entry_seqnum_set &&
772 entry_seqnum >= le64toh(o->entry.seqnum)) {
773 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
774 r = -EBADMSG;
775 goto fail;
776 }
777
778 entry_seqnum = le64toh(o->entry.seqnum);
779 entry_seqnum_set = true;
780
781 if (entry_monotonic_set &&
782 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
783 entry_monotonic > le64toh(o->entry.monotonic)) {
784 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
785 r = -EBADMSG;
786 goto fail;
787 }
788
789 entry_monotonic = le64toh(o->entry.monotonic);
790 entry_boot_id = o->entry.boot_id;
791 entry_monotonic_set = true;
792
793 if (!entry_realtime_set &&
794 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
795 log_error("Head entry realtime timestamp incorrect");
796 r = -EBADMSG;
797 goto fail;
798 }
799
800 entry_realtime = le64toh(o->entry.realtime);
801 entry_realtime_set = true;
802
803 n_entries ++;
804 } else if (o->object.type == OBJECT_ENTRY_ARRAY) {
805
806 r = write_uint64(entry_array_fd, p);
807 if (r < 0)
808 goto fail;
809
810 if (p == le64toh(f->header->entry_array_offset)) {
811 if (found_main_entry_array) {
812 log_error("More than one main entry array at %llu", (unsigned long long) p);
813 r = -EBADMSG;
814 goto fail;
815 }
816
817 found_main_entry_array = true;
818 }
819
820 n_entry_arrays++;
821
822 } else if (o->object.type == OBJECT_DATA) {
823
824 r = write_uint64(data_fd, p);
825 if (r < 0)
826 goto fail;
827
828 n_data++;
829
830 } else if (o->object.type == OBJECT_FIELD)
831 n_fields++;
832 else if (o->object.type == OBJECT_DATA_HASH_TABLE) {
833 n_data_hash_tables++;
834
835 if (n_data_hash_tables > 1) {
836 log_error("More than one data hash table at %llu", (unsigned long long) p);
837 r = -EBADMSG;
838 goto fail;
839 }
840
841 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
842 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
843 log_error("Header fields for data hash table invalid.");
844 r = -EBADMSG;
845 goto fail;
846 }
847 } else if (o->object.type == OBJECT_FIELD_HASH_TABLE) {
848 n_field_hash_tables++;
849
850 if (n_field_hash_tables > 1) {
851 log_error("More than one field hash table at %llu", (unsigned long long) p);
852 r = -EBADMSG;
853 goto fail;
854 }
855
856 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
857 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
858 log_error("Header fields for field hash table invalid.");
859 r = -EBADMSG;
860 goto fail;
861 }
862 } else if (o->object.type >= _OBJECT_TYPE_MAX)
863 n_weird ++;
864
865 if (p == le64toh(f->header->tail_object_offset))
866 p = 0;
867 else
868 p = p + ALIGN64(le64toh(o->object.size));
869 }
870
871 if (n_objects != le64toh(f->header->n_objects)) {
872 log_error("Object number mismatch");
873 r = -EBADMSG;
874 goto fail;
875 }
876
877 if (n_entries != le64toh(f->header->n_entries)) {
878 log_error("Entry number mismatch");
879 r = -EBADMSG;
880 goto fail;
881 }
882
883 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
884 n_data != le64toh(f->header->n_data)) {
885 log_error("Data number mismatch");
886 r = -EBADMSG;
887 goto fail;
888 }
889
890 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
891 n_fields != le64toh(f->header->n_fields)) {
892 log_error("Field number mismatch");
893 r = -EBADMSG;
894 goto fail;
895 }
896
897 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
898 tag_seqnum != le64toh(f->header->n_tags)) {
899 log_error("Tag number mismatch");
900 r = -EBADMSG;
901 goto fail;
902 }
903
904 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
905 n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
906 log_error("Entry array number mismatch");
907 r = -EBADMSG;
908 goto fail;
909 }
910
911 if (n_data_hash_tables != 1) {
912 log_error("Missing data hash table");
913 r = -EBADMSG;
914 goto fail;
915 }
916
917 if (n_field_hash_tables != 1) {
918 log_error("Missing field hash table");
919 r = -EBADMSG;
920 goto fail;
921 }
922
923 if (!found_main_entry_array) {
924 log_error("Missing entry array");
925 r = -EBADMSG;
926 goto fail;
927 }
928
929 if (entry_seqnum_set &&
930 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
931 log_error("Invalid tail seqnum");
932 r = -EBADMSG;
933 goto fail;
934 }
935
936 if (entry_monotonic_set &&
937 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
938 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
939 log_error("Invalid tail monotonic timestamp");
940 r = -EBADMSG;
941 goto fail;
942 }
943
944 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
945 log_error("Invalid tail realtime timestamp");
946 r = -EBADMSG;
947 goto fail;
948 }
949
950 /* Second iteration: we follow all objects referenced from the
951 * two entry points: the object hash table and the entry
952 * array. We also check that everything referenced (directly
953 * or indirectly) in the data hash table also exists in the
954 * entry array, and vice versa. Note that we do not care for
955 * unreferenced objects. We only care that everything that is
956 * referenced is consistent. */
957
958 r = verify_entry_array(f,
959 data_fd, n_data,
960 entry_fd, n_entries,
961 entry_array_fd, n_entry_arrays,
962 &last_usec);
963 if (r < 0)
964 goto fail;
965
966 r = verify_hash_table(f,
967 data_fd, n_data,
968 entry_fd, n_entries,
969 entry_array_fd, n_entry_arrays,
970 &last_usec);
971 if (r < 0)
972 goto fail;
973
974 flush_progress();
975
976 mmap_cache_close_fd(f->mmap, data_fd);
977 mmap_cache_close_fd(f->mmap, entry_fd);
978 mmap_cache_close_fd(f->mmap, entry_array_fd);
979
980 close_nointr_nofail(data_fd);
981 close_nointr_nofail(entry_fd);
982 close_nointr_nofail(entry_array_fd);
983
984 return 0;
985
986 fail:
987 flush_progress();
988
989 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
990 f->path,
991 (unsigned long long) p,
992 (unsigned long long) f->last_stat.st_size,
993 (unsigned long long) (100 * p / f->last_stat.st_size));
994
995 if (data_fd >= 0) {
996 mmap_cache_close_fd(f->mmap, data_fd);
997 close_nointr_nofail(data_fd);
998 }
999
1000 if (entry_fd >= 0) {
1001 mmap_cache_close_fd(f->mmap, entry_fd);
1002 close_nointr_nofail(entry_fd);
1003 }
1004
1005 if (entry_array_fd >= 0) {
1006 mmap_cache_close_fd(f->mmap, entry_array_fd);
1007 close_nointr_nofail(entry_array_fd);
1008 }
1009
1010 return r;
1011 }