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/>.
28 #include "journal-def.h"
29 #include "journal-file.h"
30 #include "journal-authenticate.h"
31 #include "journal-verify.h"
33 static int journal_file_object_verify(JournalFile *f, Object *o) {
37 /* This does various superficial tests about the length an
38 * possible field values. It does not follow any references to
41 switch (o->object.type) {
43 if (le64toh(o->data.entry_offset) <= 0 ||
44 le64toh(o->data.n_entries) <= 0)
47 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
52 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
57 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
60 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
63 if (le64toh(o->entry.seqnum) <= 0 ||
64 le64toh(o->entry.realtime) <= 0)
69 case OBJECT_DATA_HASH_TABLE:
70 case OBJECT_FIELD_HASH_TABLE:
71 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0)
76 case OBJECT_ENTRY_ARRAY:
77 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0)
83 if (le64toh(o->object.size) != sizeof(TagObject))
91 static void draw_progress(uint64_t p, usec_t *last_usec) {
95 if (!isatty(STDOUT_FILENO))
98 z = now(CLOCK_MONOTONIC);
101 if (x != 0 && x + 40 * USEC_PER_MSEC > z)
106 n = (3 * columns()) / 4;
107 j = (n * (unsigned) p) / 65535ULL;
110 fputs("\r\x1B[?25l", stdout);
112 for (i = 0; i < j; i++)
113 fputs("\xe2\x96\x88", stdout);
115 for (i = 0; i < k; i++)
116 fputs("\xe2\x96\x91", stdout);
118 printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU);
120 fputs("\r\x1B[?25h", stdout);
124 static void flush_progress(void) {
127 if (!isatty(STDOUT_FILENO))
130 n = (3 * columns()) / 4;
134 for (i = 0; i < n + 5; i++)
141 static int write_uint64(int fd, uint64_t p) {
144 k = write(fd, &p, sizeof(p));
153 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
168 r = mmap_cache_get(m, fd, PROT_READ, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z);
184 int journal_file_verify(JournalFile *f, const char *key) {
188 uint64_t tag_seqnum = 0, entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
189 sd_id128_t entry_boot_id;
190 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
191 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;
192 usec_t last_usec = 0;
193 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
194 char data_path[] = "/var/tmp/journal-data-XXXXXX",
195 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
196 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
200 data_fd = mkostemp(data_path, O_CLOEXEC);
202 log_error("Failed to create data file: %m");
207 entry_fd = mkostemp(entry_path, O_CLOEXEC);
209 log_error("Failed to create entry file: %m");
214 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
215 if (entry_array_fd < 0) {
216 log_error("Failed to create entry array file: %m");
219 unlink(entry_array_path);
221 /* First iteration: we go through all objects, verify the
222 * superficial structure, headers, hashes. */
224 r = journal_file_hmac_put_header(f);
226 log_error("Failed to calculate HMAC of header.");
230 p = le64toh(f->header->header_size);
232 draw_progress((0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec);
234 r = journal_file_move_to_object(f, -1, p, &o);
236 log_error("Invalid object at %llu", (unsigned long long) p);
240 if (le64toh(f->header->tail_object_offset) < p) {
241 log_error("Invalid tail object pointer.");
248 r = journal_file_object_verify(f, o);
250 log_error("Invalid object contents at %llu", (unsigned long long) p);
254 r = journal_file_hmac_put_object(f, -1, p);
256 log_error("Failed to calculate HMAC at %llu", (unsigned long long) p);
260 if (o->object.flags & OBJECT_COMPRESSED &&
261 !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
262 log_error("Compressed object without compression at %llu", (unsigned long long) p);
267 if (o->object.flags & OBJECT_COMPRESSED &&
268 o->object.type != OBJECT_DATA) {
269 log_error("Compressed non-data object at %llu", (unsigned long long) p);
274 if (o->object.type == OBJECT_TAG) {
276 if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) {
277 log_error("Tag object without authentication at %llu", (unsigned long long) p);
282 if (le64toh(o->tag.seqnum) != tag_seqnum) {
283 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
288 } else if (o->object.type == OBJECT_ENTRY) {
290 r = write_uint64(entry_fd, p);
294 if (!entry_seqnum_set &&
295 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
296 log_error("Head entry sequence number incorrect");
301 if (entry_seqnum_set &&
302 entry_seqnum >= le64toh(o->entry.seqnum)) {
303 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
308 entry_seqnum = le64toh(o->entry.seqnum);
309 entry_seqnum_set = true;
311 if (entry_monotonic_set &&
312 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
313 entry_monotonic > le64toh(o->entry.monotonic)) {
314 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
319 entry_monotonic = le64toh(o->entry.monotonic);
320 entry_boot_id = o->entry.boot_id;
321 entry_monotonic_set = true;
323 if (!entry_realtime_set &&
324 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
325 log_error("Head entry realtime timestamp incorrect");
330 entry_realtime = le64toh(o->entry.realtime);
331 entry_realtime_set = true;
334 } else if (o->object.type == OBJECT_ENTRY_ARRAY) {
336 r = write_uint64(entry_array_fd, p);
340 if (p == le64toh(f->header->entry_array_offset)) {
341 if (found_main_entry_array) {
342 log_error("More than one main entry array at %llu", (unsigned long long) p);
347 found_main_entry_array = true;
352 } else if (o->object.type == OBJECT_DATA) {
354 r = write_uint64(data_fd, p);
360 } else if (o->object.type == OBJECT_FIELD)
362 else if (o->object.type == OBJECT_DATA_HASH_TABLE) {
363 n_data_hash_tables++;
365 if (n_data_hash_tables > 1) {
366 log_error("More than one data hash table at %llu", (unsigned long long) p);
371 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
372 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
373 log_error("Header fields for data hash table invalid.");
377 } else if (o->object.type == OBJECT_FIELD_HASH_TABLE) {
378 n_field_hash_tables++;
380 if (n_field_hash_tables > 1) {
381 log_error("More than one field hash table at %llu", (unsigned long long) p);
386 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
387 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
388 log_error("Header fields for field hash table invalid.");
392 } else if (o->object.type >= _OBJECT_TYPE_MAX)
395 if (p == le64toh(f->header->tail_object_offset))
398 p = p + ALIGN64(le64toh(o->object.size));
401 if (n_objects != le64toh(f->header->n_objects)) {
402 log_error("Object number mismatch");
407 if (n_entries != le64toh(f->header->n_entries)) {
408 log_error("Entry number mismatch");
413 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
414 n_data != le64toh(f->header->n_data)) {
415 log_error("Data number mismatch");
420 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
421 n_fields != le64toh(f->header->n_fields)) {
422 log_error("Field number mismatch");
427 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
428 tag_seqnum != le64toh(f->header->n_tags)) {
429 log_error("Tag number mismatch");
434 if (n_data_hash_tables != 1) {
435 log_error("Missing data hash table");
440 if (n_field_hash_tables != 1) {
441 log_error("Missing field hash table");
446 if (!found_main_entry_array) {
447 log_error("Missing entry array");
452 if (entry_seqnum_set &&
453 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
454 log_error("Invalid tail seqnum");
459 if (entry_monotonic_set &&
460 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
461 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
462 log_error("Invalid tail monotonic timestamp");
467 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
468 log_error("Invalid tail realtime timestamp");
473 /* Second iteration: we go through all objects again, this
474 * time verify all pointers. */
476 p = le64toh(f->header->header_size);
478 draw_progress(0x8000 + (0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec);
480 r = journal_file_move_to_object(f, -1, p, &o);
482 log_error("Invalid object at %llu", (unsigned long long) p);
486 if (o->object.type == OBJECT_ENTRY_ARRAY) {
489 if (le64toh(o->entry_array.next_entry_array_offset) != 0 &&
490 !contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, le64toh(o->entry_array.next_entry_array_offset))) {
491 log_error("Entry array chains up to invalid next array at %llu", (unsigned long long) p);
496 n = journal_file_entry_array_n_items(o);
497 for (i = 0; i < n; i++) {
498 if (le64toh(o->entry_array.items[i]) != 0 &&
499 !contains_uint64(f->mmap, entry_fd, n_entries, le64toh(o->entry_array.items[i]))) {
501 log_error("Entry array points to invalid next array at %llu", (unsigned long long) p);
509 r = journal_file_move_to_object(f, -1, p, &o);
511 log_error("Invalid object at %llu", (unsigned long long) p);
515 if (p == le64toh(f->header->tail_object_offset))
518 p = p + ALIGN64(le64toh(o->object.size));
523 mmap_cache_close_fd(f->mmap, data_fd);
524 mmap_cache_close_fd(f->mmap, entry_fd);
525 mmap_cache_close_fd(f->mmap, entry_array_fd);
527 close_nointr_nofail(data_fd);
528 close_nointr_nofail(entry_fd);
529 close_nointr_nofail(entry_array_fd);
536 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
538 (unsigned long long) p,
539 (unsigned long long) f->last_stat.st_size,
540 (unsigned long long) (100 * p / f->last_stat.st_size));
543 mmap_cache_close_fd(f->mmap, data_fd);
544 close_nointr_nofail(data_fd);
548 mmap_cache_close_fd(f->mmap, entry_fd);
549 close_nointr_nofail(entry_fd);
552 if (entry_array_fd >= 0) {
553 mmap_cache_close_fd(f->mmap, entry_array_fd);
554 close_nointr_nofail(entry_array_fd);