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
, stdout
);
58 for (i
= 0; i
< j
; i
++)
59 fputs("\xe2\x96\x88", stdout
);
61 fputs(ANSI_NORMAL
, 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 r
= log_error_errno(errno
, "Failed to create data file: %m");
846 entry_fd
= open_tmpfile("/var/tmp", O_RDWR
| O_CLOEXEC
);
848 r
= log_error_errno(errno
, "Failed to create entry file: %m");
852 entry_array_fd
= open_tmpfile("/var/tmp", O_RDWR
| O_CLOEXEC
);
853 if (entry_array_fd
< 0) {
854 r
= log_error_errno(errno
,
855 "Failed to create entry array file: %m");
859 if (le32toh(f
->header
->compatible_flags
) & ~HEADER_COMPATIBLE_SUPPORTED
) {
860 log_error("Cannot verify file with unknown extensions.");
865 for (i
= 0; i
< sizeof(f
->header
->reserved
); i
++)
866 if (f
->header
->reserved
[i
] != 0) {
867 error(offsetof(Header
, reserved
[i
]), "Reserved field is non-zero");
872 /* First iteration: we go through all objects, verify the
873 * superficial structure, headers, hashes. */
875 p
= le64toh(f
->header
->header_size
);
877 /* Early exit if there are no objects in the file, at all */
878 if (le64toh(f
->header
->tail_object_offset
) == 0)
882 draw_progress(scale_progress(0x7FFF, p
, le64toh(f
->header
->tail_object_offset
)), &last_usec
);
884 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, p
, &o
);
886 error(p
, "Invalid object");
890 if (p
> le64toh(f
->header
->tail_object_offset
)) {
891 error(offsetof(Header
, tail_object_offset
), "Invalid tail object pointer");
898 r
= journal_file_object_verify(f
, p
, o
);
900 error(p
, "Invalid object contents: %s", strerror(-r
));
904 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) &&
905 (o
->object
.flags
& OBJECT_COMPRESSED_LZ4
)) {
906 error(p
, "Objected with double compression");
911 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) && !JOURNAL_HEADER_COMPRESSED_XZ(f
->header
)) {
912 error(p
, "XZ compressed object in file without XZ compression");
917 if ((o
->object
.flags
& OBJECT_COMPRESSED_LZ4
) && !JOURNAL_HEADER_COMPRESSED_LZ4(f
->header
)) {
918 error(p
, "LZ4 compressed object in file without LZ4 compression");
923 switch (o
->object
.type
) {
926 r
= write_uint64(data_fd
, p
);
938 if (JOURNAL_HEADER_SEALED(f
->header
) && n_tags
<= 0) {
939 error(p
, "First entry before first tag");
944 r
= write_uint64(entry_fd
, p
);
948 if (le64toh(o
->entry
.realtime
) < last_tag_realtime
) {
949 error(p
, "Older entry after newer tag");
954 if (!entry_seqnum_set
&&
955 le64toh(o
->entry
.seqnum
) != le64toh(f
->header
->head_entry_seqnum
)) {
956 error(p
, "Head entry sequence number incorrect");
961 if (entry_seqnum_set
&&
962 entry_seqnum
>= le64toh(o
->entry
.seqnum
)) {
963 error(p
, "Entry sequence number out of synchronization");
968 entry_seqnum
= le64toh(o
->entry
.seqnum
);
969 entry_seqnum_set
= true;
971 if (entry_monotonic_set
&&
972 sd_id128_equal(entry_boot_id
, o
->entry
.boot_id
) &&
973 entry_monotonic
> le64toh(o
->entry
.monotonic
)) {
974 error(p
, "Entry timestamp out of synchronization");
979 entry_monotonic
= le64toh(o
->entry
.monotonic
);
980 entry_boot_id
= o
->entry
.boot_id
;
981 entry_monotonic_set
= true;
983 if (!entry_realtime_set
&&
984 le64toh(o
->entry
.realtime
) != le64toh(f
->header
->head_entry_realtime
)) {
985 error(p
, "Head entry realtime timestamp incorrect");
990 entry_realtime
= le64toh(o
->entry
.realtime
);
991 entry_realtime_set
= true;
996 case OBJECT_DATA_HASH_TABLE
:
997 if (n_data_hash_tables
> 1) {
998 error(p
, "More than one data hash table");
1003 if (le64toh(f
->header
->data_hash_table_offset
) != p
+ offsetof(HashTableObject
, items
) ||
1004 le64toh(f
->header
->data_hash_table_size
) != le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) {
1005 error(p
, "header fields for data hash table invalid");
1010 n_data_hash_tables
++;
1013 case OBJECT_FIELD_HASH_TABLE
:
1014 if (n_field_hash_tables
> 1) {
1015 error(p
, "More than one field hash table");
1020 if (le64toh(f
->header
->field_hash_table_offset
) != p
+ offsetof(HashTableObject
, items
) ||
1021 le64toh(f
->header
->field_hash_table_size
) != le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) {
1022 error(p
, "Header fields for field hash table invalid");
1027 n_field_hash_tables
++;
1030 case OBJECT_ENTRY_ARRAY
:
1031 r
= write_uint64(entry_array_fd
, p
);
1035 if (p
== le64toh(f
->header
->entry_array_offset
)) {
1036 if (found_main_entry_array
) {
1037 error(p
, "More than one main entry array");
1042 found_main_entry_array
= true;
1049 if (!JOURNAL_HEADER_SEALED(f
->header
)) {
1050 error(p
, "Tag object in file without sealing");
1055 if (le64toh(o
->tag
.seqnum
) != n_tags
+ 1) {
1056 error(p
, "Tag sequence number out of synchronization");
1061 if (le64toh(o
->tag
.epoch
) < last_epoch
) {
1062 error(p
, "Epoch sequence out of synchronization");
1071 debug(p
, "Checking tag %"PRIu64
"...", le64toh(o
->tag
.seqnum
));
1073 rt
= f
->fss_start_usec
+ o
->tag
.epoch
* f
->fss_interval_usec
;
1074 if (entry_realtime_set
&& entry_realtime
>= rt
+ f
->fss_interval_usec
) {
1075 error(p
, "tag/entry realtime timestamp out of synchronization");
1080 /* OK, now we know the epoch. So let's now set
1081 * it, and calculate the HMAC for everything
1082 * since the last tag. */
1083 r
= journal_file_fsprg_seek(f
, le64toh(o
->tag
.epoch
));
1087 r
= journal_file_hmac_start(f
);
1091 if (last_tag
== 0) {
1092 r
= journal_file_hmac_put_header(f
);
1096 q
= le64toh(f
->header
->header_size
);
1101 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, q
, &o
);
1105 r
= journal_file_hmac_put_object(f
, OBJECT_UNUSED
, o
, q
);
1109 q
= q
+ ALIGN64(le64toh(o
->object
.size
));
1112 /* Position might have changed, let's reposition things */
1113 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, p
, &o
);
1117 if (memcmp(o
->tag
.tag
, gcry_md_read(f
->hmac
, 0), TAG_LENGTH
) != 0) {
1118 error(p
, "Tag failed verification");
1123 f
->hmac_running
= false;
1124 last_tag_realtime
= rt
;
1125 last_sealed_realtime
= entry_realtime
;
1128 last_tag
= p
+ ALIGN64(le64toh(o
->object
.size
));
1131 last_epoch
= le64toh(o
->tag
.epoch
);
1140 if (p
== le64toh(f
->header
->tail_object_offset
)) {
1145 p
= p
+ ALIGN64(le64toh(o
->object
.size
));
1148 if (!found_last
&& le64toh(f
->header
->tail_object_offset
) != 0) {
1149 error(le64toh(f
->header
->tail_object_offset
), "Tail object pointer dead");
1154 if (n_objects
!= le64toh(f
->header
->n_objects
)) {
1155 error(offsetof(Header
, n_objects
), "Object number mismatch");
1160 if (n_entries
!= le64toh(f
->header
->n_entries
)) {
1161 error(offsetof(Header
, n_entries
), "Entry number mismatch");
1166 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_data
) &&
1167 n_data
!= le64toh(f
->header
->n_data
)) {
1168 error(offsetof(Header
, n_data
), "Data number mismatch");
1173 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_fields
) &&
1174 n_fields
!= le64toh(f
->header
->n_fields
)) {
1175 error(offsetof(Header
, n_fields
), "Field number mismatch");
1180 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_tags
) &&
1181 n_tags
!= le64toh(f
->header
->n_tags
)) {
1182 error(offsetof(Header
, n_tags
), "Tag number mismatch");
1187 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_entry_arrays
) &&
1188 n_entry_arrays
!= le64toh(f
->header
->n_entry_arrays
)) {
1189 error(offsetof(Header
, n_entry_arrays
), "Entry array number mismatch");
1194 if (!found_main_entry_array
&& le64toh(f
->header
->entry_array_offset
) != 0) {
1195 error(0, "Missing entry array");
1200 if (entry_seqnum_set
&&
1201 entry_seqnum
!= le64toh(f
->header
->tail_entry_seqnum
)) {
1202 error(offsetof(Header
, tail_entry_seqnum
), "Invalid tail seqnum");
1207 if (entry_monotonic_set
&&
1208 (!sd_id128_equal(entry_boot_id
, f
->header
->boot_id
) ||
1209 entry_monotonic
!= le64toh(f
->header
->tail_entry_monotonic
))) {
1210 error(0, "Invalid tail monotonic timestamp");
1215 if (entry_realtime_set
&& entry_realtime
!= le64toh(f
->header
->tail_entry_realtime
)) {
1216 error(0, "Invalid tail realtime timestamp");
1221 /* Second iteration: we follow all objects referenced from the
1222 * two entry points: the object hash table and the entry
1223 * array. We also check that everything referenced (directly
1224 * or indirectly) in the data hash table also exists in the
1225 * entry array, and vice versa. Note that we do not care for
1226 * unreferenced objects. We only care that everything that is
1227 * referenced is consistent. */
1229 r
= verify_entry_array(f
,
1231 entry_fd
, n_entries
,
1232 entry_array_fd
, n_entry_arrays
,
1238 r
= verify_hash_table(f
,
1240 entry_fd
, n_entries
,
1241 entry_array_fd
, n_entry_arrays
,
1250 mmap_cache_close_fd(f
->mmap
, data_fd
);
1251 mmap_cache_close_fd(f
->mmap
, entry_fd
);
1252 mmap_cache_close_fd(f
->mmap
, entry_array_fd
);
1254 safe_close(data_fd
);
1255 safe_close(entry_fd
);
1256 safe_close(entry_array_fd
);
1258 if (first_contained
)
1259 *first_contained
= le64toh(f
->header
->head_entry_realtime
);
1261 *last_validated
= last_sealed_realtime
;
1263 *last_contained
= le64toh(f
->header
->tail_entry_realtime
);
1271 log_error("File corruption detected at %s:"OFSfmt
" (of %llu bytes, %"PRIu64
"%%).",
1274 (unsigned long long) f
->last_stat
.st_size
,
1275 100 * p
/ f
->last_stat
.st_size
);
1278 mmap_cache_close_fd(f
->mmap
, data_fd
);
1279 safe_close(data_fd
);
1282 if (entry_fd
>= 0) {
1283 mmap_cache_close_fd(f
->mmap
, entry_fd
);
1284 safe_close(entry_fd
);
1287 if (entry_array_fd
>= 0) {
1288 mmap_cache_close_fd(f
->mmap
, entry_array_fd
);
1289 safe_close(entry_array_fd
);