1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
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.
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.
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/>.
29 #include "journal-def.h"
30 #include "journal-file.h"
31 #include "journal-authenticate.h"
32 #include "journal-verify.h"
36 static void draw_progress(uint64_t p, usec_t *last_usec) {
43 z = now(CLOCK_MONOTONIC);
46 if (x != 0 && x + 40 * USEC_PER_MSEC > z)
51 n = (3 * columns()) / 4;
52 j = (n * (unsigned) p) / 65535ULL;
55 fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
57 for (i = 0; i < j; i++)
58 fputs("\xe2\x96\x88", stdout);
60 fputs(ANSI_HIGHLIGHT_OFF, stdout);
62 for (i = 0; i < k; i++)
63 fputs("\xe2\x96\x91", stdout);
65 printf(" %3"PRIu64"%%", 100U * p / 65535U);
67 fputs("\r\x1B[?25h", stdout);
71 static void flush_progress(void) {
77 n = (3 * columns()) / 4;
81 for (i = 0; i < n + 5; i++)
88 #define debug(_offset, _fmt, ...) do{ \
90 log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
93 #define warning(_offset, _fmt, ...) do{ \
95 log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
98 #define error(_offset, _fmt, ...) do{ \
100 log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
103 static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) {
110 /* This does various superficial tests about the length an
111 * possible field values. It does not follow any references to
114 if ((o->object.flags & OBJECT_COMPRESSED_XZ) &&
115 o->object.type != OBJECT_DATA)
118 switch (o->object.type) {
124 if (le64toh(o->data.entry_offset) == 0)
125 warning(offset, "unused data (entry_offset==0)");
127 if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) {
128 error(offset, "bad n_entries: %"PRIu64, o->data.n_entries);
132 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) {
133 error(offset, "bad object size (<= %zu): %"PRIu64,
134 offsetof(DataObject, payload),
135 le64toh(o->object.size));
139 h1 = le64toh(o->data.hash);
141 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
143 _cleanup_free_ void *b = NULL;
144 size_t alloc = 0, b_size;
146 r = decompress_blob(compression,
148 le64toh(o->object.size) - offsetof(Object, data.payload),
149 &b, &alloc, &b_size, 0);
151 error(offset, "%s decompression failed: %s",
152 object_compressed_to_string(compression), strerror(-r));
156 h2 = hash64(b, b_size);
158 h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
161 error(offset, "invalid hash (%08"PRIx64" vs. %08"PRIx64, h1, h2);
165 if (!VALID64(o->data.next_hash_offset) ||
166 !VALID64(o->data.next_field_offset) ||
167 !VALID64(o->data.entry_offset) ||
168 !VALID64(o->data.entry_array_offset)) {
169 error(offset, "invalid offset (next_hash_offset="OFSfmt", next_field_offset="OFSfmt", entry_offset="OFSfmt", entry_array_offset="OFSfmt,
170 o->data.next_hash_offset,
171 o->data.next_field_offset,
172 o->data.entry_offset,
173 o->data.entry_array_offset);
181 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) {
183 "bad field size (<= %zu): %"PRIu64,
184 offsetof(FieldObject, payload),
185 le64toh(o->object.size));
189 if (!VALID64(o->field.next_hash_offset) ||
190 !VALID64(o->field.head_data_offset)) {
192 "invalid offset (next_hash_offset="OFSfmt", head_data_offset="OFSfmt,
193 o->field.next_hash_offset,
194 o->field.head_data_offset);
200 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) {
202 "bad entry size (<= %zu): %"PRIu64,
203 offsetof(EntryObject, items),
204 le64toh(o->object.size));
208 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) {
210 "invalid number items in entry: %"PRIu64,
211 (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem));
215 if (le64toh(o->entry.seqnum) <= 0) {
217 "invalid entry seqnum: %"PRIx64,
218 le64toh(o->entry.seqnum));
222 if (!VALID_REALTIME(le64toh(o->entry.realtime))) {
224 "invalid entry realtime timestamp: %"PRIu64,
225 le64toh(o->entry.realtime));
229 if (!VALID_MONOTONIC(le64toh(o->entry.monotonic))) {
231 "invalid entry monotonic timestamp: %"PRIu64,
232 le64toh(o->entry.monotonic));
236 for (i = 0; i < journal_file_entry_n_items(o); i++) {
237 if (o->entry.items[i].object_offset == 0 ||
238 !VALID64(o->entry.items[i].object_offset)) {
240 "invalid entry item (%"PRIu64"/%"PRIu64" offset: "OFSfmt,
241 i, journal_file_entry_n_items(o),
242 o->entry.items[i].object_offset);
249 case OBJECT_DATA_HASH_TABLE:
250 case OBJECT_FIELD_HASH_TABLE:
251 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
252 (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) {
254 "invalid %s hash table size: %"PRIu64,
255 o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
256 le64toh(o->object.size));
260 for (i = 0; i < journal_file_hash_table_n_items(o); i++) {
261 if (o->hash_table.items[i].head_hash_offset != 0 &&
262 !VALID64(le64toh(o->hash_table.items[i].head_hash_offset))) {
264 "invalid %s hash table item (%"PRIu64"/%"PRIu64") head_hash_offset: "OFSfmt,
265 o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
266 i, journal_file_hash_table_n_items(o),
267 le64toh(o->hash_table.items[i].head_hash_offset));
270 if (o->hash_table.items[i].tail_hash_offset != 0 &&
271 !VALID64(le64toh(o->hash_table.items[i].tail_hash_offset))) {
273 "invalid %s hash table item (%"PRIu64"/%"PRIu64") tail_hash_offset: "OFSfmt,
274 o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
275 i, journal_file_hash_table_n_items(o),
276 le64toh(o->hash_table.items[i].tail_hash_offset));
280 if ((o->hash_table.items[i].head_hash_offset != 0) !=
281 (o->hash_table.items[i].tail_hash_offset != 0)) {
283 "invalid %s hash table item (%"PRIu64"/%"PRIu64"): head_hash_offset="OFSfmt" 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].head_hash_offset),
287 le64toh(o->hash_table.items[i].tail_hash_offset));
294 case OBJECT_ENTRY_ARRAY:
295 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
296 (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) {
298 "invalid object entry array size: %"PRIu64,
299 le64toh(o->object.size));
303 if (!VALID64(o->entry_array.next_entry_array_offset)) {
305 "invalid object entry array next_entry_array_offset: "OFSfmt,
306 o->entry_array.next_entry_array_offset);
310 for (i = 0; i < journal_file_entry_array_n_items(o); i++)
311 if (le64toh(o->entry_array.items[i]) != 0 &&
312 !VALID64(le64toh(o->entry_array.items[i]))) {
314 "invalid object entry array item (%"PRIu64"/%"PRIu64"): "OFSfmt,
315 i, journal_file_entry_array_n_items(o),
316 le64toh(o->entry_array.items[i]));
323 if (le64toh(o->object.size) != sizeof(TagObject)) {
325 "invalid object tag size: %"PRIu64,
326 le64toh(o->object.size));
330 if (!VALID_EPOCH(o->tag.epoch)) {
332 "invalid object tag epoch: %"PRIu64,
343 static int write_uint64(int fd, uint64_t p) {
346 k = write(fd, &p, sizeof(p));
355 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
370 r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z);
389 static int entry_points_to_data(
402 assert(entry_fd >= 0);
404 if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
406 "data object references invalid entry at "OFSfmt, entry_p);
410 r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
414 n = journal_file_entry_n_items(o);
415 for (i = 0; i < n; i++)
416 if (le64toh(o->entry.items[i].object_offset) == data_p) {
423 "data object at "OFSfmt" not referenced by linked entry", data_p);
427 /* Check if this entry is also in main entry array. Since the
428 * main entry array has already been verified we can rely on
429 * its consistency. */
432 n = le64toh(f->header->n_entries);
433 a = le64toh(f->header->entry_array_offset);
438 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
442 m = journal_file_entry_array_n_items(o);
445 if (entry_p <= le64toh(o->entry_array.items[u-1])) {
454 if (le64toh(o->entry_array.items[z]) == entry_p)
460 if (entry_p < le64toh(o->entry_array.items[z]))
466 error(entry_p, "entry object doesn't exist in main entry array");
471 a = le64toh(o->entry_array.next_entry_array_offset);
477 static int verify_data(
479 Object *o, uint64_t p,
480 int entry_fd, uint64_t n_entries,
481 int entry_array_fd, uint64_t n_entry_arrays) {
483 uint64_t i, n, a, last, q;
488 assert(entry_fd >= 0);
489 assert(entry_array_fd >= 0);
491 n = le64toh(o->data.n_entries);
492 a = le64toh(o->data.entry_array_offset);
494 /* Entry array means at least two objects */
497 "entry array present (entry_array_offset="OFSfmt", but n_entries=%"PRIu64")",
505 /* We already checked that earlier */
506 assert(o->data.entry_offset);
508 last = q = le64toh(o->data.entry_offset);
509 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
518 error(p, "array chain too short");
522 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
523 error(p, "invalid array offset "OFSfmt, a);
527 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
531 next = le64toh(o->entry_array.next_entry_array_offset);
532 if (next != 0 && next <= a) {
533 error(p, "array chain has cycle (jumps back from "OFSfmt" to "OFSfmt")",
538 m = journal_file_entry_array_n_items(o);
539 for (j = 0; i < n && j < m; i++, j++) {
541 q = le64toh(o->entry_array.items[j]);
543 error(p, "data object's entry array not sorted");
548 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
552 /* Pointer might have moved, reposition */
553 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
564 static int verify_hash_table(
566 int data_fd, uint64_t n_data,
567 int entry_fd, uint64_t n_entries,
568 int entry_array_fd, uint64_t n_entry_arrays,
570 bool show_progress) {
576 assert(data_fd >= 0);
577 assert(entry_fd >= 0);
578 assert(entry_array_fd >= 0);
581 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
582 for (i = 0; i < n; i++) {
583 uint64_t last = 0, p;
586 draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
588 p = le64toh(f->data_hash_table[i].head_hash_offset);
593 if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
594 error(p, "invalid data object at hash entry %"PRIu64" of %"PRIu64,
599 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
603 next = le64toh(o->data.next_hash_offset);
604 if (next != 0 && next <= p) {
605 error(p, "hash chain has a cycle in hash entry %"PRIu64" of %"PRIu64,
610 if (le64toh(o->data.hash) % n != i) {
611 error(p, "hash value mismatch in hash entry %"PRIu64" of %"PRIu64,
616 r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
624 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
625 error(p, "tail hash pointer mismatch in hash table");
633 static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
638 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
641 q = le64toh(f->data_hash_table[h].head_hash_offset);
648 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
652 q = le64toh(o->data.next_hash_offset);
658 static int verify_entry(
660 Object *o, uint64_t p,
661 int data_fd, uint64_t n_data) {
668 assert(data_fd >= 0);
670 n = journal_file_entry_n_items(o);
671 for (i = 0; i < n; i++) {
675 q = le64toh(o->entry.items[i].object_offset);
676 h = le64toh(o->entry.items[i].hash);
678 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
679 error(p, "invalid data object of entry");
683 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
687 if (le64toh(u->data.hash) != h) {
688 error(p, "hash mismatch for data object of entry");
692 r = data_object_in_hash_table(f, h, q);
696 error(p, "data object missing from hash table");
704 static int verify_entry_array(
706 int data_fd, uint64_t n_data,
707 int entry_fd, uint64_t n_entries,
708 int entry_array_fd, uint64_t n_entry_arrays,
710 bool show_progress) {
712 uint64_t i = 0, a, n, last = 0;
716 assert(data_fd >= 0);
717 assert(entry_fd >= 0);
718 assert(entry_array_fd >= 0);
721 n = le64toh(f->header->n_entries);
722 a = le64toh(f->header->entry_array_offset);
728 draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
731 error(a, "array chain too short at %"PRIu64" of %"PRIu64, i, n);
735 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
736 error(a, "invalid array %"PRIu64" of %"PRIu64, i, n);
740 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
744 next = le64toh(o->entry_array.next_entry_array_offset);
745 if (next != 0 && next <= a) {
747 "array chain has cycle at %"PRIu64" of %"PRIu64" (jumps back from to "OFSfmt")",
752 m = journal_file_entry_array_n_items(o);
753 for (j = 0; i < n && j < m; i++, j++) {
756 p = le64toh(o->entry_array.items[j]);
758 error(a, "entry array not sorted at %"PRIu64" of %"PRIu64,
764 if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
765 error(a, "invalid array entry at %"PRIu64" of %"PRIu64,
770 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
774 r = verify_entry(f, o, p, data_fd, n_data);
778 /* Pointer might have moved, reposition */
779 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
790 int journal_file_verify(
793 usec_t *first_contained, usec_t *last_validated, usec_t *last_contained,
794 bool show_progress) {
797 uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0;
799 uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
800 sd_id128_t entry_boot_id;
801 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
802 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;
803 usec_t last_usec = 0;
804 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
806 bool found_last = false;
808 uint64_t last_tag = 0;
814 r = journal_file_parse_verification_key(f, key);
816 log_error("Failed to parse seed.");
825 data_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
827 log_error_errno(errno, "Failed to create data file: %m");
832 entry_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
834 log_error_errno(errno, "Failed to create entry file: %m");
839 entry_array_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
840 if (entry_array_fd < 0) {
841 log_error_errno(errno, "Failed to create entry array file: %m");
846 if (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SUPPORTED) {
847 log_error("Cannot verify file with unknown extensions.");
852 for (i = 0; i < sizeof(f->header->reserved); i++)
853 if (f->header->reserved[i] != 0) {
854 error(offsetof(Header, reserved[i]), "reserved field is non-zero");
859 /* First iteration: we go through all objects, verify the
860 * superficial structure, headers, hashes. */
862 p = le64toh(f->header->header_size);
865 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
867 r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
869 error(p, "invalid object");
873 if (p > le64toh(f->header->tail_object_offset)) {
874 error(offsetof(Header, tail_object_offset), "invalid tail object pointer");
879 if (p == le64toh(f->header->tail_object_offset))
884 r = journal_file_object_verify(f, p, o);
886 error(p, "invalid object contents: %s", strerror(-r));
890 if ((o->object.flags & OBJECT_COMPRESSED_XZ) &&
891 (o->object.flags & OBJECT_COMPRESSED_LZ4)) {
892 error(p, "objected with double compression");
897 if ((o->object.flags & OBJECT_COMPRESSED_XZ) && !JOURNAL_HEADER_COMPRESSED_XZ(f->header)) {
898 error(p, "XZ compressed object in file without XZ compression");
903 if ((o->object.flags & OBJECT_COMPRESSED_LZ4) && !JOURNAL_HEADER_COMPRESSED_LZ4(f->header)) {
904 error(p, "LZ4 compressed object in file without LZ4 compression");
909 switch (o->object.type) {
912 r = write_uint64(data_fd, p);
924 if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) {
925 error(p, "first entry before first tag");
930 r = write_uint64(entry_fd, p);
934 if (le64toh(o->entry.realtime) < last_tag_realtime) {
935 error(p, "older entry after newer tag");
940 if (!entry_seqnum_set &&
941 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
942 error(p, "head entry sequence number incorrect");
947 if (entry_seqnum_set &&
948 entry_seqnum >= le64toh(o->entry.seqnum)) {
949 error(p, "entry sequence number out of synchronization");
954 entry_seqnum = le64toh(o->entry.seqnum);
955 entry_seqnum_set = true;
957 if (entry_monotonic_set &&
958 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
959 entry_monotonic > le64toh(o->entry.monotonic)) {
960 error(p, "entry timestamp out of synchronization");
965 entry_monotonic = le64toh(o->entry.monotonic);
966 entry_boot_id = o->entry.boot_id;
967 entry_monotonic_set = true;
969 if (!entry_realtime_set &&
970 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
971 error(p, "head entry realtime timestamp incorrect");
976 entry_realtime = le64toh(o->entry.realtime);
977 entry_realtime_set = true;
982 case OBJECT_DATA_HASH_TABLE:
983 if (n_data_hash_tables > 1) {
984 error(p, "more than one data hash table");
989 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
990 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
991 error(p, "header fields for data hash table invalid");
996 n_data_hash_tables++;
999 case OBJECT_FIELD_HASH_TABLE:
1000 if (n_field_hash_tables > 1) {
1001 error(p, "more than one field hash table");
1006 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
1007 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
1008 error(p, "header fields for field hash table invalid");
1013 n_field_hash_tables++;
1016 case OBJECT_ENTRY_ARRAY:
1017 r = write_uint64(entry_array_fd, p);
1021 if (p == le64toh(f->header->entry_array_offset)) {
1022 if (found_main_entry_array) {
1023 error(p, "more than one main entry array");
1028 found_main_entry_array = true;
1035 if (!JOURNAL_HEADER_SEALED(f->header)) {
1036 error(p, "tag object in file without sealing");
1041 if (le64toh(o->tag.seqnum) != n_tags + 1) {
1042 error(p, "tag sequence number out of synchronization");
1047 if (le64toh(o->tag.epoch) < last_epoch) {
1048 error(p, "epoch sequence out of synchronization");
1057 debug(p, "checking tag %"PRIu64"...", le64toh(o->tag.seqnum));
1059 rt = f->fss_start_usec + o->tag.epoch * f->fss_interval_usec;
1060 if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) {
1061 error(p, "tag/entry realtime timestamp out of synchronization");
1066 /* OK, now we know the epoch. So let's now set
1067 * it, and calculate the HMAC for everything
1068 * since the last tag. */
1069 r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
1073 r = journal_file_hmac_start(f);
1077 if (last_tag == 0) {
1078 r = journal_file_hmac_put_header(f);
1082 q = le64toh(f->header->header_size);
1087 r = journal_file_move_to_object(f, OBJECT_UNUSED, q, &o);
1091 r = journal_file_hmac_put_object(f, OBJECT_UNUSED, o, q);
1095 q = q + ALIGN64(le64toh(o->object.size));
1098 /* Position might have changed, let's reposition things */
1099 r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
1103 if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
1104 error(p, "tag failed verification");
1109 f->hmac_running = false;
1110 last_tag_realtime = rt;
1111 last_sealed_realtime = entry_realtime;
1114 last_tag = p + ALIGN64(le64toh(o->object.size));
1117 last_epoch = le64toh(o->tag.epoch);
1126 if (p == le64toh(f->header->tail_object_offset))
1129 p = p + ALIGN64(le64toh(o->object.size));
1133 error(le64toh(f->header->tail_object_offset), "tail object pointer dead");
1138 if (n_objects != le64toh(f->header->n_objects)) {
1139 error(offsetof(Header, n_objects), "object number mismatch");
1144 if (n_entries != le64toh(f->header->n_entries)) {
1145 error(offsetof(Header, n_entries), "entry number mismatch");
1150 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
1151 n_data != le64toh(f->header->n_data)) {
1152 error(offsetof(Header, n_data), "data number mismatch");
1157 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
1158 n_fields != le64toh(f->header->n_fields)) {
1159 error(offsetof(Header, n_fields), "field number mismatch");
1164 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
1165 n_tags != le64toh(f->header->n_tags)) {
1166 error(offsetof(Header, n_tags), "tag number mismatch");
1171 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
1172 n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
1173 error(offsetof(Header, n_entry_arrays), "entry array number mismatch");
1178 if (n_data_hash_tables != 1) {
1179 error(0, "missing data hash table");
1184 if (n_field_hash_tables != 1) {
1185 error(0, "missing field hash table");
1190 if (!found_main_entry_array) {
1191 error(0, "missing entry array");
1196 if (entry_seqnum_set &&
1197 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
1198 error(offsetof(Header, tail_entry_seqnum), "invalid tail seqnum");
1203 if (entry_monotonic_set &&
1204 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
1205 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
1206 error(0, "invalid tail monotonic timestamp");
1211 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1212 error(0, "invalid tail realtime timestamp");
1217 /* Second iteration: we follow all objects referenced from the
1218 * two entry points: the object hash table and the entry
1219 * array. We also check that everything referenced (directly
1220 * or indirectly) in the data hash table also exists in the
1221 * entry array, and vice versa. Note that we do not care for
1222 * unreferenced objects. We only care that everything that is
1223 * referenced is consistent. */
1225 r = verify_entry_array(f,
1227 entry_fd, n_entries,
1228 entry_array_fd, n_entry_arrays,
1234 r = verify_hash_table(f,
1236 entry_fd, n_entries,
1237 entry_array_fd, n_entry_arrays,
1246 mmap_cache_close_fd(f->mmap, data_fd);
1247 mmap_cache_close_fd(f->mmap, entry_fd);
1248 mmap_cache_close_fd(f->mmap, entry_array_fd);
1250 safe_close(data_fd);
1251 safe_close(entry_fd);
1252 safe_close(entry_array_fd);
1254 if (first_contained)
1255 *first_contained = le64toh(f->header->head_entry_realtime);
1257 *last_validated = last_sealed_realtime;
1259 *last_contained = le64toh(f->header->tail_entry_realtime);
1267 log_error("File corruption detected at %s:"OFSfmt" (of %llu bytes, %"PRIu64"%%).",
1270 (unsigned long long) f->last_stat.st_size,
1271 100 * p / f->last_stat.st_size);
1274 mmap_cache_close_fd(f->mmap, data_fd);
1275 safe_close(data_fd);
1278 if (entry_fd >= 0) {
1279 mmap_cache_close_fd(f->mmap, entry_fd);
1280 safe_close(entry_fd);
1283 if (entry_array_fd >= 0) {
1284 mmap_cache_close_fd(f->mmap, entry_array_fd);
1285 safe_close(entry_array_fd);