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/>.
30 #include "journal-authenticate.h"
31 #include "journal-def.h"
32 #include "journal-file.h"
33 #include "journal-verify.h"
36 #include "terminal-util.h"
39 static void draw_progress(uint64_t p
, usec_t
*last_usec
) {
46 z
= now(CLOCK_MONOTONIC
);
49 if (x
!= 0 && x
+ 40 * USEC_PER_MSEC
> z
)
54 n
= (3 * columns()) / 4;
55 j
= (n
* (unsigned) p
) / 65535ULL;
58 fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN
, stdout
);
60 for (i
= 0; i
< j
; i
++)
61 fputs("\xe2\x96\x88", stdout
);
63 fputs(ANSI_NORMAL
, stdout
);
65 for (i
= 0; i
< k
; i
++)
66 fputs("\xe2\x96\x91", stdout
);
68 printf(" %3"PRIu64
"%%", 100U * p
/ 65535U);
70 fputs("\r\x1B[?25h", stdout
);
74 static uint64_t scale_progress(uint64_t scale
, uint64_t p
, uint64_t m
) {
76 /* Calculates scale * p / m, but handles m == 0 safely, and saturates */
84 static void flush_progress(void) {
90 n
= (3 * columns()) / 4;
94 for (i
= 0; i
< n
+ 5; i
++)
101 #define debug(_offset, _fmt, ...) do{ \
103 log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
106 #define warning(_offset, _fmt, ...) do{ \
108 log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
111 #define error(_offset, _fmt, ...) do{ \
113 log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
116 static int journal_file_object_verify(JournalFile
*f
, uint64_t offset
, Object
*o
) {
123 /* This does various superficial tests about the length an
124 * possible field values. It does not follow any references to
127 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) &&
128 o
->object
.type
!= OBJECT_DATA
) {
129 error(offset
, "Found compressed object that isn't of type DATA, which is not allowed.");
133 switch (o
->object
.type
) {
139 if (le64toh(o
->data
.entry_offset
) == 0)
140 warning(offset
, "Unused data (entry_offset==0)");
142 if ((le64toh(o
->data
.entry_offset
) == 0) ^ (le64toh(o
->data
.n_entries
) == 0)) {
143 error(offset
, "Bad n_entries: %"PRIu64
, o
->data
.n_entries
);
147 if (le64toh(o
->object
.size
) - offsetof(DataObject
, payload
) <= 0) {
148 error(offset
, "Bad object size (<= %zu): %"PRIu64
,
149 offsetof(DataObject
, payload
),
150 le64toh(o
->object
.size
));
154 h1
= le64toh(o
->data
.hash
);
156 compression
= o
->object
.flags
& OBJECT_COMPRESSION_MASK
;
158 _cleanup_free_
void *b
= NULL
;
159 size_t alloc
= 0, b_size
;
161 r
= decompress_blob(compression
,
163 le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
),
164 &b
, &alloc
, &b_size
, 0);
166 error(offset
, "%s decompression failed: %s",
167 object_compressed_to_string(compression
), strerror(-r
));
171 h2
= hash64(b
, b_size
);
173 h2
= hash64(o
->data
.payload
, le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
));
176 error(offset
, "Invalid hash (%08"PRIx64
" vs. %08"PRIx64
, h1
, h2
);
180 if (!VALID64(o
->data
.next_hash_offset
) ||
181 !VALID64(o
->data
.next_field_offset
) ||
182 !VALID64(o
->data
.entry_offset
) ||
183 !VALID64(o
->data
.entry_array_offset
)) {
184 error(offset
, "Invalid offset (next_hash_offset="OFSfmt
", next_field_offset="OFSfmt
", entry_offset="OFSfmt
", entry_array_offset="OFSfmt
,
185 o
->data
.next_hash_offset
,
186 o
->data
.next_field_offset
,
187 o
->data
.entry_offset
,
188 o
->data
.entry_array_offset
);
196 if (le64toh(o
->object
.size
) - offsetof(FieldObject
, payload
) <= 0) {
198 "Bad field size (<= %zu): %"PRIu64
,
199 offsetof(FieldObject
, payload
),
200 le64toh(o
->object
.size
));
204 if (!VALID64(o
->field
.next_hash_offset
) ||
205 !VALID64(o
->field
.head_data_offset
)) {
207 "Invalid offset (next_hash_offset="OFSfmt
", head_data_offset="OFSfmt
,
208 o
->field
.next_hash_offset
,
209 o
->field
.head_data_offset
);
215 if ((le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) % sizeof(EntryItem
) != 0) {
217 "Bad entry size (<= %zu): %"PRIu64
,
218 offsetof(EntryObject
, items
),
219 le64toh(o
->object
.size
));
223 if ((le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) / sizeof(EntryItem
) <= 0) {
225 "Invalid number items in entry: %"PRIu64
,
226 (le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) / sizeof(EntryItem
));
230 if (le64toh(o
->entry
.seqnum
) <= 0) {
232 "Invalid entry seqnum: %"PRIx64
,
233 le64toh(o
->entry
.seqnum
));
237 if (!VALID_REALTIME(le64toh(o
->entry
.realtime
))) {
239 "Invalid entry realtime timestamp: %"PRIu64
,
240 le64toh(o
->entry
.realtime
));
244 if (!VALID_MONOTONIC(le64toh(o
->entry
.monotonic
))) {
246 "Invalid entry monotonic timestamp: %"PRIu64
,
247 le64toh(o
->entry
.monotonic
));
251 for (i
= 0; i
< journal_file_entry_n_items(o
); i
++) {
252 if (o
->entry
.items
[i
].object_offset
== 0 ||
253 !VALID64(o
->entry
.items
[i
].object_offset
)) {
255 "Invalid entry item (%"PRIu64
"/%"PRIu64
" offset: "OFSfmt
,
256 i
, journal_file_entry_n_items(o
),
257 o
->entry
.items
[i
].object_offset
);
264 case OBJECT_DATA_HASH_TABLE
:
265 case OBJECT_FIELD_HASH_TABLE
:
266 if ((le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) % sizeof(HashItem
) != 0 ||
267 (le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) / sizeof(HashItem
) <= 0) {
269 "Invalid %s hash table size: %"PRIu64
,
270 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
271 le64toh(o
->object
.size
));
275 for (i
= 0; i
< journal_file_hash_table_n_items(o
); i
++) {
276 if (o
->hash_table
.items
[i
].head_hash_offset
!= 0 &&
277 !VALID64(le64toh(o
->hash_table
.items
[i
].head_hash_offset
))) {
279 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
") head_hash_offset: "OFSfmt
,
280 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
281 i
, journal_file_hash_table_n_items(o
),
282 le64toh(o
->hash_table
.items
[i
].head_hash_offset
));
285 if (o
->hash_table
.items
[i
].tail_hash_offset
!= 0 &&
286 !VALID64(le64toh(o
->hash_table
.items
[i
].tail_hash_offset
))) {
288 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
") tail_hash_offset: "OFSfmt
,
289 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
290 i
, journal_file_hash_table_n_items(o
),
291 le64toh(o
->hash_table
.items
[i
].tail_hash_offset
));
295 if ((o
->hash_table
.items
[i
].head_hash_offset
!= 0) !=
296 (o
->hash_table
.items
[i
].tail_hash_offset
!= 0)) {
298 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
"): head_hash_offset="OFSfmt
" tail_hash_offset="OFSfmt
,
299 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
300 i
, journal_file_hash_table_n_items(o
),
301 le64toh(o
->hash_table
.items
[i
].head_hash_offset
),
302 le64toh(o
->hash_table
.items
[i
].tail_hash_offset
));
309 case OBJECT_ENTRY_ARRAY
:
310 if ((le64toh(o
->object
.size
) - offsetof(EntryArrayObject
, items
)) % sizeof(le64_t
) != 0 ||
311 (le64toh(o
->object
.size
) - offsetof(EntryArrayObject
, items
)) / sizeof(le64_t
) <= 0) {
313 "Invalid object entry array size: %"PRIu64
,
314 le64toh(o
->object
.size
));
318 if (!VALID64(o
->entry_array
.next_entry_array_offset
)) {
320 "Invalid object entry array next_entry_array_offset: "OFSfmt
,
321 o
->entry_array
.next_entry_array_offset
);
325 for (i
= 0; i
< journal_file_entry_array_n_items(o
); i
++)
326 if (le64toh(o
->entry_array
.items
[i
]) != 0 &&
327 !VALID64(le64toh(o
->entry_array
.items
[i
]))) {
329 "Invalid object entry array item (%"PRIu64
"/%"PRIu64
"): "OFSfmt
,
330 i
, journal_file_entry_array_n_items(o
),
331 le64toh(o
->entry_array
.items
[i
]));
338 if (le64toh(o
->object
.size
) != sizeof(TagObject
)) {
340 "Invalid object tag size: %"PRIu64
,
341 le64toh(o
->object
.size
));
345 if (!VALID_EPOCH(o
->tag
.epoch
)) {
347 "Invalid object tag epoch: %"PRIu64
,
358 static int write_uint64(int fd
, uint64_t p
) {
361 k
= write(fd
, &p
, sizeof(p
));
370 static int contains_uint64(MMapCache
*m
, int fd
, uint64_t n
, uint64_t p
) {
385 r
= mmap_cache_get(m
, fd
, PROT_READ
|PROT_WRITE
, 0, false, c
* sizeof(uint64_t), sizeof(uint64_t), NULL
, (void **) &z
);
404 static int entry_points_to_data(
417 assert(entry_fd
>= 0);
419 if (!contains_uint64(f
->mmap
, entry_fd
, n_entries
, entry_p
)) {
420 error(data_p
, "Data object references invalid entry at "OFSfmt
, entry_p
);
424 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, entry_p
, &o
);
428 n
= journal_file_entry_n_items(o
);
429 for (i
= 0; i
< n
; i
++)
430 if (le64toh(o
->entry
.items
[i
].object_offset
) == data_p
) {
436 error(entry_p
, "Data object at "OFSfmt
" not referenced by linked entry", data_p
);
440 /* Check if this entry is also in main entry array. Since the
441 * main entry array has already been verified we can rely on
442 * its consistency. */
445 n
= le64toh(f
->header
->n_entries
);
446 a
= le64toh(f
->header
->entry_array_offset
);
451 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
455 m
= journal_file_entry_array_n_items(o
);
458 if (entry_p
<= le64toh(o
->entry_array
.items
[u
-1])) {
467 if (le64toh(o
->entry_array
.items
[z
]) == entry_p
)
473 if (entry_p
< le64toh(o
->entry_array
.items
[z
]))
479 error(entry_p
, "Entry object doesn't exist in main entry array");
484 a
= le64toh(o
->entry_array
.next_entry_array_offset
);
490 static int verify_data(
492 Object
*o
, uint64_t p
,
493 int entry_fd
, uint64_t n_entries
,
494 int entry_array_fd
, uint64_t n_entry_arrays
) {
496 uint64_t i
, n
, a
, last
, q
;
501 assert(entry_fd
>= 0);
502 assert(entry_array_fd
>= 0);
504 n
= le64toh(o
->data
.n_entries
);
505 a
= le64toh(o
->data
.entry_array_offset
);
507 /* Entry array means at least two objects */
509 error(p
, "Entry array present (entry_array_offset="OFSfmt
", but n_entries=%"PRIu64
")", a
, n
);
516 /* We already checked that earlier */
517 assert(o
->data
.entry_offset
);
519 last
= q
= le64toh(o
->data
.entry_offset
);
520 r
= entry_points_to_data(f
, entry_fd
, n_entries
, q
, p
);
529 error(p
, "Array chain too short");
533 if (!contains_uint64(f
->mmap
, entry_array_fd
, n_entry_arrays
, a
)) {
534 error(p
, "Invalid array offset "OFSfmt
, a
);
538 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
542 next
= le64toh(o
->entry_array
.next_entry_array_offset
);
543 if (next
!= 0 && next
<= a
) {
544 error(p
, "Array chain has cycle (jumps back from "OFSfmt
" to "OFSfmt
")", a
, next
);
548 m
= journal_file_entry_array_n_items(o
);
549 for (j
= 0; i
< n
&& j
< m
; i
++, j
++) {
551 q
= le64toh(o
->entry_array
.items
[j
]);
553 error(p
, "Data object's entry array not sorted");
558 r
= entry_points_to_data(f
, entry_fd
, n_entries
, q
, p
);
562 /* Pointer might have moved, reposition */
563 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
574 static int verify_hash_table(
576 int data_fd
, uint64_t n_data
,
577 int entry_fd
, uint64_t n_entries
,
578 int entry_array_fd
, uint64_t n_entry_arrays
,
580 bool show_progress
) {
586 assert(data_fd
>= 0);
587 assert(entry_fd
>= 0);
588 assert(entry_array_fd
>= 0);
591 n
= le64toh(f
->header
->data_hash_table_size
) / sizeof(HashItem
);
595 r
= journal_file_map_data_hash_table(f
);
597 return log_error_errno(r
, "Failed to map data hash table: %m");
599 for (i
= 0; i
< n
; i
++) {
600 uint64_t last
= 0, p
;
603 draw_progress(0xC000 + scale_progress(0x3FFF, i
, n
), last_usec
);
605 p
= le64toh(f
->data_hash_table
[i
].head_hash_offset
);
610 if (!contains_uint64(f
->mmap
, data_fd
, n_data
, p
)) {
611 error(p
, "Invalid data object at hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
615 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
619 next
= le64toh(o
->data
.next_hash_offset
);
620 if (next
!= 0 && next
<= p
) {
621 error(p
, "Hash chain has a cycle in hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
625 if (le64toh(o
->data
.hash
) % n
!= i
) {
626 error(p
, "Hash value mismatch in hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
630 r
= verify_data(f
, o
, p
, entry_fd
, n_entries
, entry_array_fd
, n_entry_arrays
);
638 if (last
!= le64toh(f
->data_hash_table
[i
].tail_hash_offset
)) {
639 error(p
, "Tail hash pointer mismatch in hash table");
647 static int data_object_in_hash_table(JournalFile
*f
, uint64_t hash
, uint64_t p
) {
652 n
= le64toh(f
->header
->data_hash_table_size
) / sizeof(HashItem
);
656 r
= journal_file_map_data_hash_table(f
);
658 return log_error_errno(r
, "Failed to map data hash table: %m");
662 q
= le64toh(f
->data_hash_table
[h
].head_hash_offset
);
669 r
= journal_file_move_to_object(f
, OBJECT_DATA
, q
, &o
);
673 q
= le64toh(o
->data
.next_hash_offset
);
679 static int verify_entry(
681 Object
*o
, uint64_t p
,
682 int data_fd
, uint64_t n_data
) {
689 assert(data_fd
>= 0);
691 n
= journal_file_entry_n_items(o
);
692 for (i
= 0; i
< n
; i
++) {
696 q
= le64toh(o
->entry
.items
[i
].object_offset
);
697 h
= le64toh(o
->entry
.items
[i
].hash
);
699 if (!contains_uint64(f
->mmap
, data_fd
, n_data
, q
)) {
700 error(p
, "Invalid data object of entry");
704 r
= journal_file_move_to_object(f
, OBJECT_DATA
, q
, &u
);
708 if (le64toh(u
->data
.hash
) != h
) {
709 error(p
, "Hash mismatch for data object of entry");
713 r
= data_object_in_hash_table(f
, h
, q
);
717 error(p
, "Data object missing from hash table");
725 static int verify_entry_array(
727 int data_fd
, uint64_t n_data
,
728 int entry_fd
, uint64_t n_entries
,
729 int entry_array_fd
, uint64_t n_entry_arrays
,
731 bool show_progress
) {
733 uint64_t i
= 0, a
, n
, last
= 0;
737 assert(data_fd
>= 0);
738 assert(entry_fd
>= 0);
739 assert(entry_array_fd
>= 0);
742 n
= le64toh(f
->header
->n_entries
);
743 a
= le64toh(f
->header
->entry_array_offset
);
749 draw_progress(0x8000 + scale_progress(0x3FFF, i
, n
), last_usec
);
752 error(a
, "Array chain too short at %"PRIu64
" of %"PRIu64
, i
, n
);
756 if (!contains_uint64(f
->mmap
, entry_array_fd
, n_entry_arrays
, a
)) {
757 error(a
, "Invalid array %"PRIu64
" of %"PRIu64
, i
, n
);
761 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
765 next
= le64toh(o
->entry_array
.next_entry_array_offset
);
766 if (next
!= 0 && next
<= a
) {
767 error(a
, "Array chain has cycle at %"PRIu64
" of %"PRIu64
" (jumps back from to "OFSfmt
")", i
, n
, next
);
771 m
= journal_file_entry_array_n_items(o
);
772 for (j
= 0; i
< n
&& j
< m
; i
++, j
++) {
775 p
= le64toh(o
->entry_array
.items
[j
]);
777 error(a
, "Entry array not sorted at %"PRIu64
" of %"PRIu64
, i
, n
);
782 if (!contains_uint64(f
->mmap
, entry_fd
, n_entries
, p
)) {
783 error(a
, "Invalid array entry at %"PRIu64
" of %"PRIu64
, i
, n
);
787 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, p
, &o
);
791 r
= verify_entry(f
, o
, p
, data_fd
, n_data
);
795 /* Pointer might have moved, reposition */
796 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
807 int journal_file_verify(
810 usec_t
*first_contained
, usec_t
*last_validated
, usec_t
*last_contained
,
811 bool show_progress
) {
814 uint64_t p
= 0, last_epoch
= 0, last_tag_realtime
= 0, last_sealed_realtime
= 0;
816 uint64_t entry_seqnum
= 0, entry_monotonic
= 0, entry_realtime
= 0;
817 sd_id128_t entry_boot_id
;
818 bool entry_seqnum_set
= false, entry_monotonic_set
= false, entry_realtime_set
= false, found_main_entry_array
= false;
819 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;
820 usec_t last_usec
= 0;
821 int data_fd
= -1, entry_fd
= -1, entry_array_fd
= -1;
823 bool found_last
= false;
825 uint64_t last_tag
= 0;
831 r
= journal_file_parse_verification_key(f
, key
);
833 log_error("Failed to parse seed.");
842 data_fd
= open_tmpfile("/var/tmp", O_RDWR
| O_CLOEXEC
);
844 r
= log_error_errno(errno
, "Failed to create data file: %m");
848 entry_fd
= open_tmpfile("/var/tmp", O_RDWR
| O_CLOEXEC
);
850 r
= log_error_errno(errno
, "Failed to create entry file: %m");
854 entry_array_fd
= open_tmpfile("/var/tmp", O_RDWR
| O_CLOEXEC
);
855 if (entry_array_fd
< 0) {
856 r
= log_error_errno(errno
,
857 "Failed to create entry array file: %m");
861 if (le32toh(f
->header
->compatible_flags
) & ~HEADER_COMPATIBLE_SUPPORTED
) {
862 log_error("Cannot verify file with unknown extensions.");
867 for (i
= 0; i
< sizeof(f
->header
->reserved
); i
++)
868 if (f
->header
->reserved
[i
] != 0) {
869 error(offsetof(Header
, reserved
[i
]), "Reserved field is non-zero");
874 /* First iteration: we go through all objects, verify the
875 * superficial structure, headers, hashes. */
877 p
= le64toh(f
->header
->header_size
);
879 /* Early exit if there are no objects in the file, at all */
880 if (le64toh(f
->header
->tail_object_offset
) == 0)
884 draw_progress(scale_progress(0x7FFF, p
, le64toh(f
->header
->tail_object_offset
)), &last_usec
);
886 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, p
, &o
);
888 error(p
, "Invalid object");
892 if (p
> le64toh(f
->header
->tail_object_offset
)) {
893 error(offsetof(Header
, tail_object_offset
), "Invalid tail object pointer");
900 r
= journal_file_object_verify(f
, p
, o
);
902 error(p
, "Invalid object contents: %s", strerror(-r
));
906 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) &&
907 (o
->object
.flags
& OBJECT_COMPRESSED_LZ4
)) {
908 error(p
, "Objected with double compression");
913 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) && !JOURNAL_HEADER_COMPRESSED_XZ(f
->header
)) {
914 error(p
, "XZ compressed object in file without XZ compression");
919 if ((o
->object
.flags
& OBJECT_COMPRESSED_LZ4
) && !JOURNAL_HEADER_COMPRESSED_LZ4(f
->header
)) {
920 error(p
, "LZ4 compressed object in file without LZ4 compression");
925 switch (o
->object
.type
) {
928 r
= write_uint64(data_fd
, p
);
940 if (JOURNAL_HEADER_SEALED(f
->header
) && n_tags
<= 0) {
941 error(p
, "First entry before first tag");
946 r
= write_uint64(entry_fd
, p
);
950 if (le64toh(o
->entry
.realtime
) < last_tag_realtime
) {
951 error(p
, "Older entry after newer tag");
956 if (!entry_seqnum_set
&&
957 le64toh(o
->entry
.seqnum
) != le64toh(f
->header
->head_entry_seqnum
)) {
958 error(p
, "Head entry sequence number incorrect");
963 if (entry_seqnum_set
&&
964 entry_seqnum
>= le64toh(o
->entry
.seqnum
)) {
965 error(p
, "Entry sequence number out of synchronization");
970 entry_seqnum
= le64toh(o
->entry
.seqnum
);
971 entry_seqnum_set
= true;
973 if (entry_monotonic_set
&&
974 sd_id128_equal(entry_boot_id
, o
->entry
.boot_id
) &&
975 entry_monotonic
> le64toh(o
->entry
.monotonic
)) {
976 error(p
, "Entry timestamp out of synchronization");
981 entry_monotonic
= le64toh(o
->entry
.monotonic
);
982 entry_boot_id
= o
->entry
.boot_id
;
983 entry_monotonic_set
= true;
985 if (!entry_realtime_set
&&
986 le64toh(o
->entry
.realtime
) != le64toh(f
->header
->head_entry_realtime
)) {
987 error(p
, "Head entry realtime timestamp incorrect");
992 entry_realtime
= le64toh(o
->entry
.realtime
);
993 entry_realtime_set
= true;
998 case OBJECT_DATA_HASH_TABLE
:
999 if (n_data_hash_tables
> 1) {
1000 error(p
, "More than one data hash table");
1005 if (le64toh(f
->header
->data_hash_table_offset
) != p
+ offsetof(HashTableObject
, items
) ||
1006 le64toh(f
->header
->data_hash_table_size
) != le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) {
1007 error(p
, "header fields for data hash table invalid");
1012 n_data_hash_tables
++;
1015 case OBJECT_FIELD_HASH_TABLE
:
1016 if (n_field_hash_tables
> 1) {
1017 error(p
, "More than one field hash table");
1022 if (le64toh(f
->header
->field_hash_table_offset
) != p
+ offsetof(HashTableObject
, items
) ||
1023 le64toh(f
->header
->field_hash_table_size
) != le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) {
1024 error(p
, "Header fields for field hash table invalid");
1029 n_field_hash_tables
++;
1032 case OBJECT_ENTRY_ARRAY
:
1033 r
= write_uint64(entry_array_fd
, p
);
1037 if (p
== le64toh(f
->header
->entry_array_offset
)) {
1038 if (found_main_entry_array
) {
1039 error(p
, "More than one main entry array");
1044 found_main_entry_array
= true;
1051 if (!JOURNAL_HEADER_SEALED(f
->header
)) {
1052 error(p
, "Tag object in file without sealing");
1057 if (le64toh(o
->tag
.seqnum
) != n_tags
+ 1) {
1058 error(p
, "Tag sequence number out of synchronization");
1063 if (le64toh(o
->tag
.epoch
) < last_epoch
) {
1064 error(p
, "Epoch sequence out of synchronization");
1073 debug(p
, "Checking tag %"PRIu64
"...", le64toh(o
->tag
.seqnum
));
1075 rt
= f
->fss_start_usec
+ o
->tag
.epoch
* f
->fss_interval_usec
;
1076 if (entry_realtime_set
&& entry_realtime
>= rt
+ f
->fss_interval_usec
) {
1077 error(p
, "tag/entry realtime timestamp out of synchronization");
1082 /* OK, now we know the epoch. So let's now set
1083 * it, and calculate the HMAC for everything
1084 * since the last tag. */
1085 r
= journal_file_fsprg_seek(f
, le64toh(o
->tag
.epoch
));
1089 r
= journal_file_hmac_start(f
);
1093 if (last_tag
== 0) {
1094 r
= journal_file_hmac_put_header(f
);
1098 q
= le64toh(f
->header
->header_size
);
1103 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, q
, &o
);
1107 r
= journal_file_hmac_put_object(f
, OBJECT_UNUSED
, o
, q
);
1111 q
= q
+ ALIGN64(le64toh(o
->object
.size
));
1114 /* Position might have changed, let's reposition things */
1115 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, p
, &o
);
1119 if (memcmp(o
->tag
.tag
, gcry_md_read(f
->hmac
, 0), TAG_LENGTH
) != 0) {
1120 error(p
, "Tag failed verification");
1125 f
->hmac_running
= false;
1126 last_tag_realtime
= rt
;
1127 last_sealed_realtime
= entry_realtime
;
1130 last_tag
= p
+ ALIGN64(le64toh(o
->object
.size
));
1133 last_epoch
= le64toh(o
->tag
.epoch
);
1142 if (p
== le64toh(f
->header
->tail_object_offset
)) {
1147 p
= p
+ ALIGN64(le64toh(o
->object
.size
));
1150 if (!found_last
&& le64toh(f
->header
->tail_object_offset
) != 0) {
1151 error(le64toh(f
->header
->tail_object_offset
), "Tail object pointer dead");
1156 if (n_objects
!= le64toh(f
->header
->n_objects
)) {
1157 error(offsetof(Header
, n_objects
), "Object number mismatch");
1162 if (n_entries
!= le64toh(f
->header
->n_entries
)) {
1163 error(offsetof(Header
, n_entries
), "Entry number mismatch");
1168 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_data
) &&
1169 n_data
!= le64toh(f
->header
->n_data
)) {
1170 error(offsetof(Header
, n_data
), "Data number mismatch");
1175 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_fields
) &&
1176 n_fields
!= le64toh(f
->header
->n_fields
)) {
1177 error(offsetof(Header
, n_fields
), "Field number mismatch");
1182 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_tags
) &&
1183 n_tags
!= le64toh(f
->header
->n_tags
)) {
1184 error(offsetof(Header
, n_tags
), "Tag number mismatch");
1189 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_entry_arrays
) &&
1190 n_entry_arrays
!= le64toh(f
->header
->n_entry_arrays
)) {
1191 error(offsetof(Header
, n_entry_arrays
), "Entry array number mismatch");
1196 if (!found_main_entry_array
&& le64toh(f
->header
->entry_array_offset
) != 0) {
1197 error(0, "Missing entry array");
1202 if (entry_seqnum_set
&&
1203 entry_seqnum
!= le64toh(f
->header
->tail_entry_seqnum
)) {
1204 error(offsetof(Header
, tail_entry_seqnum
), "Invalid tail seqnum");
1209 if (entry_monotonic_set
&&
1210 (!sd_id128_equal(entry_boot_id
, f
->header
->boot_id
) ||
1211 entry_monotonic
!= le64toh(f
->header
->tail_entry_monotonic
))) {
1212 error(0, "Invalid tail monotonic timestamp");
1217 if (entry_realtime_set
&& entry_realtime
!= le64toh(f
->header
->tail_entry_realtime
)) {
1218 error(0, "Invalid tail realtime timestamp");
1223 /* Second iteration: we follow all objects referenced from the
1224 * two entry points: the object hash table and the entry
1225 * array. We also check that everything referenced (directly
1226 * or indirectly) in the data hash table also exists in the
1227 * entry array, and vice versa. Note that we do not care for
1228 * unreferenced objects. We only care that everything that is
1229 * referenced is consistent. */
1231 r
= verify_entry_array(f
,
1233 entry_fd
, n_entries
,
1234 entry_array_fd
, n_entry_arrays
,
1240 r
= verify_hash_table(f
,
1242 entry_fd
, n_entries
,
1243 entry_array_fd
, n_entry_arrays
,
1252 mmap_cache_close_fd(f
->mmap
, data_fd
);
1253 mmap_cache_close_fd(f
->mmap
, entry_fd
);
1254 mmap_cache_close_fd(f
->mmap
, entry_array_fd
);
1256 safe_close(data_fd
);
1257 safe_close(entry_fd
);
1258 safe_close(entry_array_fd
);
1260 if (first_contained
)
1261 *first_contained
= le64toh(f
->header
->head_entry_realtime
);
1263 *last_validated
= last_sealed_realtime
;
1265 *last_contained
= le64toh(f
->header
->tail_entry_realtime
);
1273 log_error("File corruption detected at %s:"OFSfmt
" (of %llu bytes, %"PRIu64
"%%).",
1276 (unsigned long long) f
->last_stat
.st_size
,
1277 100 * p
/ f
->last_stat
.st_size
);
1280 mmap_cache_close_fd(f
->mmap
, data_fd
);
1281 safe_close(data_fd
);
1284 if (entry_fd
>= 0) {
1285 mmap_cache_close_fd(f
->mmap
, entry_fd
);
1286 safe_close(entry_fd
);
1289 if (entry_array_fd
>= 0) {
1290 mmap_cache_close_fd(f
->mmap
, entry_array_fd
);
1291 safe_close(entry_array_fd
);