2 This file is part of systemd.
4 Copyright 2012 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include "alloc-util.h"
29 #include "journal-authenticate.h"
30 #include "journal-def.h"
31 #include "journal-file.h"
32 #include "journal-verify.h"
35 #include "terminal-util.h"
38 static void draw_progress(uint64_t p
, usec_t
*last_usec
) {
45 z
= now(CLOCK_MONOTONIC
);
48 if (x
!= 0 && x
+ 40 * USEC_PER_MSEC
> z
)
53 n
= (3 * columns()) / 4;
54 j
= (n
* (unsigned) p
) / 65535ULL;
57 fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN
, stdout
);
59 for (i
= 0; i
< j
; i
++)
60 fputs("\xe2\x96\x88", stdout
);
62 fputs(ANSI_NORMAL
, stdout
);
64 for (i
= 0; i
< k
; i
++)
65 fputs("\xe2\x96\x91", stdout
);
67 printf(" %3"PRIu64
"%%", 100U * p
/ 65535U);
69 fputs("\r\x1B[?25h", stdout
);
73 static uint64_t scale_progress(uint64_t scale
, uint64_t p
, uint64_t m
) {
75 /* Calculates scale * p / m, but handles m == 0 safely, and saturates */
83 static void flush_progress(void) {
89 n
= (3 * columns()) / 4;
93 for (i
= 0; i
< n
+ 5; i
++)
100 #define debug(_offset, _fmt, ...) do{ \
102 log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
105 #define warning(_offset, _fmt, ...) do{ \
107 log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
110 #define error(_offset, _fmt, ...) do{ \
112 log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
115 static int journal_file_object_verify(JournalFile
*f
, uint64_t offset
, Object
*o
) {
122 /* This does various superficial tests about the length an
123 * possible field values. It does not follow any references to
126 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) &&
127 o
->object
.type
!= OBJECT_DATA
) {
128 error(offset
, "Found compressed object that isn't of type DATA, which is not allowed.");
132 switch (o
->object
.type
) {
138 if (le64toh(o
->data
.entry_offset
) == 0)
139 warning(offset
, "Unused data (entry_offset==0)");
141 if ((le64toh(o
->data
.entry_offset
) == 0) ^ (le64toh(o
->data
.n_entries
) == 0)) {
142 error(offset
, "Bad n_entries: %"PRIu64
, o
->data
.n_entries
);
146 if (le64toh(o
->object
.size
) - offsetof(DataObject
, payload
) <= 0) {
147 error(offset
, "Bad object size (<= %zu): %"PRIu64
,
148 offsetof(DataObject
, payload
),
149 le64toh(o
->object
.size
));
153 h1
= le64toh(o
->data
.hash
);
155 compression
= o
->object
.flags
& OBJECT_COMPRESSION_MASK
;
157 _cleanup_free_
void *b
= NULL
;
158 size_t alloc
= 0, b_size
;
160 r
= decompress_blob(compression
,
162 le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
),
163 &b
, &alloc
, &b_size
, 0);
165 error(offset
, "%s decompression failed: %s",
166 object_compressed_to_string(compression
), strerror(-r
));
170 h2
= hash64(b
, b_size
);
172 h2
= hash64(o
->data
.payload
, le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
));
175 error(offset
, "Invalid hash (%08"PRIx64
" vs. %08"PRIx64
, h1
, h2
);
179 if (!VALID64(o
->data
.next_hash_offset
) ||
180 !VALID64(o
->data
.next_field_offset
) ||
181 !VALID64(o
->data
.entry_offset
) ||
182 !VALID64(o
->data
.entry_array_offset
)) {
183 error(offset
, "Invalid offset (next_hash_offset="OFSfmt
", next_field_offset="OFSfmt
", entry_offset="OFSfmt
", entry_array_offset="OFSfmt
,
184 o
->data
.next_hash_offset
,
185 o
->data
.next_field_offset
,
186 o
->data
.entry_offset
,
187 o
->data
.entry_array_offset
);
195 if (le64toh(o
->object
.size
) - offsetof(FieldObject
, payload
) <= 0) {
197 "Bad field size (<= %zu): %"PRIu64
,
198 offsetof(FieldObject
, payload
),
199 le64toh(o
->object
.size
));
203 if (!VALID64(o
->field
.next_hash_offset
) ||
204 !VALID64(o
->field
.head_data_offset
)) {
206 "Invalid offset (next_hash_offset="OFSfmt
", head_data_offset="OFSfmt
,
207 o
->field
.next_hash_offset
,
208 o
->field
.head_data_offset
);
214 if ((le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) % sizeof(EntryItem
) != 0) {
216 "Bad entry size (<= %zu): %"PRIu64
,
217 offsetof(EntryObject
, items
),
218 le64toh(o
->object
.size
));
222 if ((le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) / sizeof(EntryItem
) <= 0) {
224 "Invalid number items in entry: %"PRIu64
,
225 (le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) / sizeof(EntryItem
));
229 if (le64toh(o
->entry
.seqnum
) <= 0) {
231 "Invalid entry seqnum: %"PRIx64
,
232 le64toh(o
->entry
.seqnum
));
236 if (!VALID_REALTIME(le64toh(o
->entry
.realtime
))) {
238 "Invalid entry realtime timestamp: %"PRIu64
,
239 le64toh(o
->entry
.realtime
));
243 if (!VALID_MONOTONIC(le64toh(o
->entry
.monotonic
))) {
245 "Invalid entry monotonic timestamp: %"PRIu64
,
246 le64toh(o
->entry
.monotonic
));
250 for (i
= 0; i
< journal_file_entry_n_items(o
); i
++) {
251 if (o
->entry
.items
[i
].object_offset
== 0 ||
252 !VALID64(o
->entry
.items
[i
].object_offset
)) {
254 "Invalid entry item (%"PRIu64
"/%"PRIu64
" offset: "OFSfmt
,
255 i
, journal_file_entry_n_items(o
),
256 o
->entry
.items
[i
].object_offset
);
263 case OBJECT_DATA_HASH_TABLE
:
264 case OBJECT_FIELD_HASH_TABLE
:
265 if ((le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) % sizeof(HashItem
) != 0 ||
266 (le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) / sizeof(HashItem
) <= 0) {
268 "Invalid %s hash table size: %"PRIu64
,
269 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
270 le64toh(o
->object
.size
));
274 for (i
= 0; i
< journal_file_hash_table_n_items(o
); i
++) {
275 if (o
->hash_table
.items
[i
].head_hash_offset
!= 0 &&
276 !VALID64(le64toh(o
->hash_table
.items
[i
].head_hash_offset
))) {
278 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
") head_hash_offset: "OFSfmt
,
279 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
280 i
, journal_file_hash_table_n_items(o
),
281 le64toh(o
->hash_table
.items
[i
].head_hash_offset
));
284 if (o
->hash_table
.items
[i
].tail_hash_offset
!= 0 &&
285 !VALID64(le64toh(o
->hash_table
.items
[i
].tail_hash_offset
))) {
287 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
") tail_hash_offset: "OFSfmt
,
288 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
289 i
, journal_file_hash_table_n_items(o
),
290 le64toh(o
->hash_table
.items
[i
].tail_hash_offset
));
294 if ((o
->hash_table
.items
[i
].head_hash_offset
!= 0) !=
295 (o
->hash_table
.items
[i
].tail_hash_offset
!= 0)) {
297 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
"): head_hash_offset="OFSfmt
" tail_hash_offset="OFSfmt
,
298 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
299 i
, journal_file_hash_table_n_items(o
),
300 le64toh(o
->hash_table
.items
[i
].head_hash_offset
),
301 le64toh(o
->hash_table
.items
[i
].tail_hash_offset
));
308 case OBJECT_ENTRY_ARRAY
:
309 if ((le64toh(o
->object
.size
) - offsetof(EntryArrayObject
, items
)) % sizeof(le64_t
) != 0 ||
310 (le64toh(o
->object
.size
) - offsetof(EntryArrayObject
, items
)) / sizeof(le64_t
) <= 0) {
312 "Invalid object entry array size: %"PRIu64
,
313 le64toh(o
->object
.size
));
317 if (!VALID64(o
->entry_array
.next_entry_array_offset
)) {
319 "Invalid object entry array next_entry_array_offset: "OFSfmt
,
320 o
->entry_array
.next_entry_array_offset
);
324 for (i
= 0; i
< journal_file_entry_array_n_items(o
); i
++)
325 if (le64toh(o
->entry_array
.items
[i
]) != 0 &&
326 !VALID64(le64toh(o
->entry_array
.items
[i
]))) {
328 "Invalid object entry array item (%"PRIu64
"/%"PRIu64
"): "OFSfmt
,
329 i
, journal_file_entry_array_n_items(o
),
330 le64toh(o
->entry_array
.items
[i
]));
337 if (le64toh(o
->object
.size
) != sizeof(TagObject
)) {
339 "Invalid object tag size: %"PRIu64
,
340 le64toh(o
->object
.size
));
344 if (!VALID_EPOCH(o
->tag
.epoch
)) {
346 "Invalid object tag epoch: %"PRIu64
,
357 static int write_uint64(int fd
, uint64_t p
) {
360 k
= write(fd
, &p
, sizeof(p
));
369 static int contains_uint64(MMapCache
*m
, int fd
, uint64_t n
, uint64_t p
) {
384 r
= mmap_cache_get(m
, fd
, PROT_READ
|PROT_WRITE
, 0, false, c
* sizeof(uint64_t), sizeof(uint64_t), NULL
, (void **) &z
);
403 static int entry_points_to_data(
416 assert(entry_fd
>= 0);
418 if (!contains_uint64(f
->mmap
, entry_fd
, n_entries
, entry_p
)) {
419 error(data_p
, "Data object references invalid entry at "OFSfmt
, entry_p
);
423 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, entry_p
, &o
);
427 n
= journal_file_entry_n_items(o
);
428 for (i
= 0; i
< n
; i
++)
429 if (le64toh(o
->entry
.items
[i
].object_offset
) == data_p
) {
435 error(entry_p
, "Data object at "OFSfmt
" not referenced by linked entry", data_p
);
439 /* Check if this entry is also in main entry array. Since the
440 * main entry array has already been verified we can rely on
441 * its consistency. */
444 n
= le64toh(f
->header
->n_entries
);
445 a
= le64toh(f
->header
->entry_array_offset
);
450 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
454 m
= journal_file_entry_array_n_items(o
);
457 if (entry_p
<= le64toh(o
->entry_array
.items
[u
-1])) {
466 if (le64toh(o
->entry_array
.items
[z
]) == entry_p
)
472 if (entry_p
< le64toh(o
->entry_array
.items
[z
]))
478 error(entry_p
, "Entry object doesn't exist in main entry array");
483 a
= le64toh(o
->entry_array
.next_entry_array_offset
);
489 static int verify_data(
491 Object
*o
, uint64_t p
,
492 int entry_fd
, uint64_t n_entries
,
493 int entry_array_fd
, uint64_t n_entry_arrays
) {
495 uint64_t i
, n
, a
, last
, q
;
500 assert(entry_fd
>= 0);
501 assert(entry_array_fd
>= 0);
503 n
= le64toh(o
->data
.n_entries
);
504 a
= le64toh(o
->data
.entry_array_offset
);
506 /* Entry array means at least two objects */
508 error(p
, "Entry array present (entry_array_offset="OFSfmt
", but n_entries=%"PRIu64
")", a
, n
);
515 /* We already checked that earlier */
516 assert(o
->data
.entry_offset
);
518 last
= q
= le64toh(o
->data
.entry_offset
);
519 r
= entry_points_to_data(f
, entry_fd
, n_entries
, q
, p
);
528 error(p
, "Array chain too short");
532 if (!contains_uint64(f
->mmap
, entry_array_fd
, n_entry_arrays
, a
)) {
533 error(p
, "Invalid array offset "OFSfmt
, a
);
537 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
541 next
= le64toh(o
->entry_array
.next_entry_array_offset
);
542 if (next
!= 0 && next
<= a
) {
543 error(p
, "Array chain has cycle (jumps back from "OFSfmt
" to "OFSfmt
")", a
, next
);
547 m
= journal_file_entry_array_n_items(o
);
548 for (j
= 0; i
< n
&& j
< m
; i
++, j
++) {
550 q
= le64toh(o
->entry_array
.items
[j
]);
552 error(p
, "Data object's entry array not sorted");
557 r
= entry_points_to_data(f
, entry_fd
, n_entries
, q
, p
);
561 /* Pointer might have moved, reposition */
562 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
573 static int verify_hash_table(
575 int data_fd
, uint64_t n_data
,
576 int entry_fd
, uint64_t n_entries
,
577 int entry_array_fd
, uint64_t n_entry_arrays
,
579 bool show_progress
) {
585 assert(data_fd
>= 0);
586 assert(entry_fd
>= 0);
587 assert(entry_array_fd
>= 0);
590 n
= le64toh(f
->header
->data_hash_table_size
) / sizeof(HashItem
);
594 r
= journal_file_map_data_hash_table(f
);
596 return log_error_errno(r
, "Failed to map data hash table: %m");
598 for (i
= 0; i
< n
; i
++) {
599 uint64_t last
= 0, p
;
602 draw_progress(0xC000 + scale_progress(0x3FFF, i
, n
), last_usec
);
604 p
= le64toh(f
->data_hash_table
[i
].head_hash_offset
);
609 if (!contains_uint64(f
->mmap
, data_fd
, n_data
, p
)) {
610 error(p
, "Invalid data object at hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
614 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
618 next
= le64toh(o
->data
.next_hash_offset
);
619 if (next
!= 0 && next
<= p
) {
620 error(p
, "Hash chain has a cycle in hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
624 if (le64toh(o
->data
.hash
) % n
!= i
) {
625 error(p
, "Hash value mismatch in hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
629 r
= verify_data(f
, o
, p
, entry_fd
, n_entries
, entry_array_fd
, n_entry_arrays
);
637 if (last
!= le64toh(f
->data_hash_table
[i
].tail_hash_offset
)) {
638 error(p
, "Tail hash pointer mismatch in hash table");
646 static int data_object_in_hash_table(JournalFile
*f
, uint64_t hash
, uint64_t p
) {
651 n
= le64toh(f
->header
->data_hash_table_size
) / sizeof(HashItem
);
655 r
= journal_file_map_data_hash_table(f
);
657 return log_error_errno(r
, "Failed to map data hash table: %m");
661 q
= le64toh(f
->data_hash_table
[h
].head_hash_offset
);
668 r
= journal_file_move_to_object(f
, OBJECT_DATA
, q
, &o
);
672 q
= le64toh(o
->data
.next_hash_offset
);
678 static int verify_entry(
680 Object
*o
, uint64_t p
,
681 int data_fd
, uint64_t n_data
) {
688 assert(data_fd
>= 0);
690 n
= journal_file_entry_n_items(o
);
691 for (i
= 0; i
< n
; i
++) {
695 q
= le64toh(o
->entry
.items
[i
].object_offset
);
696 h
= le64toh(o
->entry
.items
[i
].hash
);
698 if (!contains_uint64(f
->mmap
, data_fd
, n_data
, q
)) {
699 error(p
, "Invalid data object of entry");
703 r
= journal_file_move_to_object(f
, OBJECT_DATA
, q
, &u
);
707 if (le64toh(u
->data
.hash
) != h
) {
708 error(p
, "Hash mismatch for data object of entry");
712 r
= data_object_in_hash_table(f
, h
, q
);
716 error(p
, "Data object missing from hash table");
724 static int verify_entry_array(
726 int data_fd
, uint64_t n_data
,
727 int entry_fd
, uint64_t n_entries
,
728 int entry_array_fd
, uint64_t n_entry_arrays
,
730 bool show_progress
) {
732 uint64_t i
= 0, a
, n
, last
= 0;
736 assert(data_fd
>= 0);
737 assert(entry_fd
>= 0);
738 assert(entry_array_fd
>= 0);
741 n
= le64toh(f
->header
->n_entries
);
742 a
= le64toh(f
->header
->entry_array_offset
);
748 draw_progress(0x8000 + scale_progress(0x3FFF, i
, n
), last_usec
);
751 error(a
, "Array chain too short at %"PRIu64
" of %"PRIu64
, i
, n
);
755 if (!contains_uint64(f
->mmap
, entry_array_fd
, n_entry_arrays
, a
)) {
756 error(a
, "Invalid array %"PRIu64
" of %"PRIu64
, i
, n
);
760 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
764 next
= le64toh(o
->entry_array
.next_entry_array_offset
);
765 if (next
!= 0 && next
<= a
) {
766 error(a
, "Array chain has cycle at %"PRIu64
" of %"PRIu64
" (jumps back from to "OFSfmt
")", i
, n
, next
);
770 m
= journal_file_entry_array_n_items(o
);
771 for (j
= 0; i
< n
&& j
< m
; i
++, j
++) {
774 p
= le64toh(o
->entry_array
.items
[j
]);
776 error(a
, "Entry array not sorted at %"PRIu64
" of %"PRIu64
, i
, n
);
781 if (!contains_uint64(f
->mmap
, entry_fd
, n_entries
, p
)) {
782 error(a
, "Invalid array entry at %"PRIu64
" of %"PRIu64
, i
, n
);
786 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, p
, &o
);
790 r
= verify_entry(f
, o
, p
, data_fd
, n_data
);
794 /* Pointer might have moved, reposition */
795 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
806 int journal_file_verify(
809 usec_t
*first_contained
, usec_t
*last_validated
, usec_t
*last_contained
,
810 bool show_progress
) {
813 uint64_t p
= 0, last_epoch
= 0, last_tag_realtime
= 0, last_sealed_realtime
= 0;
815 uint64_t entry_seqnum
= 0, entry_monotonic
= 0, entry_realtime
= 0;
816 sd_id128_t entry_boot_id
;
817 bool entry_seqnum_set
= false, entry_monotonic_set
= false, entry_realtime_set
= false, found_main_entry_array
= false;
818 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;
819 usec_t last_usec
= 0;
820 int data_fd
= -1, entry_fd
= -1, entry_array_fd
= -1;
822 bool found_last
= false;
824 uint64_t last_tag
= 0;
830 r
= journal_file_parse_verification_key(f
, key
);
832 log_error("Failed to parse seed.");
841 data_fd
= open_tmpfile("/var/tmp", O_RDWR
| O_CLOEXEC
);
843 r
= log_error_errno(data_fd
, "Failed to create data file: %m");
847 entry_fd
= open_tmpfile("/var/tmp", O_RDWR
| O_CLOEXEC
);
849 r
= log_error_errno(entry_fd
, "Failed to create entry file: %m");
853 entry_array_fd
= open_tmpfile("/var/tmp", O_RDWR
| O_CLOEXEC
);
854 if (entry_array_fd
< 0) {
855 r
= log_error_errno(entry_array_fd
,
856 "Failed to create entry array file: %m");
860 if (le32toh(f
->header
->compatible_flags
) & ~HEADER_COMPATIBLE_SUPPORTED
) {
861 log_error("Cannot verify file with unknown extensions.");
866 for (i
= 0; i
< sizeof(f
->header
->reserved
); i
++)
867 if (f
->header
->reserved
[i
] != 0) {
868 error(offsetof(Header
, reserved
[i
]), "Reserved field is non-zero");
873 /* First iteration: we go through all objects, verify the
874 * superficial structure, headers, hashes. */
876 p
= le64toh(f
->header
->header_size
);
878 /* Early exit if there are no objects in the file, at all */
879 if (le64toh(f
->header
->tail_object_offset
) == 0)
883 draw_progress(scale_progress(0x7FFF, p
, le64toh(f
->header
->tail_object_offset
)), &last_usec
);
885 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, p
, &o
);
887 error(p
, "Invalid object");
891 if (p
> le64toh(f
->header
->tail_object_offset
)) {
892 error(offsetof(Header
, tail_object_offset
), "Invalid tail object pointer");
899 r
= journal_file_object_verify(f
, p
, o
);
901 error(p
, "Invalid object contents: %s", strerror(-r
));
905 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) &&
906 (o
->object
.flags
& OBJECT_COMPRESSED_LZ4
)) {
907 error(p
, "Objected with double compression");
912 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) && !JOURNAL_HEADER_COMPRESSED_XZ(f
->header
)) {
913 error(p
, "XZ compressed object in file without XZ compression");
918 if ((o
->object
.flags
& OBJECT_COMPRESSED_LZ4
) && !JOURNAL_HEADER_COMPRESSED_LZ4(f
->header
)) {
919 error(p
, "LZ4 compressed object in file without LZ4 compression");
924 switch (o
->object
.type
) {
927 r
= write_uint64(data_fd
, p
);
939 if (JOURNAL_HEADER_SEALED(f
->header
) && n_tags
<= 0) {
940 error(p
, "First entry before first tag");
945 r
= write_uint64(entry_fd
, p
);
949 if (le64toh(o
->entry
.realtime
) < last_tag_realtime
) {
950 error(p
, "Older entry after newer tag");
955 if (!entry_seqnum_set
&&
956 le64toh(o
->entry
.seqnum
) != le64toh(f
->header
->head_entry_seqnum
)) {
957 error(p
, "Head entry sequence number incorrect");
962 if (entry_seqnum_set
&&
963 entry_seqnum
>= le64toh(o
->entry
.seqnum
)) {
964 error(p
, "Entry sequence number out of synchronization");
969 entry_seqnum
= le64toh(o
->entry
.seqnum
);
970 entry_seqnum_set
= true;
972 if (entry_monotonic_set
&&
973 sd_id128_equal(entry_boot_id
, o
->entry
.boot_id
) &&
974 entry_monotonic
> le64toh(o
->entry
.monotonic
)) {
975 error(p
, "Entry timestamp out of synchronization");
980 entry_monotonic
= le64toh(o
->entry
.monotonic
);
981 entry_boot_id
= o
->entry
.boot_id
;
982 entry_monotonic_set
= true;
984 if (!entry_realtime_set
&&
985 le64toh(o
->entry
.realtime
) != le64toh(f
->header
->head_entry_realtime
)) {
986 error(p
, "Head entry realtime timestamp incorrect");
991 entry_realtime
= le64toh(o
->entry
.realtime
);
992 entry_realtime_set
= true;
997 case OBJECT_DATA_HASH_TABLE
:
998 if (n_data_hash_tables
> 1) {
999 error(p
, "More than one data hash table");
1004 if (le64toh(f
->header
->data_hash_table_offset
) != p
+ offsetof(HashTableObject
, items
) ||
1005 le64toh(f
->header
->data_hash_table_size
) != le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) {
1006 error(p
, "header fields for data hash table invalid");
1011 n_data_hash_tables
++;
1014 case OBJECT_FIELD_HASH_TABLE
:
1015 if (n_field_hash_tables
> 1) {
1016 error(p
, "More than one field hash table");
1021 if (le64toh(f
->header
->field_hash_table_offset
) != p
+ offsetof(HashTableObject
, items
) ||
1022 le64toh(f
->header
->field_hash_table_size
) != le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) {
1023 error(p
, "Header fields for field hash table invalid");
1028 n_field_hash_tables
++;
1031 case OBJECT_ENTRY_ARRAY
:
1032 r
= write_uint64(entry_array_fd
, p
);
1036 if (p
== le64toh(f
->header
->entry_array_offset
)) {
1037 if (found_main_entry_array
) {
1038 error(p
, "More than one main entry array");
1043 found_main_entry_array
= true;
1050 if (!JOURNAL_HEADER_SEALED(f
->header
)) {
1051 error(p
, "Tag object in file without sealing");
1056 if (le64toh(o
->tag
.seqnum
) != n_tags
+ 1) {
1057 error(p
, "Tag sequence number out of synchronization");
1062 if (le64toh(o
->tag
.epoch
) < last_epoch
) {
1063 error(p
, "Epoch sequence out of synchronization");
1072 debug(p
, "Checking tag %"PRIu64
"...", le64toh(o
->tag
.seqnum
));
1074 rt
= f
->fss_start_usec
+ o
->tag
.epoch
* f
->fss_interval_usec
;
1075 if (entry_realtime_set
&& entry_realtime
>= rt
+ f
->fss_interval_usec
) {
1076 error(p
, "tag/entry realtime timestamp out of synchronization");
1081 /* OK, now we know the epoch. So let's now set
1082 * it, and calculate the HMAC for everything
1083 * since the last tag. */
1084 r
= journal_file_fsprg_seek(f
, le64toh(o
->tag
.epoch
));
1088 r
= journal_file_hmac_start(f
);
1092 if (last_tag
== 0) {
1093 r
= journal_file_hmac_put_header(f
);
1097 q
= le64toh(f
->header
->header_size
);
1102 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, q
, &o
);
1106 r
= journal_file_hmac_put_object(f
, OBJECT_UNUSED
, o
, q
);
1110 q
= q
+ ALIGN64(le64toh(o
->object
.size
));
1113 /* Position might have changed, let's reposition things */
1114 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, p
, &o
);
1118 if (memcmp(o
->tag
.tag
, gcry_md_read(f
->hmac
, 0), TAG_LENGTH
) != 0) {
1119 error(p
, "Tag failed verification");
1124 f
->hmac_running
= false;
1125 last_tag_realtime
= rt
;
1126 last_sealed_realtime
= entry_realtime
;
1129 last_tag
= p
+ ALIGN64(le64toh(o
->object
.size
));
1132 last_epoch
= le64toh(o
->tag
.epoch
);
1141 if (p
== le64toh(f
->header
->tail_object_offset
)) {
1146 p
= p
+ ALIGN64(le64toh(o
->object
.size
));
1149 if (!found_last
&& le64toh(f
->header
->tail_object_offset
) != 0) {
1150 error(le64toh(f
->header
->tail_object_offset
), "Tail object pointer dead");
1155 if (n_objects
!= le64toh(f
->header
->n_objects
)) {
1156 error(offsetof(Header
, n_objects
), "Object number mismatch");
1161 if (n_entries
!= le64toh(f
->header
->n_entries
)) {
1162 error(offsetof(Header
, n_entries
), "Entry number mismatch");
1167 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_data
) &&
1168 n_data
!= le64toh(f
->header
->n_data
)) {
1169 error(offsetof(Header
, n_data
), "Data number mismatch");
1174 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_fields
) &&
1175 n_fields
!= le64toh(f
->header
->n_fields
)) {
1176 error(offsetof(Header
, n_fields
), "Field number mismatch");
1181 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_tags
) &&
1182 n_tags
!= le64toh(f
->header
->n_tags
)) {
1183 error(offsetof(Header
, n_tags
), "Tag number mismatch");
1188 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_entry_arrays
) &&
1189 n_entry_arrays
!= le64toh(f
->header
->n_entry_arrays
)) {
1190 error(offsetof(Header
, n_entry_arrays
), "Entry array number mismatch");
1195 if (!found_main_entry_array
&& le64toh(f
->header
->entry_array_offset
) != 0) {
1196 error(0, "Missing entry array");
1201 if (entry_seqnum_set
&&
1202 entry_seqnum
!= le64toh(f
->header
->tail_entry_seqnum
)) {
1203 error(offsetof(Header
, tail_entry_seqnum
), "Invalid tail seqnum");
1208 if (entry_monotonic_set
&&
1209 (!sd_id128_equal(entry_boot_id
, f
->header
->boot_id
) ||
1210 entry_monotonic
!= le64toh(f
->header
->tail_entry_monotonic
))) {
1211 error(0, "Invalid tail monotonic timestamp");
1216 if (entry_realtime_set
&& entry_realtime
!= le64toh(f
->header
->tail_entry_realtime
)) {
1217 error(0, "Invalid tail realtime timestamp");
1222 /* Second iteration: we follow all objects referenced from the
1223 * two entry points: the object hash table and the entry
1224 * array. We also check that everything referenced (directly
1225 * or indirectly) in the data hash table also exists in the
1226 * entry array, and vice versa. Note that we do not care for
1227 * unreferenced objects. We only care that everything that is
1228 * referenced is consistent. */
1230 r
= verify_entry_array(f
,
1232 entry_fd
, n_entries
,
1233 entry_array_fd
, n_entry_arrays
,
1239 r
= verify_hash_table(f
,
1241 entry_fd
, n_entries
,
1242 entry_array_fd
, n_entry_arrays
,
1251 mmap_cache_close_fd(f
->mmap
, data_fd
);
1252 mmap_cache_close_fd(f
->mmap
, entry_fd
);
1253 mmap_cache_close_fd(f
->mmap
, entry_array_fd
);
1255 safe_close(data_fd
);
1256 safe_close(entry_fd
);
1257 safe_close(entry_array_fd
);
1259 if (first_contained
)
1260 *first_contained
= le64toh(f
->header
->head_entry_realtime
);
1262 *last_validated
= last_sealed_realtime
;
1264 *last_contained
= le64toh(f
->header
->tail_entry_realtime
);
1272 log_error("File corruption detected at %s:"OFSfmt
" (of %llu bytes, %"PRIu64
"%%).",
1275 (unsigned long long) f
->last_stat
.st_size
,
1276 100 * p
/ f
->last_stat
.st_size
);
1279 mmap_cache_close_fd(f
->mmap
, data_fd
);
1280 safe_close(data_fd
);
1283 if (entry_fd
>= 0) {
1284 mmap_cache_close_fd(f
->mmap
, entry_fd
);
1285 safe_close(entry_fd
);
1288 if (entry_array_fd
>= 0) {
1289 mmap_cache_close_fd(f
->mmap
, entry_array_fd
);
1290 safe_close(entry_array_fd
);