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/>.
27 #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;
59 fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN
, stdout
);
61 for (i
= 0; i
< j
; i
++)
62 fputs("\xe2\x96\x88", stdout
);
64 fputs(ANSI_NORMAL
, stdout
);
66 for (i
= 0; i
< k
; i
++)
67 fputs("\xe2\x96\x91", stdout
);
69 printf(" %3"PRIu64
"%%", 100U * p
/ 65535U);
71 fputs("\r\x1B[?25h", stdout
);
75 static uint64_t scale_progress(uint64_t scale
, uint64_t p
, uint64_t m
) {
77 /* Calculates scale * p / m, but handles m == 0 safely, and saturates */
85 static void flush_progress(void) {
91 n
= (3 * columns()) / 4;
95 for (i
= 0; i
< n
+ 5; i
++)
102 #define debug(_offset, _fmt, ...) do{ \
104 log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
107 #define warning(_offset, _fmt, ...) do{ \
109 log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
112 #define error(_offset, _fmt, ...) do{ \
114 log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
117 static int journal_file_object_verify(JournalFile
*f
, uint64_t offset
, Object
*o
) {
124 /* This does various superficial tests about the length an
125 * possible field values. It does not follow any references to
128 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) &&
129 o
->object
.type
!= OBJECT_DATA
) {
130 error(offset
, "Found compressed object that isn't of type DATA, which is not allowed.");
134 switch (o
->object
.type
) {
140 if (le64toh(o
->data
.entry_offset
) == 0)
141 warning(offset
, "Unused data (entry_offset==0)");
143 if ((le64toh(o
->data
.entry_offset
) == 0) ^ (le64toh(o
->data
.n_entries
) == 0)) {
144 error(offset
, "Bad n_entries: %"PRIu64
, o
->data
.n_entries
);
148 if (le64toh(o
->object
.size
) - offsetof(DataObject
, payload
) <= 0) {
149 error(offset
, "Bad object size (<= %zu): %"PRIu64
,
150 offsetof(DataObject
, payload
),
151 le64toh(o
->object
.size
));
155 h1
= le64toh(o
->data
.hash
);
157 compression
= o
->object
.flags
& OBJECT_COMPRESSION_MASK
;
159 _cleanup_free_
void *b
= NULL
;
160 size_t alloc
= 0, b_size
;
162 r
= decompress_blob(compression
,
164 le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
),
165 &b
, &alloc
, &b_size
, 0);
167 error(offset
, "%s decompression failed: %s",
168 object_compressed_to_string(compression
), strerror(-r
));
172 h2
= hash64(b
, b_size
);
174 h2
= hash64(o
->data
.payload
, le64toh(o
->object
.size
) - offsetof(Object
, data
.payload
));
177 error(offset
, "Invalid hash (%08"PRIx64
" vs. %08"PRIx64
, h1
, h2
);
181 if (!VALID64(o
->data
.next_hash_offset
) ||
182 !VALID64(o
->data
.next_field_offset
) ||
183 !VALID64(o
->data
.entry_offset
) ||
184 !VALID64(o
->data
.entry_array_offset
)) {
185 error(offset
, "Invalid offset (next_hash_offset="OFSfmt
", next_field_offset="OFSfmt
", entry_offset="OFSfmt
", entry_array_offset="OFSfmt
,
186 o
->data
.next_hash_offset
,
187 o
->data
.next_field_offset
,
188 o
->data
.entry_offset
,
189 o
->data
.entry_array_offset
);
197 if (le64toh(o
->object
.size
) - offsetof(FieldObject
, payload
) <= 0) {
199 "Bad field size (<= %zu): %"PRIu64
,
200 offsetof(FieldObject
, payload
),
201 le64toh(o
->object
.size
));
205 if (!VALID64(o
->field
.next_hash_offset
) ||
206 !VALID64(o
->field
.head_data_offset
)) {
208 "Invalid offset (next_hash_offset="OFSfmt
", head_data_offset="OFSfmt
,
209 o
->field
.next_hash_offset
,
210 o
->field
.head_data_offset
);
216 if ((le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) % sizeof(EntryItem
) != 0) {
218 "Bad entry size (<= %zu): %"PRIu64
,
219 offsetof(EntryObject
, items
),
220 le64toh(o
->object
.size
));
224 if ((le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) / sizeof(EntryItem
) <= 0) {
226 "Invalid number items in entry: %"PRIu64
,
227 (le64toh(o
->object
.size
) - offsetof(EntryObject
, items
)) / sizeof(EntryItem
));
231 if (le64toh(o
->entry
.seqnum
) <= 0) {
233 "Invalid entry seqnum: %"PRIx64
,
234 le64toh(o
->entry
.seqnum
));
238 if (!VALID_REALTIME(le64toh(o
->entry
.realtime
))) {
240 "Invalid entry realtime timestamp: %"PRIu64
,
241 le64toh(o
->entry
.realtime
));
245 if (!VALID_MONOTONIC(le64toh(o
->entry
.monotonic
))) {
247 "Invalid entry monotonic timestamp: %"PRIu64
,
248 le64toh(o
->entry
.monotonic
));
252 for (i
= 0; i
< journal_file_entry_n_items(o
); i
++) {
253 if (o
->entry
.items
[i
].object_offset
== 0 ||
254 !VALID64(o
->entry
.items
[i
].object_offset
)) {
256 "Invalid entry item (%"PRIu64
"/%"PRIu64
" offset: "OFSfmt
,
257 i
, journal_file_entry_n_items(o
),
258 o
->entry
.items
[i
].object_offset
);
265 case OBJECT_DATA_HASH_TABLE
:
266 case OBJECT_FIELD_HASH_TABLE
:
267 if ((le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) % sizeof(HashItem
) != 0 ||
268 (le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) / sizeof(HashItem
) <= 0) {
270 "Invalid %s hash table size: %"PRIu64
,
271 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
272 le64toh(o
->object
.size
));
276 for (i
= 0; i
< journal_file_hash_table_n_items(o
); i
++) {
277 if (o
->hash_table
.items
[i
].head_hash_offset
!= 0 &&
278 !VALID64(le64toh(o
->hash_table
.items
[i
].head_hash_offset
))) {
280 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
") head_hash_offset: "OFSfmt
,
281 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
282 i
, journal_file_hash_table_n_items(o
),
283 le64toh(o
->hash_table
.items
[i
].head_hash_offset
));
286 if (o
->hash_table
.items
[i
].tail_hash_offset
!= 0 &&
287 !VALID64(le64toh(o
->hash_table
.items
[i
].tail_hash_offset
))) {
289 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
") tail_hash_offset: "OFSfmt
,
290 o
->object
.type
== OBJECT_DATA_HASH_TABLE
? "data" : "field",
291 i
, journal_file_hash_table_n_items(o
),
292 le64toh(o
->hash_table
.items
[i
].tail_hash_offset
));
296 if ((o
->hash_table
.items
[i
].head_hash_offset
!= 0) !=
297 (o
->hash_table
.items
[i
].tail_hash_offset
!= 0)) {
299 "Invalid %s hash table item (%"PRIu64
"/%"PRIu64
"): head_hash_offset="OFSfmt
" 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
].head_hash_offset
),
303 le64toh(o
->hash_table
.items
[i
].tail_hash_offset
));
310 case OBJECT_ENTRY_ARRAY
:
311 if ((le64toh(o
->object
.size
) - offsetof(EntryArrayObject
, items
)) % sizeof(le64_t
) != 0 ||
312 (le64toh(o
->object
.size
) - offsetof(EntryArrayObject
, items
)) / sizeof(le64_t
) <= 0) {
314 "Invalid object entry array size: %"PRIu64
,
315 le64toh(o
->object
.size
));
319 if (!VALID64(o
->entry_array
.next_entry_array_offset
)) {
321 "Invalid object entry array next_entry_array_offset: "OFSfmt
,
322 o
->entry_array
.next_entry_array_offset
);
326 for (i
= 0; i
< journal_file_entry_array_n_items(o
); i
++)
327 if (le64toh(o
->entry_array
.items
[i
]) != 0 &&
328 !VALID64(le64toh(o
->entry_array
.items
[i
]))) {
330 "Invalid object entry array item (%"PRIu64
"/%"PRIu64
"): "OFSfmt
,
331 i
, journal_file_entry_array_n_items(o
),
332 le64toh(o
->entry_array
.items
[i
]));
339 if (le64toh(o
->object
.size
) != sizeof(TagObject
)) {
341 "Invalid object tag size: %"PRIu64
,
342 le64toh(o
->object
.size
));
346 if (!VALID_EPOCH(o
->tag
.epoch
)) {
348 "Invalid object tag epoch: %"PRIu64
,
359 static int write_uint64(int fd
, uint64_t p
) {
362 k
= write(fd
, &p
, sizeof(p
));
371 static int contains_uint64(MMapCache
*m
, int fd
, uint64_t n
, uint64_t p
) {
386 r
= mmap_cache_get(m
, fd
, PROT_READ
|PROT_WRITE
, 0, false, c
* sizeof(uint64_t), sizeof(uint64_t), NULL
, (void **) &z
);
405 static int entry_points_to_data(
418 assert(entry_fd
>= 0);
420 if (!contains_uint64(f
->mmap
, entry_fd
, n_entries
, entry_p
)) {
421 error(data_p
, "Data object references invalid entry at "OFSfmt
, entry_p
);
425 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, entry_p
, &o
);
429 n
= journal_file_entry_n_items(o
);
430 for (i
= 0; i
< n
; i
++)
431 if (le64toh(o
->entry
.items
[i
].object_offset
) == data_p
) {
437 error(entry_p
, "Data object at "OFSfmt
" not referenced by linked entry", data_p
);
441 /* Check if this entry is also in main entry array. Since the
442 * main entry array has already been verified we can rely on
443 * its consistency. */
446 n
= le64toh(f
->header
->n_entries
);
447 a
= le64toh(f
->header
->entry_array_offset
);
452 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
456 m
= journal_file_entry_array_n_items(o
);
459 if (entry_p
<= le64toh(o
->entry_array
.items
[u
-1])) {
468 if (le64toh(o
->entry_array
.items
[z
]) == entry_p
)
474 if (entry_p
< le64toh(o
->entry_array
.items
[z
]))
480 error(entry_p
, "Entry object doesn't exist in main entry array");
485 a
= le64toh(o
->entry_array
.next_entry_array_offset
);
491 static int verify_data(
493 Object
*o
, uint64_t p
,
494 int entry_fd
, uint64_t n_entries
,
495 int entry_array_fd
, uint64_t n_entry_arrays
) {
497 uint64_t i
, n
, a
, last
, q
;
502 assert(entry_fd
>= 0);
503 assert(entry_array_fd
>= 0);
505 n
= le64toh(o
->data
.n_entries
);
506 a
= le64toh(o
->data
.entry_array_offset
);
508 /* Entry array means at least two objects */
510 error(p
, "Entry array present (entry_array_offset="OFSfmt
", but n_entries=%"PRIu64
")", a
, n
);
517 /* We already checked that earlier */
518 assert(o
->data
.entry_offset
);
520 last
= q
= le64toh(o
->data
.entry_offset
);
521 r
= entry_points_to_data(f
, entry_fd
, n_entries
, q
, p
);
530 error(p
, "Array chain too short");
534 if (!contains_uint64(f
->mmap
, entry_array_fd
, n_entry_arrays
, a
)) {
535 error(p
, "Invalid array offset "OFSfmt
, a
);
539 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
543 next
= le64toh(o
->entry_array
.next_entry_array_offset
);
544 if (next
!= 0 && next
<= a
) {
545 error(p
, "Array chain has cycle (jumps back from "OFSfmt
" to "OFSfmt
")", a
, next
);
549 m
= journal_file_entry_array_n_items(o
);
550 for (j
= 0; i
< n
&& j
< m
; i
++, j
++) {
552 q
= le64toh(o
->entry_array
.items
[j
]);
554 error(p
, "Data object's entry array not sorted");
559 r
= entry_points_to_data(f
, entry_fd
, n_entries
, q
, p
);
563 /* Pointer might have moved, reposition */
564 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
575 static int verify_hash_table(
577 int data_fd
, uint64_t n_data
,
578 int entry_fd
, uint64_t n_entries
,
579 int entry_array_fd
, uint64_t n_entry_arrays
,
581 bool show_progress
) {
587 assert(data_fd
>= 0);
588 assert(entry_fd
>= 0);
589 assert(entry_array_fd
>= 0);
592 n
= le64toh(f
->header
->data_hash_table_size
) / sizeof(HashItem
);
596 r
= journal_file_map_data_hash_table(f
);
598 return log_error_errno(r
, "Failed to map data hash table: %m");
600 for (i
= 0; i
< n
; i
++) {
601 uint64_t last
= 0, p
;
604 draw_progress(0xC000 + scale_progress(0x3FFF, i
, n
), last_usec
);
606 p
= le64toh(f
->data_hash_table
[i
].head_hash_offset
);
611 if (!contains_uint64(f
->mmap
, data_fd
, n_data
, p
)) {
612 error(p
, "Invalid data object at hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
616 r
= journal_file_move_to_object(f
, OBJECT_DATA
, p
, &o
);
620 next
= le64toh(o
->data
.next_hash_offset
);
621 if (next
!= 0 && next
<= p
) {
622 error(p
, "Hash chain has a cycle in hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
626 if (le64toh(o
->data
.hash
) % n
!= i
) {
627 error(p
, "Hash value mismatch in hash entry %"PRIu64
" of %"PRIu64
, i
, n
);
631 r
= verify_data(f
, o
, p
, entry_fd
, n_entries
, entry_array_fd
, n_entry_arrays
);
639 if (last
!= le64toh(f
->data_hash_table
[i
].tail_hash_offset
)) {
640 error(p
, "Tail hash pointer mismatch in hash table");
648 static int data_object_in_hash_table(JournalFile
*f
, uint64_t hash
, uint64_t p
) {
653 n
= le64toh(f
->header
->data_hash_table_size
) / sizeof(HashItem
);
657 r
= journal_file_map_data_hash_table(f
);
659 return log_error_errno(r
, "Failed to map data hash table: %m");
663 q
= le64toh(f
->data_hash_table
[h
].head_hash_offset
);
670 r
= journal_file_move_to_object(f
, OBJECT_DATA
, q
, &o
);
674 q
= le64toh(o
->data
.next_hash_offset
);
680 static int verify_entry(
682 Object
*o
, uint64_t p
,
683 int data_fd
, uint64_t n_data
) {
690 assert(data_fd
>= 0);
692 n
= journal_file_entry_n_items(o
);
693 for (i
= 0; i
< n
; i
++) {
697 q
= le64toh(o
->entry
.items
[i
].object_offset
);
698 h
= le64toh(o
->entry
.items
[i
].hash
);
700 if (!contains_uint64(f
->mmap
, data_fd
, n_data
, q
)) {
701 error(p
, "Invalid data object of entry");
705 r
= journal_file_move_to_object(f
, OBJECT_DATA
, q
, &u
);
709 if (le64toh(u
->data
.hash
) != h
) {
710 error(p
, "Hash mismatch for data object of entry");
714 r
= data_object_in_hash_table(f
, h
, q
);
718 error(p
, "Data object missing from hash table");
726 static int verify_entry_array(
728 int data_fd
, uint64_t n_data
,
729 int entry_fd
, uint64_t n_entries
,
730 int entry_array_fd
, uint64_t n_entry_arrays
,
732 bool show_progress
) {
734 uint64_t i
= 0, a
, n
, last
= 0;
738 assert(data_fd
>= 0);
739 assert(entry_fd
>= 0);
740 assert(entry_array_fd
>= 0);
743 n
= le64toh(f
->header
->n_entries
);
744 a
= le64toh(f
->header
->entry_array_offset
);
750 draw_progress(0x8000 + scale_progress(0x3FFF, i
, n
), last_usec
);
753 error(a
, "Array chain too short at %"PRIu64
" of %"PRIu64
, i
, n
);
757 if (!contains_uint64(f
->mmap
, entry_array_fd
, n_entry_arrays
, a
)) {
758 error(a
, "Invalid array %"PRIu64
" of %"PRIu64
, i
, n
);
762 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
766 next
= le64toh(o
->entry_array
.next_entry_array_offset
);
767 if (next
!= 0 && next
<= a
) {
768 error(a
, "Array chain has cycle at %"PRIu64
" of %"PRIu64
" (jumps back from to "OFSfmt
")", i
, n
, next
);
772 m
= journal_file_entry_array_n_items(o
);
773 for (j
= 0; i
< n
&& j
< m
; i
++, j
++) {
776 p
= le64toh(o
->entry_array
.items
[j
]);
778 error(a
, "Entry array not sorted at %"PRIu64
" of %"PRIu64
, i
, n
);
783 if (!contains_uint64(f
->mmap
, entry_fd
, n_entries
, p
)) {
784 error(a
, "Invalid array entry at %"PRIu64
" of %"PRIu64
, i
, n
);
788 r
= journal_file_move_to_object(f
, OBJECT_ENTRY
, p
, &o
);
792 r
= verify_entry(f
, o
, p
, data_fd
, n_data
);
796 /* Pointer might have moved, reposition */
797 r
= journal_file_move_to_object(f
, OBJECT_ENTRY_ARRAY
, a
, &o
);
808 int journal_file_verify(
811 usec_t
*first_contained
, usec_t
*last_validated
, usec_t
*last_contained
,
812 bool show_progress
) {
815 uint64_t p
= 0, last_epoch
= 0, last_tag_realtime
= 0, last_sealed_realtime
= 0;
817 uint64_t entry_seqnum
= 0, entry_monotonic
= 0, entry_realtime
= 0;
818 sd_id128_t entry_boot_id
;
819 bool entry_seqnum_set
= false, entry_monotonic_set
= false, entry_realtime_set
= false, found_main_entry_array
= false;
820 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;
821 usec_t last_usec
= 0;
822 int data_fd
= -1, entry_fd
= -1, entry_array_fd
= -1;
824 bool found_last
= false;
826 uint64_t last_tag
= 0;
832 r
= journal_file_parse_verification_key(f
, key
);
834 log_error("Failed to parse seed.");
843 data_fd
= open_tmpfile("/var/tmp", O_RDWR
| O_CLOEXEC
);
845 r
= log_error_errno(data_fd
, "Failed to create data file: %m");
849 entry_fd
= open_tmpfile("/var/tmp", O_RDWR
| O_CLOEXEC
);
851 r
= log_error_errno(entry_fd
, "Failed to create entry file: %m");
855 entry_array_fd
= open_tmpfile("/var/tmp", O_RDWR
| O_CLOEXEC
);
856 if (entry_array_fd
< 0) {
857 r
= log_error_errno(entry_array_fd
,
858 "Failed to create entry array file: %m");
862 if (le32toh(f
->header
->compatible_flags
) & ~HEADER_COMPATIBLE_SUPPORTED
) {
863 log_error("Cannot verify file with unknown extensions.");
868 for (i
= 0; i
< sizeof(f
->header
->reserved
); i
++)
869 if (f
->header
->reserved
[i
] != 0) {
870 error(offsetof(Header
, reserved
[i
]), "Reserved field is non-zero");
875 /* First iteration: we go through all objects, verify the
876 * superficial structure, headers, hashes. */
878 p
= le64toh(f
->header
->header_size
);
880 /* Early exit if there are no objects in the file, at all */
881 if (le64toh(f
->header
->tail_object_offset
) == 0)
885 draw_progress(scale_progress(0x7FFF, p
, le64toh(f
->header
->tail_object_offset
)), &last_usec
);
887 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, p
, &o
);
889 error(p
, "Invalid object");
893 if (p
> le64toh(f
->header
->tail_object_offset
)) {
894 error(offsetof(Header
, tail_object_offset
), "Invalid tail object pointer");
901 r
= journal_file_object_verify(f
, p
, o
);
903 error(p
, "Invalid object contents: %s", strerror(-r
));
907 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) &&
908 (o
->object
.flags
& OBJECT_COMPRESSED_LZ4
)) {
909 error(p
, "Objected with double compression");
914 if ((o
->object
.flags
& OBJECT_COMPRESSED_XZ
) && !JOURNAL_HEADER_COMPRESSED_XZ(f
->header
)) {
915 error(p
, "XZ compressed object in file without XZ compression");
920 if ((o
->object
.flags
& OBJECT_COMPRESSED_LZ4
) && !JOURNAL_HEADER_COMPRESSED_LZ4(f
->header
)) {
921 error(p
, "LZ4 compressed object in file without LZ4 compression");
926 switch (o
->object
.type
) {
929 r
= write_uint64(data_fd
, p
);
941 if (JOURNAL_HEADER_SEALED(f
->header
) && n_tags
<= 0) {
942 error(p
, "First entry before first tag");
947 r
= write_uint64(entry_fd
, p
);
951 if (le64toh(o
->entry
.realtime
) < last_tag_realtime
) {
952 error(p
, "Older entry after newer tag");
957 if (!entry_seqnum_set
&&
958 le64toh(o
->entry
.seqnum
) != le64toh(f
->header
->head_entry_seqnum
)) {
959 error(p
, "Head entry sequence number incorrect");
964 if (entry_seqnum_set
&&
965 entry_seqnum
>= le64toh(o
->entry
.seqnum
)) {
966 error(p
, "Entry sequence number out of synchronization");
971 entry_seqnum
= le64toh(o
->entry
.seqnum
);
972 entry_seqnum_set
= true;
974 if (entry_monotonic_set
&&
975 sd_id128_equal(entry_boot_id
, o
->entry
.boot_id
) &&
976 entry_monotonic
> le64toh(o
->entry
.monotonic
)) {
977 error(p
, "Entry timestamp out of synchronization");
982 entry_monotonic
= le64toh(o
->entry
.monotonic
);
983 entry_boot_id
= o
->entry
.boot_id
;
984 entry_monotonic_set
= true;
986 if (!entry_realtime_set
&&
987 le64toh(o
->entry
.realtime
) != le64toh(f
->header
->head_entry_realtime
)) {
988 error(p
, "Head entry realtime timestamp incorrect");
993 entry_realtime
= le64toh(o
->entry
.realtime
);
994 entry_realtime_set
= true;
999 case OBJECT_DATA_HASH_TABLE
:
1000 if (n_data_hash_tables
> 1) {
1001 error(p
, "More than one data hash table");
1006 if (le64toh(f
->header
->data_hash_table_offset
) != p
+ offsetof(HashTableObject
, items
) ||
1007 le64toh(f
->header
->data_hash_table_size
) != le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) {
1008 error(p
, "header fields for data hash table invalid");
1013 n_data_hash_tables
++;
1016 case OBJECT_FIELD_HASH_TABLE
:
1017 if (n_field_hash_tables
> 1) {
1018 error(p
, "More than one field hash table");
1023 if (le64toh(f
->header
->field_hash_table_offset
) != p
+ offsetof(HashTableObject
, items
) ||
1024 le64toh(f
->header
->field_hash_table_size
) != le64toh(o
->object
.size
) - offsetof(HashTableObject
, items
)) {
1025 error(p
, "Header fields for field hash table invalid");
1030 n_field_hash_tables
++;
1033 case OBJECT_ENTRY_ARRAY
:
1034 r
= write_uint64(entry_array_fd
, p
);
1038 if (p
== le64toh(f
->header
->entry_array_offset
)) {
1039 if (found_main_entry_array
) {
1040 error(p
, "More than one main entry array");
1045 found_main_entry_array
= true;
1052 if (!JOURNAL_HEADER_SEALED(f
->header
)) {
1053 error(p
, "Tag object in file without sealing");
1058 if (le64toh(o
->tag
.seqnum
) != n_tags
+ 1) {
1059 error(p
, "Tag sequence number out of synchronization");
1064 if (le64toh(o
->tag
.epoch
) < last_epoch
) {
1065 error(p
, "Epoch sequence out of synchronization");
1074 debug(p
, "Checking tag %"PRIu64
"...", le64toh(o
->tag
.seqnum
));
1076 rt
= f
->fss_start_usec
+ o
->tag
.epoch
* f
->fss_interval_usec
;
1077 if (entry_realtime_set
&& entry_realtime
>= rt
+ f
->fss_interval_usec
) {
1078 error(p
, "tag/entry realtime timestamp out of synchronization");
1083 /* OK, now we know the epoch. So let's now set
1084 * it, and calculate the HMAC for everything
1085 * since the last tag. */
1086 r
= journal_file_fsprg_seek(f
, le64toh(o
->tag
.epoch
));
1090 r
= journal_file_hmac_start(f
);
1094 if (last_tag
== 0) {
1095 r
= journal_file_hmac_put_header(f
);
1099 q
= le64toh(f
->header
->header_size
);
1104 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, q
, &o
);
1108 r
= journal_file_hmac_put_object(f
, OBJECT_UNUSED
, o
, q
);
1112 q
= q
+ ALIGN64(le64toh(o
->object
.size
));
1115 /* Position might have changed, let's reposition things */
1116 r
= journal_file_move_to_object(f
, OBJECT_UNUSED
, p
, &o
);
1120 if (memcmp(o
->tag
.tag
, gcry_md_read(f
->hmac
, 0), TAG_LENGTH
) != 0) {
1121 error(p
, "Tag failed verification");
1126 f
->hmac_running
= false;
1127 last_tag_realtime
= rt
;
1128 last_sealed_realtime
= entry_realtime
;
1131 last_tag
= p
+ ALIGN64(le64toh(o
->object
.size
));
1134 last_epoch
= le64toh(o
->tag
.epoch
);
1143 if (p
== le64toh(f
->header
->tail_object_offset
)) {
1148 p
= p
+ ALIGN64(le64toh(o
->object
.size
));
1151 if (!found_last
&& le64toh(f
->header
->tail_object_offset
) != 0) {
1152 error(le64toh(f
->header
->tail_object_offset
), "Tail object pointer dead");
1157 if (n_objects
!= le64toh(f
->header
->n_objects
)) {
1158 error(offsetof(Header
, n_objects
), "Object number mismatch");
1163 if (n_entries
!= le64toh(f
->header
->n_entries
)) {
1164 error(offsetof(Header
, n_entries
), "Entry number mismatch");
1169 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_data
) &&
1170 n_data
!= le64toh(f
->header
->n_data
)) {
1171 error(offsetof(Header
, n_data
), "Data number mismatch");
1176 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_fields
) &&
1177 n_fields
!= le64toh(f
->header
->n_fields
)) {
1178 error(offsetof(Header
, n_fields
), "Field number mismatch");
1183 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_tags
) &&
1184 n_tags
!= le64toh(f
->header
->n_tags
)) {
1185 error(offsetof(Header
, n_tags
), "Tag number mismatch");
1190 if (JOURNAL_HEADER_CONTAINS(f
->header
, n_entry_arrays
) &&
1191 n_entry_arrays
!= le64toh(f
->header
->n_entry_arrays
)) {
1192 error(offsetof(Header
, n_entry_arrays
), "Entry array number mismatch");
1197 if (!found_main_entry_array
&& le64toh(f
->header
->entry_array_offset
) != 0) {
1198 error(0, "Missing entry array");
1203 if (entry_seqnum_set
&&
1204 entry_seqnum
!= le64toh(f
->header
->tail_entry_seqnum
)) {
1205 error(offsetof(Header
, tail_entry_seqnum
), "Invalid tail seqnum");
1210 if (entry_monotonic_set
&&
1211 (!sd_id128_equal(entry_boot_id
, f
->header
->boot_id
) ||
1212 entry_monotonic
!= le64toh(f
->header
->tail_entry_monotonic
))) {
1213 error(0, "Invalid tail monotonic timestamp");
1218 if (entry_realtime_set
&& entry_realtime
!= le64toh(f
->header
->tail_entry_realtime
)) {
1219 error(0, "Invalid tail realtime timestamp");
1224 /* Second iteration: we follow all objects referenced from the
1225 * two entry points: the object hash table and the entry
1226 * array. We also check that everything referenced (directly
1227 * or indirectly) in the data hash table also exists in the
1228 * entry array, and vice versa. Note that we do not care for
1229 * unreferenced objects. We only care that everything that is
1230 * referenced is consistent. */
1232 r
= verify_entry_array(f
,
1234 entry_fd
, n_entries
,
1235 entry_array_fd
, n_entry_arrays
,
1241 r
= verify_hash_table(f
,
1243 entry_fd
, n_entries
,
1244 entry_array_fd
, n_entry_arrays
,
1253 mmap_cache_close_fd(f
->mmap
, data_fd
);
1254 mmap_cache_close_fd(f
->mmap
, entry_fd
);
1255 mmap_cache_close_fd(f
->mmap
, entry_array_fd
);
1257 safe_close(data_fd
);
1258 safe_close(entry_fd
);
1259 safe_close(entry_array_fd
);
1261 if (first_contained
)
1262 *first_contained
= le64toh(f
->header
->head_entry_realtime
);
1264 *last_validated
= last_sealed_realtime
;
1266 *last_contained
= le64toh(f
->header
->tail_entry_realtime
);
1274 log_error("File corruption detected at %s:"OFSfmt
" (of %llu bytes, %"PRIu64
"%%).",
1277 (unsigned long long) f
->last_stat
.st_size
,
1278 100 * p
/ f
->last_stat
.st_size
);
1281 mmap_cache_close_fd(f
->mmap
, data_fd
);
1282 safe_close(data_fd
);
1285 if (entry_fd
>= 0) {
1286 mmap_cache_close_fd(f
->mmap
, entry_fd
);
1287 safe_close(entry_fd
);
1290 if (entry_array_fd
>= 0) {
1291 mmap_cache_close_fd(f
->mmap
, entry_array_fd
);
1292 safe_close(entry_array_fd
);