1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2012 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include "alloc-util.h"
31 #include "journal-authenticate.h"
32 #include "journal-def.h"
33 #include "journal-file.h"
34 #include "journal-verify.h"
37 #include "terminal-util.h"
40 static void draw_progress(uint64_t p
, usec_t
*last_usec
) {
47 z
= now(CLOCK_MONOTONIC
);
50 if (x
!= 0 && x
+ 40 * USEC_PER_MSEC
> z
)
55 n
= (3 * columns()) / 4;
56 j
= (n
* (unsigned) p
) / 65535ULL;
61 fputs("\x1B[?25l" ANSI_HIGHLIGHT_GREEN
, stdout
);
63 for (i
= 0; i
< j
; i
++)
64 fputs("\xe2\x96\x88", stdout
);
66 fputs(ANSI_NORMAL
, stdout
);
68 for (i
= 0; i
< k
; i
++)
69 fputs("\xe2\x96\x91", stdout
);
71 printf(" %3"PRIu64
"%%", 100U * p
/ 65535U);
75 fputs("\x1B[?25h", stdout
);
80 static uint64_t scale_progress(uint64_t scale
, uint64_t p
, uint64_t m
) {
82 /* Calculates scale * p / m, but handles m == 0 safely, and saturates */
90 static void flush_progress(void) {
96 n
= (3 * columns()) / 4;
100 for (i
= 0; i
< n
+ 5; i
++)
107 #define debug(_offset, _fmt, ...) do { \
109 log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
112 #define warning(_offset, _fmt, ...) do { \
114 log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
117 #define error(_offset, _fmt, ...) do { \
119 log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
122 #define error_errno(_offset, error, _fmt, ...) do { \
124 log_error_errno(error, OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
127 static int journal_file_object_verify(JournalFile
*f
, uint64_t offset
, Object
*o
) {
134 /* This does various superficial tests about the length an
135 * possible field values. It does not follow any references to
138 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) &&
139 o
->object
.type
!= OBJECT_DATA
) {
140 error(offset
, "Found compressed object that isn't of type DATA, which is not allowed.");
144 switch (o
->object
.type
) {
150 if (le64toh(o
->data
.entry_offset
) == 0)
151 warning(offset
, "Unused data (entry_offset==0)");
153 if ((le64toh(o
->data
.entry_offset
) == 0) ^ (le64toh(o
->data
.n_entries
) == 0)) {
154 error(offset
, "Bad n_entries: %"PRIu64
, le64toh(o
->data
.n_entries
));
158 if (le64toh(o
->object
.size
) - offsetof(DataObject
, payload
) <= 0) {
159 error(offset
, "Bad object size (<= %zu): %"PRIu64
,
160 offsetof(DataObject
, payload
),
161 le64toh(o
->object
.size
));
165 h1
= le64toh(o
->data
.hash
);
167 compression
= o
->object
.flags
& OBJECT_COMPRESSION_MASK
;
169 _cleanup_free_
void *b
= NULL
;
170 size_t alloc
= 0, b_size
;
172 r
= decompress_blob(compression
,
174 le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
),
175 &b
, &alloc
, &b_size
, 0);
177 error_errno(offset
, r
, "%s decompression failed: %m",
178 object_compressed_to_string(compression
));
182 h2
= hash64(b
, b_size
);
184 h2
= hash64(o
->data
.payload
, le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
));
187 error(offset
, "Invalid hash (%08"PRIx64
" vs. %08"PRIx64
, h1
, h2
);
191 if (!VALID64(le64toh(o
->data
.next_hash_offset
)) ||
192 !VALID64(le64toh(o
->data
.next_field_offset
)) ||
193 !VALID64(le64toh(o
->data
.entry_offset
)) ||
194 !VALID64(le64toh(o
->data
.entry_array_offset
))) {
195 error(offset
, "Invalid offset (next_hash_offset="OFSfmt
", next_field_offset="OFSfmt
", entry_offset="OFSfmt
", entry_array_offset="OFSfmt
,
196 le64toh(o
->data
.next_hash_offset
),
197 le64toh(o
->data
.next_field_offset
),
198 le64toh(o
->data
.entry_offset
),
199 le64toh(o
->data
.entry_array_offset
));
207 if (le64toh(o
->object
.size
) - offsetof(FieldObject
, payload
) <= 0) {
209 "Bad field size (<= %zu): %"PRIu64
,
210 offsetof(FieldObject
, payload
),
211 le64toh(o
->object
.size
));
215 if (!VALID64(le64toh(o
->field
.next_hash_offset
)) ||
216 !VALID64(le64toh(o
->field
.head_data_offset
))) {
218 "Invalid offset (next_hash_offset="OFSfmt
", head_data_offset="OFSfmt
,
219 le64toh(o
->field
.next_hash_offset
),
220 le64toh(o
->field
.head_data_offset
));
226 if ((le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) % sizeof(EntryItem
) != 0) {
228 "Bad entry size (<= %zu): %"PRIu64
,
229 offsetof(EntryObject
, items
),
230 le64toh(o
->object
.size
));
234 if ((le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) / sizeof(EntryItem
) <= 0) {
236 "Invalid number items in entry: %"PRIu64
,
237 (le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) / sizeof(EntryItem
));
241 if (le64toh(o
->entry
.seqnum
) <= 0) {
243 "Invalid entry seqnum: %"PRIx64
,
244 le64toh(o
->entry
.seqnum
));
248 if (!VALID_REALTIME(le64toh(o
->entry
.realtime
))) {
250 "Invalid entry realtime timestamp: %"PRIu64
,
251 le64toh(o
->entry
.realtime
));
255 if (!VALID_MONOTONIC(le64toh(o
->entry
.monotonic
))) {
257 "Invalid entry monotonic timestamp: %"PRIu64
,
258 le64toh(o
->entry
.monotonic
));
262 for (i
= 0; i
< journal_file_entry_n_items(o
); i
++) {
263 if (le64toh(o
->entry
.items
[i
].object_offset
) == 0 ||
264 !VALID64(le64toh(o
->entry
.items
[i
].object_offset
))) {
266 "Invalid entry item (%"PRIu64
"/%"PRIu64
" offset: "OFSfmt
,
267 i
, journal_file_entry_n_items(o
),
268 le64toh(o
->entry
.items
[i
].object_offset
));
275 case OBJECT_DATA_HASH_TABLE
:
276 case OBJECT_FIELD_HASH_TABLE
:
277 if ((le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) % sizeof(HashItem
) != 0 ||
278 (le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) / sizeof(HashItem
) <= 0) {
280 "Invalid %s hash table size: %"PRIu64
,
281 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
282 le64toh(o
->object
.size
));
286 for (i
= 0; i
< journal_file_hash_table_n_items(o
); i
++) {
287 if (o
->hash_table
.items
[i
].head_hash_offset
!= 0 &&
288 !VALID64(le64toh(o
->hash_table
.items
[i
].head_hash_offset
))) {
290 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
") head_hash_offset: "OFSfmt
,
291 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
292 i
, journal_file_hash_table_n_items(o
),
293 le64toh(o
->hash_table
.items
[i
].head_hash_offset
));
296 if (o
->hash_table
.items
[i
].tail_hash_offset
!= 0 &&
297 !VALID64(le64toh(o
->hash_table
.items
[i
].tail_hash_offset
))) {
299 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
") tail_hash_offset: "OFSfmt
,
300 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
301 i
, journal_file_hash_table_n_items(o
),
302 le64toh(o
->hash_table
.items
[i
].tail_hash_offset
));
306 if ((o
->hash_table
.items
[i
].head_hash_offset
!= 0) !=
307 (o
->hash_table
.items
[i
].tail_hash_offset
!= 0)) {
309 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
"): head_hash_offset="OFSfmt
" tail_hash_offset="OFSfmt
,
310 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
311 i
, journal_file_hash_table_n_items(o
),
312 le64toh(o
->hash_table
.items
[i
].head_hash_offset
),
313 le64toh(o
->hash_table
.items
[i
].tail_hash_offset
));
320 case OBJECT_ENTRY_ARRAY
:
321 if ((le64toh(o
->object
.size
) - offsetof(EntryArrayObject
, items
)) % sizeof(le64_t
) != 0 ||
322 (le64toh(o
->object
.size
) - offsetof(EntryArrayObject
, items
)) / sizeof(le64_t
) <= 0) {
324 "Invalid object entry array size: %"PRIu64
,
325 le64toh(o
->object
.size
));
329 if (!VALID64(le64toh(o
->entry_array
.next_entry_array_offset
))) {
331 "Invalid object entry array next_entry_array_offset: "OFSfmt
,
332 le64toh(o
->entry_array
.next_entry_array_offset
));
336 for (i
= 0; i
< journal_file_entry_array_n_items(o
); i
++)
337 if (le64toh(o
->entry_array
.items
[i
]) != 0 &&
338 !VALID64(le64toh(o
->entry_array
.items
[i
]))) {
340 "Invalid object entry array item (%"PRIu64
"/%"PRIu64
"): "OFSfmt
,
341 i
, journal_file_entry_array_n_items(o
),
342 le64toh(o
->entry_array
.items
[i
]));
349 if (le64toh(o
->object
.size
) != sizeof(TagObject
)) {
351 "Invalid object tag size: %"PRIu64
,
352 le64toh(o
->object
.size
));
356 if (!VALID_EPOCH(le64toh(o
->tag
.epoch
))) {
358 "Invalid object tag epoch: %"PRIu64
,
359 le64toh(o
->tag
.epoch
));
369 static int write_uint64(int fd
, uint64_t p
) {
372 k
= write(fd
, &p
, sizeof(p
));
381 static int contains_uint64(MMapCache
*m
, MMapFileDescriptor
*f
, uint64_t n
, uint64_t p
) {
396 r
= mmap_cache_get(m
, f
, PROT_READ
|PROT_WRITE
, 0, false, c
* sizeof(uint64_t), sizeof(uint64_t), NULL
, (void **) &z
, NULL
);
415 static int entry_points_to_data(
417 MMapFileDescriptor
*cache_entry_fd
,
428 assert(cache_entry_fd
);
430 if (!contains_uint64(f
->mmap
, cache_entry_fd
, n_entries
, entry_p
)) {
431 error(data_p
, "Data object references invalid entry at "OFSfmt
, entry_p
);
435 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, entry_p
, &o
);
439 n
= journal_file_entry_n_items(o
);
440 for (i
= 0; i
< n
; i
++)
441 if (le64toh(o
->entry
.items
[i
].object_offset
) == data_p
) {
447 error(entry_p
, "Data object at "OFSfmt
" not referenced by linked entry", data_p
);
451 /* Check if this entry is also in main entry array. Since the
452 * main entry array has already been verified we can rely on
453 * its consistency. */
456 n
= le64toh(f
->header
->n_entries
);
457 a
= le64toh(f
->header
->entry_array_offset
);
462 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
466 m
= journal_file_entry_array_n_items(o
);
469 if (entry_p
<= le64toh(o
->entry_array
.items
[u
-1])) {
478 if (le64toh(o
->entry_array
.items
[z
]) == entry_p
)
484 if (entry_p
< le64toh(o
->entry_array
.items
[z
]))
490 error(entry_p
, "Entry object doesn't exist in main entry array");
495 a
= le64toh(o
->entry_array
.next_entry_array_offset
);
501 static int verify_data(
503 Object
*o
, uint64_t p
,
504 MMapFileDescriptor
*cache_entry_fd
, uint64_t n_entries
,
505 MMapFileDescriptor
*cache_entry_array_fd
, uint64_t n_entry_arrays
) {
507 uint64_t i
, n
, a
, last
, q
;
512 assert(cache_entry_fd
);
513 assert(cache_entry_array_fd
);
515 n
= le64toh(o
->data
.n_entries
);
516 a
= le64toh(o
->data
.entry_array_offset
);
518 /* Entry array means at least two objects */
520 error(p
, "Entry array present (entry_array_offset="OFSfmt
", but n_entries=%"PRIu64
")", a
, n
);
527 /* We already checked that earlier */
528 assert(o
->data
.entry_offset
);
530 last
= q
= le64toh(o
->data
.entry_offset
);
531 r
= entry_points_to_data(f
, cache_entry_fd
, n_entries
, q
, p
);
540 error(p
, "Array chain too short");
544 if (!contains_uint64(f
->mmap
, cache_entry_array_fd
, n_entry_arrays
, a
)) {
545 error(p
, "Invalid array offset "OFSfmt
, a
);
549 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
553 next
= le64toh(o
->entry_array
.next_entry_array_offset
);
554 if (next
!= 0 && next
<= a
) {
555 error(p
, "Array chain has cycle (jumps back from "OFSfmt
" to "OFSfmt
")", a
, next
);
559 m
= journal_file_entry_array_n_items(o
);
560 for (j
= 0; i
< n
&& j
< m
; i
++, j
++) {
562 q
= le64toh(o
->entry_array
.items
[j
]);
564 error(p
, "Data object's entry array not sorted");
569 r
= entry_points_to_data(f
, cache_entry_fd
, n_entries
, q
, p
);
573 /* Pointer might have moved, reposition */
574 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
585 static int verify_hash_table(
587 MMapFileDescriptor
*cache_data_fd
, uint64_t n_data
,
588 MMapFileDescriptor
*cache_entry_fd
, uint64_t n_entries
,
589 MMapFileDescriptor
*cache_entry_array_fd
, uint64_t n_entry_arrays
,
591 bool show_progress
) {
597 assert(cache_data_fd
);
598 assert(cache_entry_fd
);
599 assert(cache_entry_array_fd
);
602 n
= le64toh(f
->header
->data_hash_table_size
) / sizeof(HashItem
);
606 r
= journal_file_map_data_hash_table(f
);
608 return log_error_errno(r
, "Failed to map data hash table: %m");
610 for (i
= 0; i
< n
; i
++) {
611 uint64_t last
= 0, p
;
614 draw_progress(0xC000 + scale_progress(0x3FFF, i
, n
), last_usec
);
616 p
= le64toh(f
->data_hash_table
[i
].head_hash_offset
);
621 if (!contains_uint64(f
->mmap
, cache_data_fd
, n_data
, p
)) {
622 error(p
, "Invalid data object at hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
626 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
630 next
= le64toh(o
->data
.next_hash_offset
);
631 if (next
!= 0 && next
<= p
) {
632 error(p
, "Hash chain has a cycle in hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
636 if (le64toh(o
->data
.hash
) % n
!= i
) {
637 error(p
, "Hash value mismatch in hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
641 r
= verify_data(f
, o
, p
, cache_entry_fd
, n_entries
, cache_entry_array_fd
, n_entry_arrays
);
649 if (last
!= le64toh(f
->data_hash_table
[i
].tail_hash_offset
)) {
650 error(p
, "Tail hash pointer mismatch in hash table");
658 static int data_object_in_hash_table(JournalFile
*f
, uint64_t hash
, uint64_t p
) {
663 n
= le64toh(f
->header
->data_hash_table_size
) / sizeof(HashItem
);
667 r
= journal_file_map_data_hash_table(f
);
669 return log_error_errno(r
, "Failed to map data hash table: %m");
673 q
= le64toh(f
->data_hash_table
[h
].head_hash_offset
);
680 r
= journal_file_move_to_object(f
, OBJECT_DATA
, q
, &o
);
684 q
= le64toh(o
->data
.next_hash_offset
);
690 static int verify_entry(
692 Object
*o
, uint64_t p
,
693 MMapFileDescriptor
*cache_data_fd
, uint64_t n_data
) {
700 assert(cache_data_fd
);
702 n
= journal_file_entry_n_items(o
);
703 for (i
= 0; i
< n
; i
++) {
707 q
= le64toh(o
->entry
.items
[i
].object_offset
);
708 h
= le64toh(o
->entry
.items
[i
].hash
);
710 if (!contains_uint64(f
->mmap
, cache_data_fd
, n_data
, q
)) {
711 error(p
, "Invalid data object of entry");
715 r
= journal_file_move_to_object(f
, OBJECT_DATA
, q
, &u
);
719 if (le64toh(u
->data
.hash
) != h
) {
720 error(p
, "Hash mismatch for data object of entry");
724 r
= data_object_in_hash_table(f
, h
, q
);
728 error(p
, "Data object missing from hash table");
736 static int verify_entry_array(
738 MMapFileDescriptor
*cache_data_fd
, uint64_t n_data
,
739 MMapFileDescriptor
*cache_entry_fd
, uint64_t n_entries
,
740 MMapFileDescriptor
*cache_entry_array_fd
, uint64_t n_entry_arrays
,
742 bool show_progress
) {
744 uint64_t i
= 0, a
, n
, last
= 0;
748 assert(cache_data_fd
);
749 assert(cache_entry_fd
);
750 assert(cache_entry_array_fd
);
753 n
= le64toh(f
->header
->n_entries
);
754 a
= le64toh(f
->header
->entry_array_offset
);
760 draw_progress(0x8000 + scale_progress(0x3FFF, i
, n
), last_usec
);
763 error(a
, "Array chain too short at %"PRIu64
" of %"PRIu64
, i
, n
);
767 if (!contains_uint64(f
->mmap
, cache_entry_array_fd
, n_entry_arrays
, a
)) {
768 error(a
, "Invalid array %"PRIu64
" of %"PRIu64
, i
, n
);
772 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
776 next
= le64toh(o
->entry_array
.next_entry_array_offset
);
777 if (next
!= 0 && next
<= a
) {
778 error(a
, "Array chain has cycle at %"PRIu64
" of %"PRIu64
" (jumps back from to "OFSfmt
")", i
, n
, next
);
782 m
= journal_file_entry_array_n_items(o
);
783 for (j
= 0; i
< n
&& j
< m
; i
++, j
++) {
786 p
= le64toh(o
->entry_array
.items
[j
]);
788 error(a
, "Entry array not sorted at %"PRIu64
" of %"PRIu64
, i
, n
);
793 if (!contains_uint64(f
->mmap
, cache_entry_fd
, n_entries
, p
)) {
794 error(a
, "Invalid array entry at %"PRIu64
" of %"PRIu64
, i
, n
);
798 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, p
, &o
);
802 r
= verify_entry(f
, o
, p
, cache_data_fd
, n_data
);
806 /* Pointer might have moved, reposition */
807 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
818 int journal_file_verify(
821 usec_t
*first_contained
, usec_t
*last_validated
, usec_t
*last_contained
,
822 bool show_progress
) {
825 uint64_t p
= 0, last_epoch
= 0, last_tag_realtime
= 0, last_sealed_realtime
= 0;
827 uint64_t entry_seqnum
= 0, entry_monotonic
= 0, entry_realtime
= 0;
828 sd_id128_t entry_boot_id
;
829 bool entry_seqnum_set
= false, entry_monotonic_set
= false, entry_realtime_set
= false, found_main_entry_array
= false;
830 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;
831 usec_t last_usec
= 0;
832 int data_fd
= -1, entry_fd
= -1, entry_array_fd
= -1;
833 MMapFileDescriptor
*cache_data_fd
= NULL
, *cache_entry_fd
= NULL
, *cache_entry_array_fd
= NULL
;
835 bool found_last
= false;
836 const char *tmp_dir
= NULL
;
839 uint64_t last_tag
= 0;
845 r
= journal_file_parse_verification_key(f
, key
);
847 log_error("Failed to parse seed.");
856 r
= var_tmp_dir(&tmp_dir
);
858 log_error_errno(r
, "Failed to determine temporary directory: %m");
862 data_fd
= open_tmpfile_unlinkable(tmp_dir
, O_RDWR
| O_CLOEXEC
);
864 r
= log_error_errno(data_fd
, "Failed to create data file: %m");
868 entry_fd
= open_tmpfile_unlinkable(tmp_dir
, O_RDWR
| O_CLOEXEC
);
870 r
= log_error_errno(entry_fd
, "Failed to create entry file: %m");
874 entry_array_fd
= open_tmpfile_unlinkable(tmp_dir
, O_RDWR
| O_CLOEXEC
);
875 if (entry_array_fd
< 0) {
876 r
= log_error_errno(entry_array_fd
,
877 "Failed to create entry array file: %m");
881 cache_data_fd
= mmap_cache_add_fd(f
->mmap
, data_fd
);
882 if (!cache_data_fd
) {
887 cache_entry_fd
= mmap_cache_add_fd(f
->mmap
, entry_fd
);
888 if (!cache_entry_fd
) {
893 cache_entry_array_fd
= mmap_cache_add_fd(f
->mmap
, entry_array_fd
);
894 if (!cache_entry_array_fd
) {
899 if (le32toh(f
->header
->compatible_flags
) & ~HEADER_COMPATIBLE_SUPPORTED
) {
900 log_error("Cannot verify file with unknown extensions.");
905 for (i
= 0; i
< sizeof(f
->header
->reserved
); i
++)
906 if (f
->header
->reserved
[i
] != 0) {
907 error(offsetof(Header
, reserved
[i
]), "Reserved field is non-zero");
912 /* First iteration: we go through all objects, verify the
913 * superficial structure, headers, hashes. */
915 p
= le64toh(f
->header
->header_size
);
917 /* Early exit if there are no objects in the file, at all */
918 if (le64toh(f
->header
->tail_object_offset
) == 0)
922 draw_progress(scale_progress(0x7FFF, p
, le64toh(f
->header
->tail_object_offset
)), &last_usec
);
924 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, p
, &o
);
926 error(p
, "Invalid object");
930 if (p
> le64toh(f
->header
->tail_object_offset
)) {
931 error(offsetof(Header
, tail_object_offset
), "Invalid tail object pointer");
938 r
= journal_file_object_verify(f
, p
, o
);
940 error_errno(p
, r
, "Invalid object contents: %m");
944 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) &&
945 (o
->object
.flags
& OBJECT_COMPRESSED_LZ4
)) {
946 error(p
, "Objected with double compression");
951 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) && !JOURNAL_HEADER_COMPRESSED_XZ(f
->header
)) {
952 error(p
, "XZ compressed object in file without XZ compression");
957 if ((o
->object
.flags
& OBJECT_COMPRESSED_LZ4
) && !JOURNAL_HEADER_COMPRESSED_LZ4(f
->header
)) {
958 error(p
, "LZ4 compressed object in file without LZ4 compression");
963 switch (o
->object
.type
) {
966 r
= write_uint64(data_fd
, p
);
978 if (JOURNAL_HEADER_SEALED(f
->header
) && n_tags
<= 0) {
979 error(p
, "First entry before first tag");
984 r
= write_uint64(entry_fd
, p
);
988 if (le64toh(o
->entry
.realtime
) < last_tag_realtime
) {
989 error(p
, "Older entry after newer tag");
994 if (!entry_seqnum_set
&&
995 le64toh(o
->entry
.seqnum
) != le64toh(f
->header
->head_entry_seqnum
)) {
996 error(p
, "Head entry sequence number incorrect");
1001 if (entry_seqnum_set
&&
1002 entry_seqnum
>= le64toh(o
->entry
.seqnum
)) {
1003 error(p
, "Entry sequence number out of synchronization");
1008 entry_seqnum
= le64toh(o
->entry
.seqnum
);
1009 entry_seqnum_set
= true;
1011 if (entry_monotonic_set
&&
1012 sd_id128_equal(entry_boot_id
, o
->entry
.boot_id
) &&
1013 entry_monotonic
> le64toh(o
->entry
.monotonic
)) {
1014 error(p
, "Entry timestamp out of synchronization");
1019 entry_monotonic
= le64toh(o
->entry
.monotonic
);
1020 entry_boot_id
= o
->entry
.boot_id
;
1021 entry_monotonic_set
= true;
1023 if (!entry_realtime_set
&&
1024 le64toh(o
->entry
.realtime
) != le64toh(f
->header
->head_entry_realtime
)) {
1025 error(p
, "Head entry realtime timestamp incorrect");
1030 entry_realtime
= le64toh(o
->entry
.realtime
);
1031 entry_realtime_set
= true;
1036 case OBJECT_DATA_HASH_TABLE
:
1037 if (n_data_hash_tables
> 1) {
1038 error(p
, "More than one data hash table");
1043 if (le64toh(f
->header
->data_hash_table_offset
) != p
+ offsetof(HashTableObject
, items
) ||
1044 le64toh(f
->header
->data_hash_table_size
) != le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) {
1045 error(p
, "header fields for data hash table invalid");
1050 n_data_hash_tables
++;
1053 case OBJECT_FIELD_HASH_TABLE
:
1054 if (n_field_hash_tables
> 1) {
1055 error(p
, "More than one field hash table");
1060 if (le64toh(f
->header
->field_hash_table_offset
) != p
+ offsetof(HashTableObject
, items
) ||
1061 le64toh(f
->header
->field_hash_table_size
) != le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) {
1062 error(p
, "Header fields for field hash table invalid");
1067 n_field_hash_tables
++;
1070 case OBJECT_ENTRY_ARRAY
:
1071 r
= write_uint64(entry_array_fd
, p
);
1075 if (p
== le64toh(f
->header
->entry_array_offset
)) {
1076 if (found_main_entry_array
) {
1077 error(p
, "More than one main entry array");
1082 found_main_entry_array
= true;
1089 if (!JOURNAL_HEADER_SEALED(f
->header
)) {
1090 error(p
, "Tag object in file without sealing");
1095 if (le64toh(o
->tag
.seqnum
) != n_tags
+ 1) {
1096 error(p
, "Tag sequence number out of synchronization");
1101 if (le64toh(o
->tag
.epoch
) < last_epoch
) {
1102 error(p
, "Epoch sequence out of synchronization");
1111 debug(p
, "Checking tag %"PRIu64
"...", le64toh(o
->tag
.seqnum
));
1113 rt
= f
->fss_start_usec
+ le64toh(o
->tag
.epoch
) * f
->fss_interval_usec
;
1114 if (entry_realtime_set
&& entry_realtime
>= rt
+ f
->fss_interval_usec
) {
1115 error(p
, "tag/entry realtime timestamp out of synchronization");
1120 /* OK, now we know the epoch. So let's now set
1121 * it, and calculate the HMAC for everything
1122 * since the last tag. */
1123 r
= journal_file_fsprg_seek(f
, le64toh(o
->tag
.epoch
));
1127 r
= journal_file_hmac_start(f
);
1131 if (last_tag
== 0) {
1132 r
= journal_file_hmac_put_header(f
);
1136 q
= le64toh(f
->header
->header_size
);
1141 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, q
, &o
);
1145 r
= journal_file_hmac_put_object(f
, OBJECT_UNUSED
, o
, q
);
1149 q
= q
+ ALIGN64(le64toh(o
->object
.size
));
1152 /* Position might have changed, let's reposition things */
1153 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, p
, &o
);
1157 if (memcmp(o
->tag
.tag
, gcry_md_read(f
->hmac
, 0), TAG_LENGTH
) != 0) {
1158 error(p
, "Tag failed verification");
1163 f
->hmac_running
= false;
1164 last_tag_realtime
= rt
;
1165 last_sealed_realtime
= entry_realtime
;
1168 last_tag
= p
+ ALIGN64(le64toh(o
->object
.size
));
1171 last_epoch
= le64toh(o
->tag
.epoch
);
1180 if (p
== le64toh(f
->header
->tail_object_offset
)) {
1185 p
= p
+ ALIGN64(le64toh(o
->object
.size
));
1188 if (!found_last
&& le64toh(f
->header
->tail_object_offset
) != 0) {
1189 error(le64toh(f
->header
->tail_object_offset
), "Tail object pointer dead");
1194 if (n_objects
!= le64toh(f
->header
->n_objects
)) {
1195 error(offsetof(Header
, n_objects
), "Object number mismatch");
1200 if (n_entries
!= le64toh(f
->header
->n_entries
)) {
1201 error(offsetof(Header
, n_entries
), "Entry number mismatch");
1206 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_data
) &&
1207 n_data
!= le64toh(f
->header
->n_data
)) {
1208 error(offsetof(Header
, n_data
), "Data number mismatch");
1213 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_fields
) &&
1214 n_fields
!= le64toh(f
->header
->n_fields
)) {
1215 error(offsetof(Header
, n_fields
), "Field number mismatch");
1220 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_tags
) &&
1221 n_tags
!= le64toh(f
->header
->n_tags
)) {
1222 error(offsetof(Header
, n_tags
), "Tag number mismatch");
1227 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_entry_arrays
) &&
1228 n_entry_arrays
!= le64toh(f
->header
->n_entry_arrays
)) {
1229 error(offsetof(Header
, n_entry_arrays
), "Entry array number mismatch");
1234 if (!found_main_entry_array
&& le64toh(f
->header
->entry_array_offset
) != 0) {
1235 error(0, "Missing entry array");
1240 if (entry_seqnum_set
&&
1241 entry_seqnum
!= le64toh(f
->header
->tail_entry_seqnum
)) {
1242 error(offsetof(Header
, tail_entry_seqnum
), "Invalid tail seqnum");
1247 if (entry_monotonic_set
&&
1248 (!sd_id128_equal(entry_boot_id
, f
->header
->boot_id
) ||
1249 entry_monotonic
!= le64toh(f
->header
->tail_entry_monotonic
))) {
1250 error(0, "Invalid tail monotonic timestamp");
1255 if (entry_realtime_set
&& entry_realtime
!= le64toh(f
->header
->tail_entry_realtime
)) {
1256 error(0, "Invalid tail realtime timestamp");
1261 /* Second iteration: we follow all objects referenced from the
1262 * two entry points: the object hash table and the entry
1263 * array. We also check that everything referenced (directly
1264 * or indirectly) in the data hash table also exists in the
1265 * entry array, and vice versa. Note that we do not care for
1266 * unreferenced objects. We only care that everything that is
1267 * referenced is consistent. */
1269 r
= verify_entry_array(f
,
1270 cache_data_fd
, n_data
,
1271 cache_entry_fd
, n_entries
,
1272 cache_entry_array_fd
, n_entry_arrays
,
1278 r
= verify_hash_table(f
,
1279 cache_data_fd
, n_data
,
1280 cache_entry_fd
, n_entries
,
1281 cache_entry_array_fd
, n_entry_arrays
,
1290 mmap_cache_free_fd(f
->mmap
, cache_data_fd
);
1291 mmap_cache_free_fd(f
->mmap
, cache_entry_fd
);
1292 mmap_cache_free_fd(f
->mmap
, cache_entry_array_fd
);
1294 safe_close(data_fd
);
1295 safe_close(entry_fd
);
1296 safe_close(entry_array_fd
);
1298 if (first_contained
)
1299 *first_contained
= le64toh(f
->header
->head_entry_realtime
);
1301 *last_validated
= last_sealed_realtime
;
1303 *last_contained
= le64toh(f
->header
->tail_entry_realtime
);
1311 log_error("File corruption detected at %s:"OFSfmt
" (of %llu bytes, %"PRIu64
"%%).",
1314 (unsigned long long) f
->last_stat
.st_size
,
1315 100 * p
/ f
->last_stat
.st_size
);
1318 safe_close(data_fd
);
1321 safe_close(entry_fd
);
1323 if (entry_array_fd
>= 0)
1324 safe_close(entry_array_fd
);
1327 mmap_cache_free_fd(f
->mmap
, cache_data_fd
);
1330 mmap_cache_free_fd(f
->mmap
, cache_entry_fd
);
1332 if (cache_entry_array_fd
)
1333 mmap_cache_free_fd(f
->mmap
, cache_entry_array_fd
);