1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "alloc-util.h"
13 #include "journal-authenticate.h"
14 #include "journal-def.h"
15 #include "journal-file.h"
16 #include "journal-verify.h"
19 #include "terminal-util.h"
20 #include "tmpfile-util.h"
23 static void draw_progress(uint64_t p
, usec_t
*last_usec
) {
30 z
= now(CLOCK_MONOTONIC
);
33 if (x
!= 0 && x
+ 40 * USEC_PER_MSEC
> z
)
38 n
= (3 * columns()) / 4;
39 j
= (n
* (unsigned) p
) / 65535ULL;
44 fputs("\x1B[?25l", stdout
);
46 fputs(ansi_highlight_green(), stdout
);
48 for (i
= 0; i
< j
; i
++)
49 fputs("\xe2\x96\x88", stdout
);
51 fputs(ansi_normal(), stdout
);
53 for (i
= 0; i
< k
; i
++)
54 fputs("\xe2\x96\x91", stdout
);
56 printf(" %3"PRIu64
"%%", 100U * p
/ 65535U);
60 fputs("\x1B[?25h", stdout
);
65 static uint64_t scale_progress(uint64_t scale
, uint64_t p
, uint64_t m
) {
66 /* Calculates scale * p / m, but handles m == 0 safely, and saturates.
67 * Currently all callers use m >= 1, but we keep the check to be defensive.
70 if (p
>= m
|| m
== 0) // lgtm[cpp/constant-comparison]
76 static void flush_progress(void) {
82 n
= (3 * columns()) / 4;
86 for (i
= 0; i
< n
+ 5; i
++)
93 #define debug(_offset, _fmt, ...) do { \
95 log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
98 #define warning(_offset, _fmt, ...) do { \
100 log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
103 #define error(_offset, _fmt, ...) do { \
105 log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
108 #define error_errno(_offset, error, _fmt, ...) do { \
110 log_error_errno(error, OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
113 static int hash_payload(JournalFile
*f
, Object
*o
, uint64_t offset
, const uint8_t *src
, uint64_t size
, uint64_t *res_hash
) {
120 compression
= o
->object
.flags
& OBJECT_COMPRESSION_MASK
;
122 _cleanup_free_
void *b
= NULL
;
125 r
= decompress_blob(compression
, src
, size
, &b
, &b_size
, 0);
127 error_errno(offset
, r
, "%s decompression failed: %m",
128 object_compressed_to_string(compression
));
132 *res_hash
= journal_file_hash_data(f
, b
, b_size
);
134 *res_hash
= journal_file_hash_data(f
, src
, size
);
139 static int journal_file_object_verify(JournalFile
*f
, uint64_t offset
, Object
*o
) {
144 /* This does various superficial tests about the length an
145 * possible field values. It does not follow any references to
148 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) &&
149 o
->object
.type
!= OBJECT_DATA
) {
151 "Found compressed object of type %s that isn't of type data, which is not allowed.",
152 journal_object_type_to_string(o
->object
.type
));
156 switch (o
->object
.type
) {
162 if (le64toh(o
->data
.entry_offset
) == 0)
163 warning(offset
, "Unused data (entry_offset==0)");
165 if ((le64toh(o
->data
.entry_offset
) == 0) ^ (le64toh(o
->data
.n_entries
) == 0)) {
166 error(offset
, "Bad n_entries: %"PRIu64
, le64toh(o
->data
.n_entries
));
170 if (le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
) <= 0) {
171 error(offset
, "Bad object size (<= %zu): %"PRIu64
,
172 offsetof(Object
, data
.payload
),
173 le64toh(o
->object
.size
));
177 h1
= le64toh(o
->data
.hash
);
178 r
= hash_payload(f
, o
, offset
, o
->data
.payload
,
179 le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
),
185 error(offset
, "Invalid hash (%08" PRIx64
" vs. %08" PRIx64
")", h1
, h2
);
189 if (!VALID64(le64toh(o
->data
.next_hash_offset
)) ||
190 !VALID64(le64toh(o
->data
.next_field_offset
)) ||
191 !VALID64(le64toh(o
->data
.entry_offset
)) ||
192 !VALID64(le64toh(o
->data
.entry_array_offset
))) {
193 error(offset
, "Invalid offset (next_hash_offset="OFSfmt
", next_field_offset="OFSfmt
", entry_offset="OFSfmt
", entry_array_offset="OFSfmt
,
194 le64toh(o
->data
.next_hash_offset
),
195 le64toh(o
->data
.next_field_offset
),
196 le64toh(o
->data
.entry_offset
),
197 le64toh(o
->data
.entry_array_offset
));
208 if (le64toh(o
->object
.size
) - offsetof(Object
, field
.payload
) <= 0) {
210 "Bad field size (<= %zu): %"PRIu64
,
211 offsetof(Object
, field
.payload
),
212 le64toh(o
->object
.size
));
216 h1
= le64toh(o
->field
.hash
);
217 r
= hash_payload(f
, o
, offset
, o
->field
.payload
,
218 le64toh(o
->object
.size
) - offsetof(Object
, field
.payload
),
224 error(offset
, "Invalid hash (%08" PRIx64
" vs. %08" PRIx64
")", h1
, h2
);
228 if (!VALID64(le64toh(o
->field
.next_hash_offset
)) ||
229 !VALID64(le64toh(o
->field
.head_data_offset
))) {
231 "Invalid offset (next_hash_offset="OFSfmt
", head_data_offset="OFSfmt
,
232 le64toh(o
->field
.next_hash_offset
),
233 le64toh(o
->field
.head_data_offset
));
240 if ((le64toh(o
->object
.size
) - offsetof(Object
, entry
.items
)) % sizeof(EntryItem
) != 0) {
242 "Bad entry size (<= %zu): %"PRIu64
,
243 offsetof(Object
, entry
.items
),
244 le64toh(o
->object
.size
));
248 if ((le64toh(o
->object
.size
) - offsetof(Object
, entry
.items
)) / sizeof(EntryItem
) <= 0) {
250 "Invalid number items in entry: %"PRIu64
,
251 (le64toh(o
->object
.size
) - offsetof(Object
, entry
.items
)) / sizeof(EntryItem
));
255 if (le64toh(o
->entry
.seqnum
) <= 0) {
257 "Invalid entry seqnum: %"PRIx64
,
258 le64toh(o
->entry
.seqnum
));
262 if (!VALID_REALTIME(le64toh(o
->entry
.realtime
))) {
264 "Invalid entry realtime timestamp: %"PRIu64
,
265 le64toh(o
->entry
.realtime
));
269 if (!VALID_MONOTONIC(le64toh(o
->entry
.monotonic
))) {
271 "Invalid entry monotonic timestamp: %"PRIu64
,
272 le64toh(o
->entry
.monotonic
));
276 for (uint64_t i
= 0; i
< journal_file_entry_n_items(o
); i
++) {
277 if (le64toh(o
->entry
.items
[i
].object_offset
) == 0 ||
278 !VALID64(le64toh(o
->entry
.items
[i
].object_offset
))) {
280 "Invalid entry item (%"PRIu64
"/%"PRIu64
" offset: "OFSfmt
,
281 i
, journal_file_entry_n_items(o
),
282 le64toh(o
->entry
.items
[i
].object_offset
));
289 case OBJECT_DATA_HASH_TABLE
:
290 case OBJECT_FIELD_HASH_TABLE
:
291 if ((le64toh(o
->object
.size
) - offsetof(Object
, hash_table
.items
)) % sizeof(HashItem
) != 0 ||
292 (le64toh(o
->object
.size
) - offsetof(Object
, hash_table
.items
)) / sizeof(HashItem
) <= 0) {
294 "Invalid %s size: %"PRIu64
,
295 journal_object_type_to_string(o
->object
.type
),
296 le64toh(o
->object
.size
));
300 for (uint64_t i
= 0; i
< journal_file_hash_table_n_items(o
); i
++) {
301 if (o
->hash_table
.items
[i
].head_hash_offset
!= 0 &&
302 !VALID64(le64toh(o
->hash_table
.items
[i
].head_hash_offset
))) {
304 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
") head_hash_offset: "OFSfmt
,
305 journal_object_type_to_string(o
->object
.type
),
306 i
, journal_file_hash_table_n_items(o
),
307 le64toh(o
->hash_table
.items
[i
].head_hash_offset
));
310 if (o
->hash_table
.items
[i
].tail_hash_offset
!= 0 &&
311 !VALID64(le64toh(o
->hash_table
.items
[i
].tail_hash_offset
))) {
313 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
") tail_hash_offset: "OFSfmt
,
314 journal_object_type_to_string(o
->object
.type
),
315 i
, journal_file_hash_table_n_items(o
),
316 le64toh(o
->hash_table
.items
[i
].tail_hash_offset
));
320 if ((o
->hash_table
.items
[i
].head_hash_offset
!= 0) !=
321 (o
->hash_table
.items
[i
].tail_hash_offset
!= 0)) {
323 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
"): head_hash_offset="OFSfmt
" tail_hash_offset="OFSfmt
,
324 journal_object_type_to_string(o
->object
.type
),
325 i
, journal_file_hash_table_n_items(o
),
326 le64toh(o
->hash_table
.items
[i
].head_hash_offset
),
327 le64toh(o
->hash_table
.items
[i
].tail_hash_offset
));
334 case OBJECT_ENTRY_ARRAY
:
335 if ((le64toh(o
->object
.size
) - offsetof(Object
, entry_array
.items
)) % sizeof(le64_t
) != 0 ||
336 (le64toh(o
->object
.size
) - offsetof(Object
, entry_array
.items
)) / sizeof(le64_t
) <= 0) {
338 "Invalid object entry array size: %"PRIu64
,
339 le64toh(o
->object
.size
));
343 if (!VALID64(le64toh(o
->entry_array
.next_entry_array_offset
))) {
345 "Invalid object entry array next_entry_array_offset: "OFSfmt
,
346 le64toh(o
->entry_array
.next_entry_array_offset
));
350 for (uint64_t i
= 0; i
< journal_file_entry_array_n_items(o
); i
++)
351 if (le64toh(o
->entry_array
.items
[i
]) != 0 &&
352 !VALID64(le64toh(o
->entry_array
.items
[i
]))) {
354 "Invalid object entry array item (%"PRIu64
"/%"PRIu64
"): "OFSfmt
,
355 i
, journal_file_entry_array_n_items(o
),
356 le64toh(o
->entry_array
.items
[i
]));
363 if (le64toh(o
->object
.size
) != sizeof(TagObject
)) {
365 "Invalid object tag size: %"PRIu64
,
366 le64toh(o
->object
.size
));
370 if (!VALID_EPOCH(le64toh(o
->tag
.epoch
))) {
372 "Invalid object tag epoch: %"PRIu64
,
373 le64toh(o
->tag
.epoch
));
383 static int write_uint64(FILE *fp
, uint64_t p
) {
384 if (fwrite(&p
, sizeof(p
), 1, fp
) != 1)
390 static int contains_uint64(MMapFileDescriptor
*f
, uint64_t n
, uint64_t p
) {
404 r
= mmap_cache_fd_get(f
, 0, false, c
* sizeof(uint64_t), sizeof(uint64_t), NULL
, (void **) &z
);
423 static int verify_data(
425 Object
*o
, uint64_t p
,
426 MMapFileDescriptor
*cache_entry_fd
, uint64_t n_entries
,
427 MMapFileDescriptor
*cache_entry_array_fd
, uint64_t n_entry_arrays
) {
429 uint64_t i
, n
, a
, last
, q
;
434 assert(cache_entry_fd
);
435 assert(cache_entry_array_fd
);
437 n
= le64toh(o
->data
.n_entries
);
438 a
= le64toh(o
->data
.entry_array_offset
);
440 /* Entry array means at least two objects */
442 error(p
, "Entry array present (entry_array_offset="OFSfmt
", but n_entries=%"PRIu64
")", a
, n
);
449 /* We already checked that earlier */
450 assert(o
->data
.entry_offset
);
452 last
= q
= le64toh(o
->data
.entry_offset
);
453 if (!contains_uint64(cache_entry_fd
, n_entries
, q
)) {
454 error(p
, "Data object references invalid entry at "OFSfmt
, q
);
458 r
= journal_file_move_to_entry_by_offset(f
, q
, DIRECTION_DOWN
, NULL
, NULL
);
462 error(q
, "Entry object doesn't exist in the main entry array");
471 error(p
, "Array chain too short");
475 if (!contains_uint64(cache_entry_array_fd
, n_entry_arrays
, a
)) {
476 error(p
, "Invalid array offset "OFSfmt
, a
);
480 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
484 next
= le64toh(o
->entry_array
.next_entry_array_offset
);
485 if (next
!= 0 && next
<= a
) {
486 error(p
, "Array chain has cycle (jumps back from "OFSfmt
" to "OFSfmt
")", a
, next
);
490 m
= journal_file_entry_array_n_items(o
);
491 for (j
= 0; i
< n
&& j
< m
; i
++, j
++) {
493 q
= le64toh(o
->entry_array
.items
[j
]);
495 error(p
, "Data object's entry array not sorted (%"PRIu64
" <= %"PRIu64
")", q
, last
);
500 if (!contains_uint64(cache_entry_fd
, n_entries
, q
)) {
501 error(p
, "Data object references invalid entry at "OFSfmt
, q
);
505 r
= journal_file_move_to_entry_by_offset(f
, q
, DIRECTION_DOWN
, NULL
, NULL
);
509 error(q
, "Entry object doesn't exist in the main entry array");
513 /* Pointer might have moved, reposition */
514 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
525 static int verify_data_hash_table(
527 MMapFileDescriptor
*cache_data_fd
, uint64_t n_data
,
528 MMapFileDescriptor
*cache_entry_fd
, uint64_t n_entries
,
529 MMapFileDescriptor
*cache_entry_array_fd
, uint64_t n_entry_arrays
,
531 bool show_progress
) {
537 assert(cache_data_fd
);
538 assert(cache_entry_fd
);
539 assert(cache_entry_array_fd
);
542 n
= le64toh(f
->header
->data_hash_table_size
) / sizeof(HashItem
);
546 r
= journal_file_map_data_hash_table(f
);
548 return log_error_errno(r
, "Failed to map data hash table: %m");
550 for (i
= 0; i
< n
; i
++) {
551 uint64_t last
= 0, p
;
554 draw_progress(0xC000 + scale_progress(0x3FFF, i
, n
), last_usec
);
556 p
= le64toh(f
->data_hash_table
[i
].head_hash_offset
);
561 if (!contains_uint64(cache_data_fd
, n_data
, p
)) {
562 error(p
, "Invalid data object at hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
566 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
570 next
= le64toh(o
->data
.next_hash_offset
);
571 if (next
!= 0 && next
<= p
) {
572 error(p
, "Hash chain has a cycle in hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
576 if (le64toh(o
->data
.hash
) % n
!= i
) {
577 error(p
, "Hash value mismatch in hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
581 r
= verify_data(f
, o
, p
, cache_entry_fd
, n_entries
, cache_entry_array_fd
, n_entry_arrays
);
589 if (last
!= le64toh(f
->data_hash_table
[i
].tail_hash_offset
)) {
591 "Tail hash pointer mismatch in hash table (%"PRIu64
" != %"PRIu64
")",
593 le64toh(f
->data_hash_table
[i
].tail_hash_offset
));
601 static int data_object_in_hash_table(JournalFile
*f
, uint64_t hash
, uint64_t p
) {
606 n
= le64toh(f
->header
->data_hash_table_size
) / sizeof(HashItem
);
610 r
= journal_file_map_data_hash_table(f
);
612 return log_error_errno(r
, "Failed to map data hash table: %m");
616 q
= le64toh(f
->data_hash_table
[h
].head_hash_offset
);
623 r
= journal_file_move_to_object(f
, OBJECT_DATA
, q
, &o
);
627 q
= le64toh(o
->data
.next_hash_offset
);
633 static int verify_entry(
635 Object
*o
, uint64_t p
,
636 MMapFileDescriptor
*cache_data_fd
, uint64_t n_data
,
644 assert(cache_data_fd
);
646 n
= journal_file_entry_n_items(o
);
647 for (i
= 0; i
< n
; i
++) {
651 q
= le64toh(o
->entry
.items
[i
].object_offset
);
653 if (!contains_uint64(cache_data_fd
, n_data
, q
)) {
654 error(p
, "Invalid data object of entry");
658 r
= journal_file_move_to_object(f
, OBJECT_DATA
, q
, &u
);
662 r
= data_object_in_hash_table(f
, le64toh(u
->data
.hash
), q
);
666 error(p
, "Data object missing from hash table");
670 r
= journal_file_move_to_entry_by_offset_for_data(f
, u
, p
, DIRECTION_DOWN
, NULL
, NULL
);
674 /* The last entry object has a very high chance of not being referenced as journal files
675 * almost always run out of space during linking of entry items when trying to add a new
676 * entry array so let's not error in that scenario. */
677 if (r
== 0 && !last
) {
678 error(p
, "Entry object not referenced by linked data object at "OFSfmt
, q
);
686 static int verify_entry_array(
688 MMapFileDescriptor
*cache_data_fd
, uint64_t n_data
,
689 MMapFileDescriptor
*cache_entry_fd
, uint64_t n_entries
,
690 MMapFileDescriptor
*cache_entry_array_fd
, uint64_t n_entry_arrays
,
692 bool show_progress
) {
694 uint64_t i
= 0, a
, n
, last
= 0;
698 assert(cache_data_fd
);
699 assert(cache_entry_fd
);
700 assert(cache_entry_array_fd
);
703 n
= le64toh(f
->header
->n_entries
);
704 a
= le64toh(f
->header
->entry_array_offset
);
710 draw_progress(0x8000 + scale_progress(0x3FFF, i
, n
), last_usec
);
713 error(a
, "Array chain too short at %"PRIu64
" of %"PRIu64
, i
, n
);
717 if (!contains_uint64(cache_entry_array_fd
, n_entry_arrays
, a
)) {
718 error(a
, "Invalid array %"PRIu64
" of %"PRIu64
, i
, n
);
722 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
726 next
= le64toh(o
->entry_array
.next_entry_array_offset
);
727 if (next
!= 0 && next
<= a
) {
728 error(a
, "Array chain has cycle at %"PRIu64
" of %"PRIu64
" (jumps back from to "OFSfmt
")", i
, n
, next
);
732 m
= journal_file_entry_array_n_items(o
);
733 for (j
= 0; i
< n
&& j
< m
; i
++, j
++) {
736 p
= le64toh(o
->entry_array
.items
[j
]);
738 error(a
, "Entry array not sorted at %"PRIu64
" of %"PRIu64
, i
, n
);
743 if (!contains_uint64(cache_entry_fd
, n_entries
, p
)) {
744 error(a
, "Invalid array entry at %"PRIu64
" of %"PRIu64
, i
, n
);
748 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, p
, &o
);
752 r
= verify_entry(f
, o
, p
, cache_data_fd
, n_data
, /*last=*/ i
+ 1 == n
);
756 /* Pointer might have moved, reposition */
757 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
768 static int verify_hash_table(
769 Object
*o
, uint64_t p
, uint64_t *n_hash_tables
, uint64_t header_offset
, uint64_t header_size
) {
772 assert(n_hash_tables
);
774 if (*n_hash_tables
> 1) {
776 "More than one %s: %" PRIu64
,
777 journal_object_type_to_string(o
->object
.type
),
782 if (header_offset
!= p
+ offsetof(Object
, hash_table
.items
)) {
784 "Header offset for %s invalid (%" PRIu64
" != %" PRIu64
")",
785 journal_object_type_to_string(o
->object
.type
),
787 p
+ offsetof(Object
, hash_table
.items
));
791 if (header_size
!= le64toh(o
->object
.size
) - offsetof(Object
, hash_table
.items
)) {
793 "Header size for %s invalid (%" PRIu64
" != %" PRIu64
")",
794 journal_object_type_to_string(o
->object
.type
),
796 le64toh(o
->object
.size
) - offsetof(Object
, hash_table
.items
));
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 _cleanup_close_
int data_fd
= -1, entry_fd
= -1, entry_array_fd
= -1;
820 _cleanup_fclose_
FILE *data_fp
= NULL
, *entry_fp
= NULL
, *entry_array_fp
= NULL
;
821 MMapFileDescriptor
*cache_data_fd
= NULL
, *cache_entry_fd
= NULL
, *cache_entry_array_fd
= NULL
;
823 bool found_last
= false;
824 const char *tmp_dir
= NULL
;
828 uint64_t last_tag
= 0;
834 r
= journal_file_parse_verification_key(f
, key
);
836 log_error("Failed to parse seed.");
845 r
= var_tmp_dir(&tmp_dir
);
847 log_error_errno(r
, "Failed to determine temporary directory: %m");
851 data_fd
= open_tmpfile_unlinkable(tmp_dir
, O_RDWR
| O_CLOEXEC
);
853 r
= log_error_errno(data_fd
, "Failed to create data file: %m");
857 entry_fd
= open_tmpfile_unlinkable(tmp_dir
, O_RDWR
| O_CLOEXEC
);
859 r
= log_error_errno(entry_fd
, "Failed to create entry file: %m");
863 entry_array_fd
= open_tmpfile_unlinkable(tmp_dir
, O_RDWR
| O_CLOEXEC
);
864 if (entry_array_fd
< 0) {
865 r
= log_error_errno(entry_array_fd
,
866 "Failed to create entry array file: %m");
870 m
= mmap_cache_fd_cache(f
->cache_fd
);
871 cache_data_fd
= mmap_cache_add_fd(m
, data_fd
, PROT_READ
|PROT_WRITE
);
872 if (!cache_data_fd
) {
877 cache_entry_fd
= mmap_cache_add_fd(m
, entry_fd
, PROT_READ
|PROT_WRITE
);
878 if (!cache_entry_fd
) {
883 cache_entry_array_fd
= mmap_cache_add_fd(m
, entry_array_fd
, PROT_READ
|PROT_WRITE
);
884 if (!cache_entry_array_fd
) {
889 r
= take_fdopen_unlocked(&data_fd
, "w+", &data_fp
);
891 log_error_errno(r
, "Failed to open data file stream: %m");
895 r
= take_fdopen_unlocked(&entry_fd
, "w+", &entry_fp
);
897 log_error_errno(r
, "Failed to open entry file stream: %m");
901 r
= take_fdopen_unlocked(&entry_array_fd
, "w+", &entry_array_fp
);
903 log_error_errno(r
, "Failed to open entry array file stream: %m");
907 if (le32toh(f
->header
->compatible_flags
) & ~HEADER_COMPATIBLE_SUPPORTED
) {
908 log_error("Cannot verify file with unknown extensions.");
913 for (i
= 0; i
< sizeof(f
->header
->reserved
); i
++)
914 if (f
->header
->reserved
[i
] != 0) {
915 error(offsetof(Header
, reserved
[i
]), "Reserved field is non-zero");
920 /* First iteration: we go through all objects, verify the
921 * superficial structure, headers, hashes. */
923 p
= le64toh(f
->header
->header_size
);
925 /* Early exit if there are no objects in the file, at all */
926 if (le64toh(f
->header
->tail_object_offset
) == 0)
930 draw_progress(scale_progress(0x7FFF, p
, le64toh(f
->header
->tail_object_offset
)), &last_usec
);
932 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, p
, &o
);
934 error_errno(p
, r
, "Invalid object: %m");
938 if (p
> le64toh(f
->header
->tail_object_offset
)) {
939 error(offsetof(Header
, tail_object_offset
),
940 "Invalid tail object pointer (%"PRIu64
" > %"PRIu64
")",
942 le64toh(f
->header
->tail_object_offset
));
949 r
= journal_file_object_verify(f
, p
, o
);
951 error_errno(p
, r
, "Invalid object contents: %m");
955 if (!!(o
->object
.flags
& OBJECT_COMPRESSED_XZ
) +
956 !!(o
->object
.flags
& OBJECT_COMPRESSED_LZ4
) +
957 !!(o
->object
.flags
& OBJECT_COMPRESSED_ZSTD
) > 1) {
958 error(p
, "Object has multiple compression flags set (flags: 0x%x)", o
->object
.flags
);
963 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) && !JOURNAL_HEADER_COMPRESSED_XZ(f
->header
)) {
964 error(p
, "XZ compressed object in file without XZ compression");
969 if ((o
->object
.flags
& OBJECT_COMPRESSED_LZ4
) && !JOURNAL_HEADER_COMPRESSED_LZ4(f
->header
)) {
970 error(p
, "LZ4 compressed object in file without LZ4 compression");
975 if ((o
->object
.flags
& OBJECT_COMPRESSED_ZSTD
) && !JOURNAL_HEADER_COMPRESSED_ZSTD(f
->header
)) {
976 error(p
, "ZSTD compressed object in file without ZSTD compression");
981 switch (o
->object
.type
) {
984 r
= write_uint64(data_fp
, p
);
996 if (JOURNAL_HEADER_SEALED(f
->header
) && n_tags
<= 0) {
997 error(p
, "First entry before first tag");
1002 r
= write_uint64(entry_fp
, p
);
1006 if (le64toh(o
->entry
.realtime
) < last_tag_realtime
) {
1008 "Older entry after newer tag (%"PRIu64
" < %"PRIu64
")",
1009 le64toh(o
->entry
.realtime
),
1015 if (!entry_seqnum_set
&&
1016 le64toh(o
->entry
.seqnum
) != le64toh(f
->header
->head_entry_seqnum
)) {
1018 "Head entry sequence number incorrect (%"PRIu64
" != %"PRIu64
")",
1019 le64toh(o
->entry
.seqnum
),
1020 le64toh(f
->header
->head_entry_seqnum
));
1025 if (entry_seqnum_set
&&
1026 entry_seqnum
>= le64toh(o
->entry
.seqnum
)) {
1028 "Entry sequence number out of synchronization (%"PRIu64
" >= %"PRIu64
")",
1030 le64toh(o
->entry
.seqnum
));
1035 entry_seqnum
= le64toh(o
->entry
.seqnum
);
1036 entry_seqnum_set
= true;
1038 if (entry_monotonic_set
&&
1039 sd_id128_equal(entry_boot_id
, o
->entry
.boot_id
) &&
1040 entry_monotonic
> le64toh(o
->entry
.monotonic
)) {
1042 "Entry timestamp out of synchronization (%"PRIu64
" > %"PRIu64
")",
1044 le64toh(o
->entry
.monotonic
));
1049 entry_monotonic
= le64toh(o
->entry
.monotonic
);
1050 entry_boot_id
= o
->entry
.boot_id
;
1051 entry_monotonic_set
= true;
1053 if (!entry_realtime_set
&&
1054 le64toh(o
->entry
.realtime
) != le64toh(f
->header
->head_entry_realtime
)) {
1056 "Head entry realtime timestamp incorrect (%"PRIu64
" != %"PRIu64
")",
1057 le64toh(o
->entry
.realtime
),
1058 le64toh(f
->header
->head_entry_realtime
));
1063 entry_realtime
= le64toh(o
->entry
.realtime
);
1064 entry_realtime_set
= true;
1069 case OBJECT_DATA_HASH_TABLE
:
1070 r
= verify_hash_table(o
, p
, &n_data_hash_tables
,
1071 le64toh(f
->header
->data_hash_table_offset
),
1072 le64toh(f
->header
->data_hash_table_size
));
1077 case OBJECT_FIELD_HASH_TABLE
:
1078 r
= verify_hash_table(o
, p
, &n_field_hash_tables
,
1079 le64toh(f
->header
->field_hash_table_offset
),
1080 le64toh(f
->header
->field_hash_table_size
));
1086 case OBJECT_ENTRY_ARRAY
:
1087 r
= write_uint64(entry_array_fp
, p
);
1091 if (p
== le64toh(f
->header
->entry_array_offset
)) {
1092 if (found_main_entry_array
) {
1093 error(p
, "More than one main entry array");
1098 found_main_entry_array
= true;
1105 if (!JOURNAL_HEADER_SEALED(f
->header
)) {
1106 error(p
, "Tag object in file without sealing");
1111 if (le64toh(o
->tag
.seqnum
) != n_tags
+ 1) {
1113 "Tag sequence number out of synchronization (%"PRIu64
" != %"PRIu64
")",
1114 le64toh(o
->tag
.seqnum
),
1120 if (le64toh(o
->tag
.epoch
) < last_epoch
) {
1122 "Epoch sequence out of synchronization (%"PRIu64
" < %"PRIu64
")",
1123 le64toh(o
->tag
.epoch
),
1133 debug(p
, "Checking tag %"PRIu64
"...", le64toh(o
->tag
.seqnum
));
1135 rt
= f
->fss_start_usec
+ le64toh(o
->tag
.epoch
) * f
->fss_interval_usec
;
1136 if (entry_realtime_set
&& entry_realtime
>= rt
+ f
->fss_interval_usec
) {
1138 "tag/entry realtime timestamp out of synchronization (%"PRIu64
" >= %"PRIu64
")",
1140 rt
+ f
->fss_interval_usec
);
1145 /* OK, now we know the epoch. So let's now set
1146 * it, and calculate the HMAC for everything
1147 * since the last tag. */
1148 r
= journal_file_fsprg_seek(f
, le64toh(o
->tag
.epoch
));
1152 r
= journal_file_hmac_start(f
);
1156 if (last_tag
== 0) {
1157 r
= journal_file_hmac_put_header(f
);
1161 q
= le64toh(f
->header
->header_size
);
1166 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, q
, &o
);
1170 r
= journal_file_hmac_put_object(f
, OBJECT_UNUSED
, o
, q
);
1174 q
= q
+ ALIGN64(le64toh(o
->object
.size
));
1177 /* Position might have changed, let's reposition things */
1178 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, p
, &o
);
1182 if (memcmp(o
->tag
.tag
, gcry_md_read(f
->hmac
, 0), TAG_LENGTH
) != 0) {
1183 error(p
, "Tag failed verification");
1188 f
->hmac_running
= false;
1189 last_tag_realtime
= rt
;
1190 last_sealed_realtime
= entry_realtime
;
1193 last_tag
= p
+ ALIGN64(le64toh(o
->object
.size
));
1196 last_epoch
= le64toh(o
->tag
.epoch
);
1205 if (p
== le64toh(f
->header
->tail_object_offset
)) {
1210 p
= p
+ ALIGN64(le64toh(o
->object
.size
));
1213 if (!found_last
&& le64toh(f
->header
->tail_object_offset
) != 0) {
1214 error(le64toh(f
->header
->tail_object_offset
),
1215 "Tail object pointer dead (%"PRIu64
" != 0)",
1216 le64toh(f
->header
->tail_object_offset
));
1221 if (n_objects
!= le64toh(f
->header
->n_objects
)) {
1222 error(offsetof(Header
, n_objects
),
1223 "Object number mismatch (%"PRIu64
" != %"PRIu64
")",
1225 le64toh(f
->header
->n_objects
));
1230 if (n_entries
!= le64toh(f
->header
->n_entries
)) {
1231 error(offsetof(Header
, n_entries
),
1232 "Entry number mismatch (%"PRIu64
" != %"PRIu64
")",
1234 le64toh(f
->header
->n_entries
));
1239 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_data
) &&
1240 n_data
!= le64toh(f
->header
->n_data
)) {
1241 error(offsetof(Header
, n_data
),
1242 "Data number mismatch (%"PRIu64
" != %"PRIu64
")",
1244 le64toh(f
->header
->n_data
));
1249 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_fields
) &&
1250 n_fields
!= le64toh(f
->header
->n_fields
)) {
1251 error(offsetof(Header
, n_fields
),
1252 "Field number mismatch (%"PRIu64
" != %"PRIu64
")",
1254 le64toh(f
->header
->n_fields
));
1259 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_tags
) &&
1260 n_tags
!= le64toh(f
->header
->n_tags
)) {
1261 error(offsetof(Header
, n_tags
),
1262 "Tag number mismatch (%"PRIu64
" != %"PRIu64
")",
1264 le64toh(f
->header
->n_tags
));
1269 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_entry_arrays
) &&
1270 n_entry_arrays
!= le64toh(f
->header
->n_entry_arrays
)) {
1271 error(offsetof(Header
, n_entry_arrays
),
1272 "Entry array number mismatch (%"PRIu64
" != %"PRIu64
")",
1274 le64toh(f
->header
->n_entry_arrays
));
1279 if (!found_main_entry_array
&& le64toh(f
->header
->entry_array_offset
) != 0) {
1280 error(0, "Missing main entry array");
1285 if (entry_seqnum_set
&&
1286 entry_seqnum
!= le64toh(f
->header
->tail_entry_seqnum
)) {
1287 error(offsetof(Header
, tail_entry_seqnum
),
1288 "Tail entry sequence number incorrect (%"PRIu64
" != %"PRIu64
")",
1290 le64toh(f
->header
->tail_entry_seqnum
));
1295 if (entry_monotonic_set
&&
1296 (sd_id128_equal(entry_boot_id
, f
->header
->boot_id
) &&
1297 entry_monotonic
!= le64toh(f
->header
->tail_entry_monotonic
))) {
1299 "Invalid tail monotonic timestamp (%"PRIu64
" != %"PRIu64
")",
1301 le64toh(f
->header
->tail_entry_monotonic
));
1306 if (entry_realtime_set
&& entry_realtime
!= le64toh(f
->header
->tail_entry_realtime
)) {
1308 "Invalid tail realtime timestamp (%"PRIu64
" != %"PRIu64
")",
1310 le64toh(f
->header
->tail_entry_realtime
));
1315 if (fflush(data_fp
) != 0) {
1316 r
= log_error_errno(errno
, "Failed to flush data file stream: %m");
1320 if (fflush(entry_fp
) != 0) {
1321 r
= log_error_errno(errno
, "Failed to flush entry file stream: %m");
1325 if (fflush(entry_array_fp
) != 0) {
1326 r
= log_error_errno(errno
, "Failed to flush entry array file stream: %m");
1330 /* Second iteration: we follow all objects referenced from the
1331 * two entry points: the object hash table and the entry
1332 * array. We also check that everything referenced (directly
1333 * or indirectly) in the data hash table also exists in the
1334 * entry array, and vice versa. Note that we do not care for
1335 * unreferenced objects. We only care that everything that is
1336 * referenced is consistent. */
1338 r
= verify_entry_array(f
,
1339 cache_data_fd
, n_data
,
1340 cache_entry_fd
, n_entries
,
1341 cache_entry_array_fd
, n_entry_arrays
,
1347 r
= verify_data_hash_table(f
,
1348 cache_data_fd
, n_data
,
1349 cache_entry_fd
, n_entries
,
1350 cache_entry_array_fd
, n_entry_arrays
,
1359 mmap_cache_fd_free(cache_data_fd
);
1360 mmap_cache_fd_free(cache_entry_fd
);
1361 mmap_cache_fd_free(cache_entry_array_fd
);
1363 if (first_contained
)
1364 *first_contained
= le64toh(f
->header
->head_entry_realtime
);
1366 *last_validated
= last_sealed_realtime
;
1368 *last_contained
= le64toh(f
->header
->tail_entry_realtime
);
1376 log_error("File corruption detected at %s:"OFSfmt
" (of %llu bytes, %"PRIu64
"%%).",
1379 (unsigned long long) f
->last_stat
.st_size
,
1380 100 * p
/ f
->last_stat
.st_size
);
1383 mmap_cache_fd_free(cache_data_fd
);
1386 mmap_cache_fd_free(cache_entry_fd
);
1388 if (cache_entry_array_fd
)
1389 mmap_cache_fd_free(cache_entry_array_fd
);