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"
35 #include "terminal-util.h"
37 static void draw_progress(uint64_t p
, usec_t
*last_usec
) {
44 z
= now(CLOCK_MONOTONIC
);
47 if (x
!= 0 && x
+ 40 * USEC_PER_MSEC
> z
)
52 n
= (3 * columns()) / 4;
53 j
= (n
* (unsigned) p
) / 65535ULL;
56 fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON
, stdout
);
58 for (i
= 0; i
< j
; i
++)
59 fputs("\xe2\x96\x88", stdout
);
61 fputs(ANSI_HIGHLIGHT_OFF
, stdout
);
63 for (i
= 0; i
< k
; i
++)
64 fputs("\xe2\x96\x91", stdout
);
66 printf(" %3"PRIu64
"%%", 100U * p
/ 65535U);
68 fputs("\r\x1B[?25h", stdout
);
72 static uint64_t scale_progress(uint64_t scale
, uint64_t p
, uint64_t m
) {
74 /* Calculates scale * p / m, but handles m == 0 safely, and saturates */
82 static void flush_progress(void) {
88 n
= (3 * columns()) / 4;
92 for (i
= 0; i
< n
+ 5; i
++)
99 #define debug(_offset, _fmt, ...) do{ \
101 log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
104 #define warning(_offset, _fmt, ...) do{ \
106 log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
109 #define error(_offset, _fmt, ...) do{ \
111 log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
114 static int journal_file_object_verify(JournalFile
*f
, uint64_t offset
, Object
*o
) {
121 /* This does various superficial tests about the length an
122 * possible field values. It does not follow any references to
125 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) &&
126 o
->object
.type
!= OBJECT_DATA
) {
127 error(offset
, "Found compressed object that isn't of type DATA, which is not allowed.");
131 switch (o
->object
.type
) {
137 if (le64toh(o
->data
.entry_offset
) == 0)
138 warning(offset
, "Unused data (entry_offset==0)");
140 if ((le64toh(o
->data
.entry_offset
) == 0) ^ (le64toh(o
->data
.n_entries
) == 0)) {
141 error(offset
, "Bad n_entries: %"PRIu64
, o
->data
.n_entries
);
145 if (le64toh(o
->object
.size
) - offsetof(DataObject
, payload
) <= 0) {
146 error(offset
, "Bad object size (<= %zu): %"PRIu64
,
147 offsetof(DataObject
, payload
),
148 le64toh(o
->object
.size
));
152 h1
= le64toh(o
->data
.hash
);
154 compression
= o
->object
.flags
& OBJECT_COMPRESSION_MASK
;
156 _cleanup_free_
void *b
= NULL
;
157 size_t alloc
= 0, b_size
;
159 r
= decompress_blob(compression
,
161 le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
),
162 &b
, &alloc
, &b_size
, 0);
164 error(offset
, "%s decompression failed: %s",
165 object_compressed_to_string(compression
), strerror(-r
));
169 h2
= hash64(b
, b_size
);
171 h2
= hash64(o
->data
.payload
, le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
));
174 error(offset
, "Invalid hash (%08"PRIx64
" vs. %08"PRIx64
, h1
, h2
);
178 if (!VALID64(o
->data
.next_hash_offset
) ||
179 !VALID64(o
->data
.next_field_offset
) ||
180 !VALID64(o
->data
.entry_offset
) ||
181 !VALID64(o
->data
.entry_array_offset
)) {
182 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
);
194 if (le64toh(o
->object
.size
) - offsetof(FieldObject
, payload
) <= 0) {
196 "Bad field size (<= %zu): %"PRIu64
,
197 offsetof(FieldObject
, payload
),
198 le64toh(o
->object
.size
));
202 if (!VALID64(o
->field
.next_hash_offset
) ||
203 !VALID64(o
->field
.head_data_offset
)) {
205 "Invalid offset (next_hash_offset="OFSfmt
", head_data_offset="OFSfmt
,
206 o
->field
.next_hash_offset
,
207 o
->field
.head_data_offset
);
213 if ((le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) % sizeof(EntryItem
) != 0) {
215 "Bad entry size (<= %zu): %"PRIu64
,
216 offsetof(EntryObject
, items
),
217 le64toh(o
->object
.size
));
221 if ((le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) / sizeof(EntryItem
) <= 0) {
223 "Invalid number items in entry: %"PRIu64
,
224 (le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) / sizeof(EntryItem
));
228 if (le64toh(o
->entry
.seqnum
) <= 0) {
230 "Invalid entry seqnum: %"PRIx64
,
231 le64toh(o
->entry
.seqnum
));
235 if (!VALID_REALTIME(le64toh(o
->entry
.realtime
))) {
237 "Invalid entry realtime timestamp: %"PRIu64
,
238 le64toh(o
->entry
.realtime
));
242 if (!VALID_MONOTONIC(le64toh(o
->entry
.monotonic
))) {
244 "Invalid entry monotonic timestamp: %"PRIu64
,
245 le64toh(o
->entry
.monotonic
));
249 for (i
= 0; i
< journal_file_entry_n_items(o
); i
++) {
250 if (o
->entry
.items
[i
].object_offset
== 0 ||
251 !VALID64(o
->entry
.items
[i
].object_offset
)) {
253 "Invalid entry item (%"PRIu64
"/%"PRIu64
" offset: "OFSfmt
,
254 i
, journal_file_entry_n_items(o
),
255 o
->entry
.items
[i
].object_offset
);
262 case OBJECT_DATA_HASH_TABLE
:
263 case OBJECT_FIELD_HASH_TABLE
:
264 if ((le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) % sizeof(HashItem
) != 0 ||
265 (le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) / sizeof(HashItem
) <= 0) {
267 "Invalid %s hash table size: %"PRIu64
,
268 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
269 le64toh(o
->object
.size
));
273 for (i
= 0; i
< journal_file_hash_table_n_items(o
); i
++) {
274 if (o
->hash_table
.items
[i
].head_hash_offset
!= 0 &&
275 !VALID64(le64toh(o
->hash_table
.items
[i
].head_hash_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
));
283 if (o
->hash_table
.items
[i
].tail_hash_offset
!= 0 &&
284 !VALID64(le64toh(o
->hash_table
.items
[i
].tail_hash_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
));
293 if ((o
->hash_table
.items
[i
].head_hash_offset
!= 0) !=
294 (o
->hash_table
.items
[i
].tail_hash_offset
!= 0)) {
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
));
307 case OBJECT_ENTRY_ARRAY
:
308 if ((le64toh(o
->object
.size
) - offsetof(EntryArrayObject
, items
)) % sizeof(le64_t
) != 0 ||
309 (le64toh(o
->object
.size
) - offsetof(EntryArrayObject
, items
)) / sizeof(le64_t
) <= 0) {
311 "Invalid object entry array size: %"PRIu64
,
312 le64toh(o
->object
.size
));
316 if (!VALID64(o
->entry_array
.next_entry_array_offset
)) {
318 "Invalid object entry array next_entry_array_offset: "OFSfmt
,
319 o
->entry_array
.next_entry_array_offset
);
323 for (i
= 0; i
< journal_file_entry_array_n_items(o
); i
++)
324 if (le64toh(o
->entry_array
.items
[i
]) != 0 &&
325 !VALID64(le64toh(o
->entry_array
.items
[i
]))) {
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
]));
336 if (le64toh(o
->object
.size
) != sizeof(TagObject
)) {
338 "Invalid object tag size: %"PRIu64
,
339 le64toh(o
->object
.size
));
343 if (!VALID_EPOCH(o
->tag
.epoch
)) {
345 "Invalid object tag epoch: %"PRIu64
,
356 static int write_uint64(int fd
, uint64_t p
) {
359 k
= write(fd
, &p
, sizeof(p
));
368 static int contains_uint64(MMapCache
*m
, int fd
, uint64_t n
, uint64_t p
) {
383 r
= mmap_cache_get(m
, fd
, PROT_READ
|PROT_WRITE
, 0, false, c
* sizeof(uint64_t), sizeof(uint64_t), NULL
, (void **) &z
);
402 static int entry_points_to_data(
415 assert(entry_fd
>= 0);
417 if (!contains_uint64(f
->mmap
, entry_fd
, n_entries
, entry_p
)) {
418 error(data_p
, "Data object references invalid entry at "OFSfmt
, entry_p
);
422 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, entry_p
, &o
);
426 n
= journal_file_entry_n_items(o
);
427 for (i
= 0; i
< n
; i
++)
428 if (le64toh(o
->entry
.items
[i
].object_offset
) == data_p
) {
434 error(entry_p
, "Data object at "OFSfmt
" not referenced by linked entry", data_p
);
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. */
443 n
= le64toh(f
->header
->n_entries
);
444 a
= le64toh(f
->header
->entry_array_offset
);
449 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
453 m
= journal_file_entry_array_n_items(o
);
456 if (entry_p
<= le64toh(o
->entry_array
.items
[u
-1])) {
465 if (le64toh(o
->entry_array
.items
[z
]) == entry_p
)
471 if (entry_p
< le64toh(o
->entry_array
.items
[z
]))
477 error(entry_p
, "Entry object doesn't exist in main entry array");
482 a
= le64toh(o
->entry_array
.next_entry_array_offset
);
488 static int verify_data(
490 Object
*o
, uint64_t p
,
491 int entry_fd
, uint64_t n_entries
,
492 int entry_array_fd
, uint64_t n_entry_arrays
) {
494 uint64_t i
, n
, a
, last
, q
;
499 assert(entry_fd
>= 0);
500 assert(entry_array_fd
>= 0);
502 n
= le64toh(o
->data
.n_entries
);
503 a
= le64toh(o
->data
.entry_array_offset
);
505 /* Entry array means at least two objects */
507 error(p
, "Entry array present (entry_array_offset="OFSfmt
", but n_entries=%"PRIu64
")", a
, n
);
514 /* We already checked that earlier */
515 assert(o
->data
.entry_offset
);
517 last
= q
= le64toh(o
->data
.entry_offset
);
518 r
= entry_points_to_data(f
, entry_fd
, n_entries
, q
, p
);
527 error(p
, "Array chain too short");
531 if (!contains_uint64(f
->mmap
, entry_array_fd
, n_entry_arrays
, a
)) {
532 error(p
, "Invalid array offset "OFSfmt
, a
);
536 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
540 next
= le64toh(o
->entry_array
.next_entry_array_offset
);
541 if (next
!= 0 && next
<= a
) {
542 error(p
, "Array chain has cycle (jumps back from "OFSfmt
" to "OFSfmt
")", a
, next
);
546 m
= journal_file_entry_array_n_items(o
);
547 for (j
= 0; i
< n
&& j
< m
; i
++, j
++) {
549 q
= le64toh(o
->entry_array
.items
[j
]);
551 error(p
, "Data object's entry array not sorted");
556 r
= entry_points_to_data(f
, entry_fd
, n_entries
, q
, p
);
560 /* Pointer might have moved, reposition */
561 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
572 static int verify_hash_table(
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
,
578 bool show_progress
) {
584 assert(data_fd
>= 0);
585 assert(entry_fd
>= 0);
586 assert(entry_array_fd
>= 0);
589 n
= le64toh(f
->header
->data_hash_table_size
) / sizeof(HashItem
);
593 r
= journal_file_map_data_hash_table(f
);
595 return log_error_errno(r
, "Failed to map data hash table: %m");
597 for (i
= 0; i
< n
; i
++) {
598 uint64_t last
= 0, p
;
601 draw_progress(0xC000 + scale_progress(0x3FFF, i
, n
), last_usec
);
603 p
= le64toh(f
->data_hash_table
[i
].head_hash_offset
);
608 if (!contains_uint64(f
->mmap
, data_fd
, n_data
, p
)) {
609 error(p
, "Invalid data object at hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
613 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
617 next
= le64toh(o
->data
.next_hash_offset
);
618 if (next
!= 0 && next
<= p
) {
619 error(p
, "Hash chain has a cycle in hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
623 if (le64toh(o
->data
.hash
) % n
!= i
) {
624 error(p
, "Hash value mismatch in hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
628 r
= verify_data(f
, o
, p
, entry_fd
, n_entries
, entry_array_fd
, n_entry_arrays
);
636 if (last
!= le64toh(f
->data_hash_table
[i
].tail_hash_offset
)) {
637 error(p
, "Tail hash pointer mismatch in hash table");
645 static int data_object_in_hash_table(JournalFile
*f
, uint64_t hash
, uint64_t p
) {
650 n
= le64toh(f
->header
->data_hash_table_size
) / sizeof(HashItem
);
654 r
= journal_file_map_data_hash_table(f
);
656 return log_error_errno(r
, "Failed to map data hash table: %m");
660 q
= le64toh(f
->data_hash_table
[h
].head_hash_offset
);
667 r
= journal_file_move_to_object(f
, OBJECT_DATA
, q
, &o
);
671 q
= le64toh(o
->data
.next_hash_offset
);
677 static int verify_entry(
679 Object
*o
, uint64_t p
,
680 int data_fd
, uint64_t n_data
) {
687 assert(data_fd
>= 0);
689 n
= journal_file_entry_n_items(o
);
690 for (i
= 0; i
< n
; i
++) {
694 q
= le64toh(o
->entry
.items
[i
].object_offset
);
695 h
= le64toh(o
->entry
.items
[i
].hash
);
697 if (!contains_uint64(f
->mmap
, data_fd
, n_data
, q
)) {
698 error(p
, "Invalid data object of entry");
702 r
= journal_file_move_to_object(f
, OBJECT_DATA
, q
, &u
);
706 if (le64toh(u
->data
.hash
) != h
) {
707 error(p
, "Hash mismatch for data object of entry");
711 r
= data_object_in_hash_table(f
, h
, q
);
715 error(p
, "Data object missing from hash table");
723 static int verify_entry_array(
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
,
729 bool show_progress
) {
731 uint64_t i
= 0, a
, n
, last
= 0;
735 assert(data_fd
>= 0);
736 assert(entry_fd
>= 0);
737 assert(entry_array_fd
>= 0);
740 n
= le64toh(f
->header
->n_entries
);
741 a
= le64toh(f
->header
->entry_array_offset
);
747 draw_progress(0x8000 + scale_progress(0x3FFF, i
, n
), last_usec
);
750 error(a
, "Array chain too short at %"PRIu64
" of %"PRIu64
, i
, n
);
754 if (!contains_uint64(f
->mmap
, entry_array_fd
, n_entry_arrays
, a
)) {
755 error(a
, "Invalid array %"PRIu64
" of %"PRIu64
, i
, n
);
759 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
763 next
= le64toh(o
->entry_array
.next_entry_array_offset
);
764 if (next
!= 0 && next
<= a
) {
765 error(a
, "Array chain has cycle at %"PRIu64
" of %"PRIu64
" (jumps back from to "OFSfmt
")", i
, n
, next
);
769 m
= journal_file_entry_array_n_items(o
);
770 for (j
= 0; i
< n
&& j
< m
; i
++, j
++) {
773 p
= le64toh(o
->entry_array
.items
[j
]);
775 error(a
, "Entry array not sorted at %"PRIu64
" of %"PRIu64
, i
, n
);
780 if (!contains_uint64(f
->mmap
, entry_fd
, n_entries
, p
)) {
781 error(a
, "Invalid array entry at %"PRIu64
" of %"PRIu64
, i
, n
);
785 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, p
, &o
);
789 r
= verify_entry(f
, o
, p
, data_fd
, n_data
);
793 /* Pointer might have moved, reposition */
794 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
805 int journal_file_verify(
808 usec_t
*first_contained
, usec_t
*last_validated
, usec_t
*last_contained
,
809 bool show_progress
) {
812 uint64_t p
= 0, last_epoch
= 0, last_tag_realtime
= 0, last_sealed_realtime
= 0;
814 uint64_t entry_seqnum
= 0, entry_monotonic
= 0, entry_realtime
= 0;
815 sd_id128_t entry_boot_id
;
816 bool entry_seqnum_set
= false, entry_monotonic_set
= false, entry_realtime_set
= false, found_main_entry_array
= false;
817 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 usec_t last_usec
= 0;
819 int data_fd
= -1, entry_fd
= -1, entry_array_fd
= -1;
821 bool found_last
= false;
823 uint64_t last_tag
= 0;
829 r
= journal_file_parse_verification_key(f
, key
);
831 log_error("Failed to parse seed.");
840 data_fd
= open_tmpfile("/var/tmp", O_RDWR
| O_CLOEXEC
);
842 log_error_errno(errno
, "Failed to create data file: %m");
847 entry_fd
= open_tmpfile("/var/tmp", O_RDWR
| O_CLOEXEC
);
849 log_error_errno(errno
, "Failed to create entry file: %m");
854 entry_array_fd
= open_tmpfile("/var/tmp", O_RDWR
| O_CLOEXEC
);
855 if (entry_array_fd
< 0) {
856 log_error_errno(errno
, "Failed to create entry array file: %m");
861 if (le32toh(f
->header
->compatible_flags
) & ~HEADER_COMPATIBLE_SUPPORTED
) {
862 log_error("Cannot verify file with unknown extensions.");
867 for (i
= 0; i
< sizeof(f
->header
->reserved
); i
++)
868 if (f
->header
->reserved
[i
] != 0) {
869 error(offsetof(Header
, reserved
[i
]), "Reserved field is non-zero");
874 /* First iteration: we go through all objects, verify the
875 * superficial structure, headers, hashes. */
877 p
= le64toh(f
->header
->header_size
);
879 /* Early exit if there are no objects in the file, at all */
880 if (le64toh(f
->header
->tail_object_offset
) == 0)
884 draw_progress(scale_progress(0x7FFF, p
, le64toh(f
->header
->tail_object_offset
)), &last_usec
);
886 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, p
, &o
);
888 error(p
, "Invalid object");
892 if (p
> le64toh(f
->header
->tail_object_offset
)) {
893 error(offsetof(Header
, tail_object_offset
), "Invalid tail object pointer");
900 r
= journal_file_object_verify(f
, p
, o
);
902 error(p
, "Envalid object contents: %s", strerror(-r
));
906 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) &&
907 (o
->object
.flags
& OBJECT_COMPRESSED_LZ4
)) {
908 error(p
, "Objected with double compression");
913 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) && !JOURNAL_HEADER_COMPRESSED_XZ(f
->header
)) {
914 error(p
, "XZ compressed object in file without XZ compression");
919 if ((o
->object
.flags
& OBJECT_COMPRESSED_LZ4
) && !JOURNAL_HEADER_COMPRESSED_LZ4(f
->header
)) {
920 error(p
, "LZ4 compressed object in file without LZ4 compression");
925 switch (o
->object
.type
) {
928 r
= write_uint64(data_fd
, p
);
940 if (JOURNAL_HEADER_SEALED(f
->header
) && n_tags
<= 0) {
941 error(p
, "First entry before first tag");
946 r
= write_uint64(entry_fd
, p
);
950 if (le64toh(o
->entry
.realtime
) < last_tag_realtime
) {
951 error(p
, "Older entry after newer tag");
956 if (!entry_seqnum_set
&&
957 le64toh(o
->entry
.seqnum
) != le64toh(f
->header
->head_entry_seqnum
)) {
958 error(p
, "Head entry sequence number incorrect");
963 if (entry_seqnum_set
&&
964 entry_seqnum
>= le64toh(o
->entry
.seqnum
)) {
965 error(p
, "Entry sequence number out of synchronization");
970 entry_seqnum
= le64toh(o
->entry
.seqnum
);
971 entry_seqnum_set
= true;
973 if (entry_monotonic_set
&&
974 sd_id128_equal(entry_boot_id
, o
->entry
.boot_id
) &&
975 entry_monotonic
> le64toh(o
->entry
.monotonic
)) {
976 error(p
, "Entry timestamp out of synchronization");
981 entry_monotonic
= le64toh(o
->entry
.monotonic
);
982 entry_boot_id
= o
->entry
.boot_id
;
983 entry_monotonic_set
= true;
985 if (!entry_realtime_set
&&
986 le64toh(o
->entry
.realtime
) != le64toh(f
->header
->head_entry_realtime
)) {
987 error(p
, "Head entry realtime timestamp incorrect");
992 entry_realtime
= le64toh(o
->entry
.realtime
);
993 entry_realtime_set
= true;
998 case OBJECT_DATA_HASH_TABLE
:
999 if (n_data_hash_tables
> 1) {
1000 error(p
, "More than one data hash table");
1005 if (le64toh(f
->header
->data_hash_table_offset
) != p
+ offsetof(HashTableObject
, items
) ||
1006 le64toh(f
->header
->data_hash_table_size
) != le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) {
1007 error(p
, "header fields for data hash table invalid");
1012 n_data_hash_tables
++;
1015 case OBJECT_FIELD_HASH_TABLE
:
1016 if (n_field_hash_tables
> 1) {
1017 error(p
, "More than one field hash table");
1022 if (le64toh(f
->header
->field_hash_table_offset
) != p
+ offsetof(HashTableObject
, items
) ||
1023 le64toh(f
->header
->field_hash_table_size
) != le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) {
1024 error(p
, "Header fields for field hash table invalid");
1029 n_field_hash_tables
++;
1032 case OBJECT_ENTRY_ARRAY
:
1033 r
= write_uint64(entry_array_fd
, p
);
1037 if (p
== le64toh(f
->header
->entry_array_offset
)) {
1038 if (found_main_entry_array
) {
1039 error(p
, "More than one main entry array");
1044 found_main_entry_array
= true;
1051 if (!JOURNAL_HEADER_SEALED(f
->header
)) {
1052 error(p
, "Tag object in file without sealing");
1057 if (le64toh(o
->tag
.seqnum
) != n_tags
+ 1) {
1058 error(p
, "Tag sequence number out of synchronization");
1063 if (le64toh(o
->tag
.epoch
) < last_epoch
) {
1064 error(p
, "Epoch sequence out of synchronization");
1073 debug(p
, "Checking tag %"PRIu64
"...", le64toh(o
->tag
.seqnum
));
1075 rt
= f
->fss_start_usec
+ o
->tag
.epoch
* f
->fss_interval_usec
;
1076 if (entry_realtime_set
&& entry_realtime
>= rt
+ f
->fss_interval_usec
) {
1077 error(p
, "tag/entry realtime timestamp out of synchronization");
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 r
= journal_file_fsprg_seek(f
, le64toh(o
->tag
.epoch
));
1089 r
= journal_file_hmac_start(f
);
1093 if (last_tag
== 0) {
1094 r
= journal_file_hmac_put_header(f
);
1098 q
= le64toh(f
->header
->header_size
);
1103 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, q
, &o
);
1107 r
= journal_file_hmac_put_object(f
, OBJECT_UNUSED
, o
, q
);
1111 q
= q
+ ALIGN64(le64toh(o
->object
.size
));
1114 /* Position might have changed, let's reposition things */
1115 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, p
, &o
);
1119 if (memcmp(o
->tag
.tag
, gcry_md_read(f
->hmac
, 0), TAG_LENGTH
) != 0) {
1120 error(p
, "Tag failed verification");
1125 f
->hmac_running
= false;
1126 last_tag_realtime
= rt
;
1127 last_sealed_realtime
= entry_realtime
;
1130 last_tag
= p
+ ALIGN64(le64toh(o
->object
.size
));
1133 last_epoch
= le64toh(o
->tag
.epoch
);
1142 if (p
== le64toh(f
->header
->tail_object_offset
)) {
1147 p
= p
+ ALIGN64(le64toh(o
->object
.size
));
1150 if (!found_last
&& le64toh(f
->header
->tail_object_offset
) != 0) {
1151 error(le64toh(f
->header
->tail_object_offset
), "Tail object pointer dead");
1156 if (n_objects
!= le64toh(f
->header
->n_objects
)) {
1157 error(offsetof(Header
, n_objects
), "Object number mismatch");
1162 if (n_entries
!= le64toh(f
->header
->n_entries
)) {
1163 error(offsetof(Header
, n_entries
), "Entry number mismatch");
1168 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_data
) &&
1169 n_data
!= le64toh(f
->header
->n_data
)) {
1170 error(offsetof(Header
, n_data
), "Data number mismatch");
1175 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_fields
) &&
1176 n_fields
!= le64toh(f
->header
->n_fields
)) {
1177 error(offsetof(Header
, n_fields
), "Field number mismatch");
1182 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_tags
) &&
1183 n_tags
!= le64toh(f
->header
->n_tags
)) {
1184 error(offsetof(Header
, n_tags
), "Tag number mismatch");
1189 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_entry_arrays
) &&
1190 n_entry_arrays
!= le64toh(f
->header
->n_entry_arrays
)) {
1191 error(offsetof(Header
, n_entry_arrays
), "Entry array number mismatch");
1196 if (!found_main_entry_array
&& le64toh(f
->header
->entry_array_offset
) != 0) {
1197 error(0, "Missing entry array");
1202 if (entry_seqnum_set
&&
1203 entry_seqnum
!= le64toh(f
->header
->tail_entry_seqnum
)) {
1204 error(offsetof(Header
, tail_entry_seqnum
), "Invalid tail seqnum");
1209 if (entry_monotonic_set
&&
1210 (!sd_id128_equal(entry_boot_id
, f
->header
->boot_id
) ||
1211 entry_monotonic
!= le64toh(f
->header
->tail_entry_monotonic
))) {
1212 error(0, "Invalid tail monotonic timestamp");
1217 if (entry_realtime_set
&& entry_realtime
!= le64toh(f
->header
->tail_entry_realtime
)) {
1218 error(0, "Invalid tail realtime timestamp");
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. */
1231 r
= verify_entry_array(f
,
1233 entry_fd
, n_entries
,
1234 entry_array_fd
, n_entry_arrays
,
1240 r
= verify_hash_table(f
,
1242 entry_fd
, n_entries
,
1243 entry_array_fd
, n_entry_arrays
,
1252 mmap_cache_close_fd(f
->mmap
, data_fd
);
1253 mmap_cache_close_fd(f
->mmap
, entry_fd
);
1254 mmap_cache_close_fd(f
->mmap
, entry_array_fd
);
1256 safe_close(data_fd
);
1257 safe_close(entry_fd
);
1258 safe_close(entry_array_fd
);
1260 if (first_contained
)
1261 *first_contained
= le64toh(f
->header
->head_entry_realtime
);
1263 *last_validated
= last_sealed_realtime
;
1265 *last_contained
= le64toh(f
->header
->tail_entry_realtime
);
1273 log_error("File corruption detected at %s:"OFSfmt
" (of %llu bytes, %"PRIu64
"%%).",
1276 (unsigned long long) f
->last_stat
.st_size
,
1277 100 * p
/ f
->last_stat
.st_size
);
1280 mmap_cache_close_fd(f
->mmap
, data_fd
);
1281 safe_close(data_fd
);
1284 if (entry_fd
>= 0) {
1285 mmap_cache_close_fd(f
->mmap
, entry_fd
);
1286 safe_close(entry_fd
);
1289 if (entry_array_fd
>= 0) {
1290 mmap_cache_close_fd(f
->mmap
, entry_array_fd
);
1291 safe_close(entry_array_fd
);