1 /* SPDX-License-Identifier: LGPL-2.1+ */
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" ANSI_HIGHLIGHT_GREEN
, stdout
);
46 for (i
= 0; i
< j
; i
++)
47 fputs("\xe2\x96\x88", stdout
);
49 fputs(ANSI_NORMAL
, stdout
);
51 for (i
= 0; i
< k
; i
++)
52 fputs("\xe2\x96\x91", stdout
);
54 printf(" %3"PRIu64
"%%", 100U * p
/ 65535U);
58 fputs("\x1B[?25h", stdout
);
63 static uint64_t scale_progress(uint64_t scale
, uint64_t p
, uint64_t m
) {
64 /* Calculates scale * p / m, but handles m == 0 safely, and saturates.
65 * Currently all callers use m >= 1, but we keep the check to be defensive.
68 if (p
>= m
|| m
== 0) // lgtm[cpp/constant-comparison]
74 static void flush_progress(void) {
80 n
= (3 * columns()) / 4;
84 for (i
= 0; i
< n
+ 5; i
++)
91 #define debug(_offset, _fmt, ...) do { \
93 log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
96 #define warning(_offset, _fmt, ...) do { \
98 log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
101 #define error(_offset, _fmt, ...) do { \
103 log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
106 #define error_errno(_offset, error, _fmt, ...) do { \
108 log_error_errno(error, OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
111 static int journal_file_object_verify(JournalFile
*f
, uint64_t offset
, Object
*o
) {
118 /* This does various superficial tests about the length an
119 * possible field values. It does not follow any references to
122 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) &&
123 o
->object
.type
!= OBJECT_DATA
) {
124 error(offset
, "Found compressed object that isn't of type DATA, which is not allowed.");
128 switch (o
->object
.type
) {
134 if (le64toh(o
->data
.entry_offset
) == 0)
135 warning(offset
, "Unused data (entry_offset==0)");
137 if ((le64toh(o
->data
.entry_offset
) == 0) ^ (le64toh(o
->data
.n_entries
) == 0)) {
138 error(offset
, "Bad n_entries: %"PRIu64
, le64toh(o
->data
.n_entries
));
142 if (le64toh(o
->object
.size
) - offsetof(DataObject
, payload
) <= 0) {
143 error(offset
, "Bad object size (<= %zu): %"PRIu64
,
144 offsetof(DataObject
, payload
),
145 le64toh(o
->object
.size
));
149 h1
= le64toh(o
->data
.hash
);
151 compression
= o
->object
.flags
& OBJECT_COMPRESSION_MASK
;
153 _cleanup_free_
void *b
= NULL
;
154 size_t alloc
= 0, b_size
;
156 r
= decompress_blob(compression
,
158 le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
),
159 &b
, &alloc
, &b_size
, 0);
161 error_errno(offset
, r
, "%s decompression failed: %m",
162 object_compressed_to_string(compression
));
166 h2
= hash64(b
, b_size
);
168 h2
= hash64(o
->data
.payload
, le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
));
171 error(offset
, "Invalid hash (%08"PRIx64
" vs. %08"PRIx64
, h1
, h2
);
175 if (!VALID64(le64toh(o
->data
.next_hash_offset
)) ||
176 !VALID64(le64toh(o
->data
.next_field_offset
)) ||
177 !VALID64(le64toh(o
->data
.entry_offset
)) ||
178 !VALID64(le64toh(o
->data
.entry_array_offset
))) {
179 error(offset
, "Invalid offset (next_hash_offset="OFSfmt
", next_field_offset="OFSfmt
", entry_offset="OFSfmt
", entry_array_offset="OFSfmt
,
180 le64toh(o
->data
.next_hash_offset
),
181 le64toh(o
->data
.next_field_offset
),
182 le64toh(o
->data
.entry_offset
),
183 le64toh(o
->data
.entry_array_offset
));
191 if (le64toh(o
->object
.size
) - offsetof(FieldObject
, payload
) <= 0) {
193 "Bad field size (<= %zu): %"PRIu64
,
194 offsetof(FieldObject
, payload
),
195 le64toh(o
->object
.size
));
199 if (!VALID64(le64toh(o
->field
.next_hash_offset
)) ||
200 !VALID64(le64toh(o
->field
.head_data_offset
))) {
202 "Invalid offset (next_hash_offset="OFSfmt
", head_data_offset="OFSfmt
,
203 le64toh(o
->field
.next_hash_offset
),
204 le64toh(o
->field
.head_data_offset
));
210 if ((le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) % sizeof(EntryItem
) != 0) {
212 "Bad entry size (<= %zu): %"PRIu64
,
213 offsetof(EntryObject
, items
),
214 le64toh(o
->object
.size
));
218 if ((le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) / sizeof(EntryItem
) <= 0) {
220 "Invalid number items in entry: %"PRIu64
,
221 (le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) / sizeof(EntryItem
));
225 if (le64toh(o
->entry
.seqnum
) <= 0) {
227 "Invalid entry seqnum: %"PRIx64
,
228 le64toh(o
->entry
.seqnum
));
232 if (!VALID_REALTIME(le64toh(o
->entry
.realtime
))) {
234 "Invalid entry realtime timestamp: %"PRIu64
,
235 le64toh(o
->entry
.realtime
));
239 if (!VALID_MONOTONIC(le64toh(o
->entry
.monotonic
))) {
241 "Invalid entry monotonic timestamp: %"PRIu64
,
242 le64toh(o
->entry
.monotonic
));
246 for (i
= 0; i
< journal_file_entry_n_items(o
); i
++) {
247 if (le64toh(o
->entry
.items
[i
].object_offset
) == 0 ||
248 !VALID64(le64toh(o
->entry
.items
[i
].object_offset
))) {
250 "Invalid entry item (%"PRIu64
"/%"PRIu64
" offset: "OFSfmt
,
251 i
, journal_file_entry_n_items(o
),
252 le64toh(o
->entry
.items
[i
].object_offset
));
259 case OBJECT_DATA_HASH_TABLE
:
260 case OBJECT_FIELD_HASH_TABLE
:
261 if ((le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) % sizeof(HashItem
) != 0 ||
262 (le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) / sizeof(HashItem
) <= 0) {
264 "Invalid %s hash table size: %"PRIu64
,
265 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
266 le64toh(o
->object
.size
));
270 for (i
= 0; i
< journal_file_hash_table_n_items(o
); i
++) {
271 if (o
->hash_table
.items
[i
].head_hash_offset
!= 0 &&
272 !VALID64(le64toh(o
->hash_table
.items
[i
].head_hash_offset
))) {
274 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
") head_hash_offset: "OFSfmt
,
275 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
276 i
, journal_file_hash_table_n_items(o
),
277 le64toh(o
->hash_table
.items
[i
].head_hash_offset
));
280 if (o
->hash_table
.items
[i
].tail_hash_offset
!= 0 &&
281 !VALID64(le64toh(o
->hash_table
.items
[i
].tail_hash_offset
))) {
283 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
") tail_hash_offset: "OFSfmt
,
284 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
285 i
, journal_file_hash_table_n_items(o
),
286 le64toh(o
->hash_table
.items
[i
].tail_hash_offset
));
290 if ((o
->hash_table
.items
[i
].head_hash_offset
!= 0) !=
291 (o
->hash_table
.items
[i
].tail_hash_offset
!= 0)) {
293 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
"): head_hash_offset="OFSfmt
" tail_hash_offset="OFSfmt
,
294 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
295 i
, journal_file_hash_table_n_items(o
),
296 le64toh(o
->hash_table
.items
[i
].head_hash_offset
),
297 le64toh(o
->hash_table
.items
[i
].tail_hash_offset
));
304 case OBJECT_ENTRY_ARRAY
:
305 if ((le64toh(o
->object
.size
) - offsetof(EntryArrayObject
, items
)) % sizeof(le64_t
) != 0 ||
306 (le64toh(o
->object
.size
) - offsetof(EntryArrayObject
, items
)) / sizeof(le64_t
) <= 0) {
308 "Invalid object entry array size: %"PRIu64
,
309 le64toh(o
->object
.size
));
313 if (!VALID64(le64toh(o
->entry_array
.next_entry_array_offset
))) {
315 "Invalid object entry array next_entry_array_offset: "OFSfmt
,
316 le64toh(o
->entry_array
.next_entry_array_offset
));
320 for (i
= 0; i
< journal_file_entry_array_n_items(o
); i
++)
321 if (le64toh(o
->entry_array
.items
[i
]) != 0 &&
322 !VALID64(le64toh(o
->entry_array
.items
[i
]))) {
324 "Invalid object entry array item (%"PRIu64
"/%"PRIu64
"): "OFSfmt
,
325 i
, journal_file_entry_array_n_items(o
),
326 le64toh(o
->entry_array
.items
[i
]));
333 if (le64toh(o
->object
.size
) != sizeof(TagObject
)) {
335 "Invalid object tag size: %"PRIu64
,
336 le64toh(o
->object
.size
));
340 if (!VALID_EPOCH(le64toh(o
->tag
.epoch
))) {
342 "Invalid object tag epoch: %"PRIu64
,
343 le64toh(o
->tag
.epoch
));
353 static int write_uint64(int fd
, uint64_t p
) {
356 k
= write(fd
, &p
, sizeof(p
));
365 static int contains_uint64(MMapCache
*m
, MMapFileDescriptor
*f
, uint64_t n
, uint64_t p
) {
380 r
= mmap_cache_get(m
, f
, PROT_READ
|PROT_WRITE
, 0, false, c
* sizeof(uint64_t), sizeof(uint64_t), NULL
, (void **) &z
, NULL
);
399 static int entry_points_to_data(
401 MMapFileDescriptor
*cache_entry_fd
,
412 assert(cache_entry_fd
);
414 if (!contains_uint64(f
->mmap
, cache_entry_fd
, n_entries
, entry_p
)) {
415 error(data_p
, "Data object references invalid entry at "OFSfmt
, entry_p
);
419 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, entry_p
, &o
);
423 n
= journal_file_entry_n_items(o
);
424 for (i
= 0; i
< n
; i
++)
425 if (le64toh(o
->entry
.items
[i
].object_offset
) == data_p
) {
431 error(entry_p
, "Data object at "OFSfmt
" not referenced by linked entry", data_p
);
435 /* Check if this entry is also in main entry array. Since the
436 * main entry array has already been verified we can rely on
437 * its consistency. */
440 n
= le64toh(f
->header
->n_entries
);
441 a
= le64toh(f
->header
->entry_array_offset
);
446 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
450 m
= journal_file_entry_array_n_items(o
);
453 if (entry_p
<= le64toh(o
->entry_array
.items
[u
-1])) {
462 if (le64toh(o
->entry_array
.items
[z
]) == entry_p
)
468 if (entry_p
< le64toh(o
->entry_array
.items
[z
]))
474 error(entry_p
, "Entry object doesn't exist in main entry array");
479 a
= le64toh(o
->entry_array
.next_entry_array_offset
);
485 static int verify_data(
487 Object
*o
, uint64_t p
,
488 MMapFileDescriptor
*cache_entry_fd
, uint64_t n_entries
,
489 MMapFileDescriptor
*cache_entry_array_fd
, uint64_t n_entry_arrays
) {
491 uint64_t i
, n
, a
, last
, q
;
496 assert(cache_entry_fd
);
497 assert(cache_entry_array_fd
);
499 n
= le64toh(o
->data
.n_entries
);
500 a
= le64toh(o
->data
.entry_array_offset
);
502 /* Entry array means at least two objects */
504 error(p
, "Entry array present (entry_array_offset="OFSfmt
", but n_entries=%"PRIu64
")", a
, n
);
511 /* We already checked that earlier */
512 assert(o
->data
.entry_offset
);
514 last
= q
= le64toh(o
->data
.entry_offset
);
515 r
= entry_points_to_data(f
, cache_entry_fd
, n_entries
, q
, p
);
524 error(p
, "Array chain too short");
528 if (!contains_uint64(f
->mmap
, cache_entry_array_fd
, n_entry_arrays
, a
)) {
529 error(p
, "Invalid array offset "OFSfmt
, a
);
533 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
537 next
= le64toh(o
->entry_array
.next_entry_array_offset
);
538 if (next
!= 0 && next
<= a
) {
539 error(p
, "Array chain has cycle (jumps back from "OFSfmt
" to "OFSfmt
")", a
, next
);
543 m
= journal_file_entry_array_n_items(o
);
544 for (j
= 0; i
< n
&& j
< m
; i
++, j
++) {
546 q
= le64toh(o
->entry_array
.items
[j
]);
548 error(p
, "Data object's entry array not sorted");
553 r
= entry_points_to_data(f
, cache_entry_fd
, n_entries
, q
, p
);
557 /* Pointer might have moved, reposition */
558 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
569 static int verify_hash_table(
571 MMapFileDescriptor
*cache_data_fd
, uint64_t n_data
,
572 MMapFileDescriptor
*cache_entry_fd
, uint64_t n_entries
,
573 MMapFileDescriptor
*cache_entry_array_fd
, uint64_t n_entry_arrays
,
575 bool show_progress
) {
581 assert(cache_data_fd
);
582 assert(cache_entry_fd
);
583 assert(cache_entry_array_fd
);
586 n
= le64toh(f
->header
->data_hash_table_size
) / sizeof(HashItem
);
590 r
= journal_file_map_data_hash_table(f
);
592 return log_error_errno(r
, "Failed to map data hash table: %m");
594 for (i
= 0; i
< n
; i
++) {
595 uint64_t last
= 0, p
;
598 draw_progress(0xC000 + scale_progress(0x3FFF, i
, n
), last_usec
);
600 p
= le64toh(f
->data_hash_table
[i
].head_hash_offset
);
605 if (!contains_uint64(f
->mmap
, cache_data_fd
, n_data
, p
)) {
606 error(p
, "Invalid data object at hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
610 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
614 next
= le64toh(o
->data
.next_hash_offset
);
615 if (next
!= 0 && next
<= p
) {
616 error(p
, "Hash chain has a cycle in hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
620 if (le64toh(o
->data
.hash
) % n
!= i
) {
621 error(p
, "Hash value mismatch in hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
625 r
= verify_data(f
, o
, p
, cache_entry_fd
, n_entries
, cache_entry_array_fd
, n_entry_arrays
);
633 if (last
!= le64toh(f
->data_hash_table
[i
].tail_hash_offset
)) {
634 error(p
, "Tail hash pointer mismatch in hash table");
642 static int data_object_in_hash_table(JournalFile
*f
, uint64_t hash
, uint64_t p
) {
647 n
= le64toh(f
->header
->data_hash_table_size
) / sizeof(HashItem
);
651 r
= journal_file_map_data_hash_table(f
);
653 return log_error_errno(r
, "Failed to map data hash table: %m");
657 q
= le64toh(f
->data_hash_table
[h
].head_hash_offset
);
664 r
= journal_file_move_to_object(f
, OBJECT_DATA
, q
, &o
);
668 q
= le64toh(o
->data
.next_hash_offset
);
674 static int verify_entry(
676 Object
*o
, uint64_t p
,
677 MMapFileDescriptor
*cache_data_fd
, uint64_t n_data
) {
684 assert(cache_data_fd
);
686 n
= journal_file_entry_n_items(o
);
687 for (i
= 0; i
< n
; i
++) {
691 q
= le64toh(o
->entry
.items
[i
].object_offset
);
692 h
= le64toh(o
->entry
.items
[i
].hash
);
694 if (!contains_uint64(f
->mmap
, cache_data_fd
, n_data
, q
)) {
695 error(p
, "Invalid data object of entry");
699 r
= journal_file_move_to_object(f
, OBJECT_DATA
, q
, &u
);
703 if (le64toh(u
->data
.hash
) != h
) {
704 error(p
, "Hash mismatch for data object of entry");
708 r
= data_object_in_hash_table(f
, h
, q
);
712 error(p
, "Data object missing from hash table");
720 static int verify_entry_array(
722 MMapFileDescriptor
*cache_data_fd
, uint64_t n_data
,
723 MMapFileDescriptor
*cache_entry_fd
, uint64_t n_entries
,
724 MMapFileDescriptor
*cache_entry_array_fd
, uint64_t n_entry_arrays
,
726 bool show_progress
) {
728 uint64_t i
= 0, a
, n
, last
= 0;
732 assert(cache_data_fd
);
733 assert(cache_entry_fd
);
734 assert(cache_entry_array_fd
);
737 n
= le64toh(f
->header
->n_entries
);
738 a
= le64toh(f
->header
->entry_array_offset
);
744 draw_progress(0x8000 + scale_progress(0x3FFF, i
, n
), last_usec
);
747 error(a
, "Array chain too short at %"PRIu64
" of %"PRIu64
, i
, n
);
751 if (!contains_uint64(f
->mmap
, cache_entry_array_fd
, n_entry_arrays
, a
)) {
752 error(a
, "Invalid array %"PRIu64
" of %"PRIu64
, i
, n
);
756 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
760 next
= le64toh(o
->entry_array
.next_entry_array_offset
);
761 if (next
!= 0 && next
<= a
) {
762 error(a
, "Array chain has cycle at %"PRIu64
" of %"PRIu64
" (jumps back from to "OFSfmt
")", i
, n
, next
);
766 m
= journal_file_entry_array_n_items(o
);
767 for (j
= 0; i
< n
&& j
< m
; i
++, j
++) {
770 p
= le64toh(o
->entry_array
.items
[j
]);
772 error(a
, "Entry array not sorted at %"PRIu64
" of %"PRIu64
, i
, n
);
777 if (!contains_uint64(f
->mmap
, cache_entry_fd
, n_entries
, p
)) {
778 error(a
, "Invalid array entry at %"PRIu64
" of %"PRIu64
, i
, n
);
782 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, p
, &o
);
786 r
= verify_entry(f
, o
, p
, cache_data_fd
, n_data
);
790 /* Pointer might have moved, reposition */
791 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
802 int journal_file_verify(
805 usec_t
*first_contained
, usec_t
*last_validated
, usec_t
*last_contained
,
806 bool show_progress
) {
809 uint64_t p
= 0, last_epoch
= 0, last_tag_realtime
= 0, last_sealed_realtime
= 0;
811 uint64_t entry_seqnum
= 0, entry_monotonic
= 0, entry_realtime
= 0;
812 sd_id128_t entry_boot_id
;
813 bool entry_seqnum_set
= false, entry_monotonic_set
= false, entry_realtime_set
= false, found_main_entry_array
= false;
814 uint64_t n_weird
= 0, n_objects
= 0, n_entries
= 0, n_data
= 0, n_fields
= 0, n_data_hash_tables
= 0, n_field_hash_tables
= 0, n_entry_arrays
= 0, n_tags
= 0;
815 usec_t last_usec
= 0;
816 int data_fd
= -1, entry_fd
= -1, entry_array_fd
= -1;
817 MMapFileDescriptor
*cache_data_fd
= NULL
, *cache_entry_fd
= NULL
, *cache_entry_array_fd
= NULL
;
819 bool found_last
= false;
820 const char *tmp_dir
= NULL
;
823 uint64_t last_tag
= 0;
829 r
= journal_file_parse_verification_key(f
, key
);
831 log_error("Failed to parse seed.");
840 r
= var_tmp_dir(&tmp_dir
);
842 log_error_errno(r
, "Failed to determine temporary directory: %m");
846 data_fd
= open_tmpfile_unlinkable(tmp_dir
, O_RDWR
| O_CLOEXEC
);
848 r
= log_error_errno(data_fd
, "Failed to create data file: %m");
852 entry_fd
= open_tmpfile_unlinkable(tmp_dir
, O_RDWR
| O_CLOEXEC
);
854 r
= log_error_errno(entry_fd
, "Failed to create entry file: %m");
858 entry_array_fd
= open_tmpfile_unlinkable(tmp_dir
, O_RDWR
| O_CLOEXEC
);
859 if (entry_array_fd
< 0) {
860 r
= log_error_errno(entry_array_fd
,
861 "Failed to create entry array file: %m");
865 cache_data_fd
= mmap_cache_add_fd(f
->mmap
, data_fd
);
866 if (!cache_data_fd
) {
871 cache_entry_fd
= mmap_cache_add_fd(f
->mmap
, entry_fd
);
872 if (!cache_entry_fd
) {
877 cache_entry_array_fd
= mmap_cache_add_fd(f
->mmap
, entry_array_fd
);
878 if (!cache_entry_array_fd
) {
883 if (le32toh(f
->header
->compatible_flags
) & ~HEADER_COMPATIBLE_SUPPORTED
) {
884 log_error("Cannot verify file with unknown extensions.");
889 for (i
= 0; i
< sizeof(f
->header
->reserved
); i
++)
890 if (f
->header
->reserved
[i
] != 0) {
891 error(offsetof(Header
, reserved
[i
]), "Reserved field is non-zero");
896 /* First iteration: we go through all objects, verify the
897 * superficial structure, headers, hashes. */
899 p
= le64toh(f
->header
->header_size
);
901 /* Early exit if there are no objects in the file, at all */
902 if (le64toh(f
->header
->tail_object_offset
) == 0)
906 draw_progress(scale_progress(0x7FFF, p
, le64toh(f
->header
->tail_object_offset
)), &last_usec
);
908 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, p
, &o
);
910 error(p
, "Invalid object");
914 if (p
> le64toh(f
->header
->tail_object_offset
)) {
915 error(offsetof(Header
, tail_object_offset
), "Invalid tail object pointer");
922 r
= journal_file_object_verify(f
, p
, o
);
924 error_errno(p
, r
, "Invalid object contents: %m");
928 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) &&
929 (o
->object
.flags
& OBJECT_COMPRESSED_LZ4
)) {
930 error(p
, "Objected with double compression");
935 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) && !JOURNAL_HEADER_COMPRESSED_XZ(f
->header
)) {
936 error(p
, "XZ compressed object in file without XZ compression");
941 if ((o
->object
.flags
& OBJECT_COMPRESSED_LZ4
) && !JOURNAL_HEADER_COMPRESSED_LZ4(f
->header
)) {
942 error(p
, "LZ4 compressed object in file without LZ4 compression");
947 switch (o
->object
.type
) {
950 r
= write_uint64(data_fd
, p
);
962 if (JOURNAL_HEADER_SEALED(f
->header
) && n_tags
<= 0) {
963 error(p
, "First entry before first tag");
968 r
= write_uint64(entry_fd
, p
);
972 if (le64toh(o
->entry
.realtime
) < last_tag_realtime
) {
973 error(p
, "Older entry after newer tag");
978 if (!entry_seqnum_set
&&
979 le64toh(o
->entry
.seqnum
) != le64toh(f
->header
->head_entry_seqnum
)) {
980 error(p
, "Head entry sequence number incorrect");
985 if (entry_seqnum_set
&&
986 entry_seqnum
>= le64toh(o
->entry
.seqnum
)) {
987 error(p
, "Entry sequence number out of synchronization");
992 entry_seqnum
= le64toh(o
->entry
.seqnum
);
993 entry_seqnum_set
= true;
995 if (entry_monotonic_set
&&
996 sd_id128_equal(entry_boot_id
, o
->entry
.boot_id
) &&
997 entry_monotonic
> le64toh(o
->entry
.monotonic
)) {
998 error(p
, "Entry timestamp out of synchronization");
1003 entry_monotonic
= le64toh(o
->entry
.monotonic
);
1004 entry_boot_id
= o
->entry
.boot_id
;
1005 entry_monotonic_set
= true;
1007 if (!entry_realtime_set
&&
1008 le64toh(o
->entry
.realtime
) != le64toh(f
->header
->head_entry_realtime
)) {
1009 error(p
, "Head entry realtime timestamp incorrect");
1014 entry_realtime
= le64toh(o
->entry
.realtime
);
1015 entry_realtime_set
= true;
1020 case OBJECT_DATA_HASH_TABLE
:
1021 if (n_data_hash_tables
> 1) {
1022 error(p
, "More than one data hash table");
1027 if (le64toh(f
->header
->data_hash_table_offset
) != p
+ offsetof(HashTableObject
, items
) ||
1028 le64toh(f
->header
->data_hash_table_size
) != le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) {
1029 error(p
, "header fields for data hash table invalid");
1034 n_data_hash_tables
++;
1037 case OBJECT_FIELD_HASH_TABLE
:
1038 if (n_field_hash_tables
> 1) {
1039 error(p
, "More than one field hash table");
1044 if (le64toh(f
->header
->field_hash_table_offset
) != p
+ offsetof(HashTableObject
, items
) ||
1045 le64toh(f
->header
->field_hash_table_size
) != le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) {
1046 error(p
, "Header fields for field hash table invalid");
1051 n_field_hash_tables
++;
1054 case OBJECT_ENTRY_ARRAY
:
1055 r
= write_uint64(entry_array_fd
, p
);
1059 if (p
== le64toh(f
->header
->entry_array_offset
)) {
1060 if (found_main_entry_array
) {
1061 error(p
, "More than one main entry array");
1066 found_main_entry_array
= true;
1073 if (!JOURNAL_HEADER_SEALED(f
->header
)) {
1074 error(p
, "Tag object in file without sealing");
1079 if (le64toh(o
->tag
.seqnum
) != n_tags
+ 1) {
1080 error(p
, "Tag sequence number out of synchronization");
1085 if (le64toh(o
->tag
.epoch
) < last_epoch
) {
1086 error(p
, "Epoch sequence out of synchronization");
1095 debug(p
, "Checking tag %"PRIu64
"...", le64toh(o
->tag
.seqnum
));
1097 rt
= f
->fss_start_usec
+ le64toh(o
->tag
.epoch
) * f
->fss_interval_usec
;
1098 if (entry_realtime_set
&& entry_realtime
>= rt
+ f
->fss_interval_usec
) {
1099 error(p
, "tag/entry realtime timestamp out of synchronization");
1104 /* OK, now we know the epoch. So let's now set
1105 * it, and calculate the HMAC for everything
1106 * since the last tag. */
1107 r
= journal_file_fsprg_seek(f
, le64toh(o
->tag
.epoch
));
1111 r
= journal_file_hmac_start(f
);
1115 if (last_tag
== 0) {
1116 r
= journal_file_hmac_put_header(f
);
1120 q
= le64toh(f
->header
->header_size
);
1125 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, q
, &o
);
1129 r
= journal_file_hmac_put_object(f
, OBJECT_UNUSED
, o
, q
);
1133 q
= q
+ ALIGN64(le64toh(o
->object
.size
));
1136 /* Position might have changed, let's reposition things */
1137 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, p
, &o
);
1141 if (memcmp(o
->tag
.tag
, gcry_md_read(f
->hmac
, 0), TAG_LENGTH
) != 0) {
1142 error(p
, "Tag failed verification");
1147 f
->hmac_running
= false;
1148 last_tag_realtime
= rt
;
1149 last_sealed_realtime
= entry_realtime
;
1152 last_tag
= p
+ ALIGN64(le64toh(o
->object
.size
));
1155 last_epoch
= le64toh(o
->tag
.epoch
);
1164 if (p
== le64toh(f
->header
->tail_object_offset
)) {
1169 p
= p
+ ALIGN64(le64toh(o
->object
.size
));
1172 if (!found_last
&& le64toh(f
->header
->tail_object_offset
) != 0) {
1173 error(le64toh(f
->header
->tail_object_offset
), "Tail object pointer dead");
1178 if (n_objects
!= le64toh(f
->header
->n_objects
)) {
1179 error(offsetof(Header
, n_objects
), "Object number mismatch");
1184 if (n_entries
!= le64toh(f
->header
->n_entries
)) {
1185 error(offsetof(Header
, n_entries
), "Entry number mismatch");
1190 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_data
) &&
1191 n_data
!= le64toh(f
->header
->n_data
)) {
1192 error(offsetof(Header
, n_data
), "Data number mismatch");
1197 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_fields
) &&
1198 n_fields
!= le64toh(f
->header
->n_fields
)) {
1199 error(offsetof(Header
, n_fields
), "Field number mismatch");
1204 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_tags
) &&
1205 n_tags
!= le64toh(f
->header
->n_tags
)) {
1206 error(offsetof(Header
, n_tags
), "Tag number mismatch");
1211 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_entry_arrays
) &&
1212 n_entry_arrays
!= le64toh(f
->header
->n_entry_arrays
)) {
1213 error(offsetof(Header
, n_entry_arrays
), "Entry array number mismatch");
1218 if (!found_main_entry_array
&& le64toh(f
->header
->entry_array_offset
) != 0) {
1219 error(0, "Missing entry array");
1224 if (entry_seqnum_set
&&
1225 entry_seqnum
!= le64toh(f
->header
->tail_entry_seqnum
)) {
1226 error(offsetof(Header
, tail_entry_seqnum
), "Invalid tail seqnum");
1231 if (entry_monotonic_set
&&
1232 (sd_id128_equal(entry_boot_id
, f
->header
->boot_id
) &&
1233 entry_monotonic
!= le64toh(f
->header
->tail_entry_monotonic
))) {
1234 error(0, "Invalid tail monotonic timestamp");
1239 if (entry_realtime_set
&& entry_realtime
!= le64toh(f
->header
->tail_entry_realtime
)) {
1240 error(0, "Invalid tail realtime timestamp");
1245 /* Second iteration: we follow all objects referenced from the
1246 * two entry points: the object hash table and the entry
1247 * array. We also check that everything referenced (directly
1248 * or indirectly) in the data hash table also exists in the
1249 * entry array, and vice versa. Note that we do not care for
1250 * unreferenced objects. We only care that everything that is
1251 * referenced is consistent. */
1253 r
= verify_entry_array(f
,
1254 cache_data_fd
, n_data
,
1255 cache_entry_fd
, n_entries
,
1256 cache_entry_array_fd
, n_entry_arrays
,
1262 r
= verify_hash_table(f
,
1263 cache_data_fd
, n_data
,
1264 cache_entry_fd
, n_entries
,
1265 cache_entry_array_fd
, n_entry_arrays
,
1274 mmap_cache_free_fd(f
->mmap
, cache_data_fd
);
1275 mmap_cache_free_fd(f
->mmap
, cache_entry_fd
);
1276 mmap_cache_free_fd(f
->mmap
, cache_entry_array_fd
);
1278 safe_close(data_fd
);
1279 safe_close(entry_fd
);
1280 safe_close(entry_array_fd
);
1282 if (first_contained
)
1283 *first_contained
= le64toh(f
->header
->head_entry_realtime
);
1285 *last_validated
= last_sealed_realtime
;
1287 *last_contained
= le64toh(f
->header
->tail_entry_realtime
);
1295 log_error("File corruption detected at %s:"OFSfmt
" (of %llu bytes, %"PRIu64
"%%).",
1298 (unsigned long long) f
->last_stat
.st_size
,
1299 100 * p
/ f
->last_stat
.st_size
);
1302 safe_close(data_fd
);
1305 safe_close(entry_fd
);
1307 if (entry_array_fd
>= 0)
1308 safe_close(entry_array_fd
);
1311 mmap_cache_free_fd(f
->mmap
, cache_data_fd
);
1314 mmap_cache_free_fd(f
->mmap
, cache_entry_fd
);
1316 if (cache_entry_array_fd
)
1317 mmap_cache_free_fd(f
->mmap
, cache_entry_array_fd
);