]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journal-verify.c
keymap: fix map name reference
[thirdparty/systemd.git] / src / journal / journal-verify.c
CommitLineData
0284adc6
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
22#include <unistd.h>
23#include <sys/mman.h>
24#include <fcntl.h>
25
26#include "util.h"
27#include "macro.h"
28#include "journal-def.h"
29#include "journal-file.h"
30#include "journal-authenticate.h"
31#include "journal-verify.h"
f59a5f6b 32#include "lookup3.h"
fd5dc320 33#include "compress.h"
f59a5f6b
LP
34
35/* FIXME:
36 *
f59a5f6b 37 * - verify FSPRG
fd5dc320 38 * - Allow building without libgcrypt
86adf873
LP
39 * - check with sparse
40 * - 64bit conversions
f59a5f6b
LP
41 *
42 * */
0284adc6
LP
43
44static int journal_file_object_verify(JournalFile *f, Object *o) {
45 assert(f);
46 assert(o);
47
48 /* This does various superficial tests about the length an
49 * possible field values. It does not follow any references to
50 * other objects. */
51
f59a5f6b
LP
52 if ((o->object.flags & OBJECT_COMPRESSED) &&
53 o->object.type != OBJECT_DATA)
54 return -EBADMSG;
55
0284adc6 56 switch (o->object.type) {
f59a5f6b 57
fd5dc320
LP
58 case OBJECT_DATA: {
59 uint64_t h1, h2;
60
0284adc6
LP
61 if (le64toh(o->data.entry_offset) <= 0 ||
62 le64toh(o->data.n_entries) <= 0)
63 return -EBADMSG;
64
65 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
66 return -EBADMSG;
f59a5f6b 67
fd5dc320 68 h1 = le64toh(o->data.hash);
f59a5f6b 69
fd5dc320
LP
70 if (o->object.flags & OBJECT_COMPRESSED) {
71 void *b = NULL;
72 uint64_t alloc = 0, b_size;
f59a5f6b 73
fd5dc320
LP
74 if (!uncompress_blob(o->data.payload,
75 le64toh(o->object.size) - offsetof(Object, data.payload),
76 &b, &alloc, &b_size))
f59a5f6b 77 return -EBADMSG;
fd5dc320
LP
78
79 h2 = hash64(b, b_size);
80 free(b);
81 } else
82 h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
83
84 if (h1 != h2)
85 return -EBADMSG;
f59a5f6b 86
0284adc6 87 break;
fd5dc320 88 }
0284adc6
LP
89
90 case OBJECT_FIELD:
91 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
92 return -EBADMSG;
93 break;
94
95 case OBJECT_ENTRY:
96 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
97 return -EBADMSG;
98
99 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
100 return -EBADMSG;
101
102 if (le64toh(o->entry.seqnum) <= 0 ||
103 le64toh(o->entry.realtime) <= 0)
104 return -EBADMSG;
105
106 break;
107
108 case OBJECT_DATA_HASH_TABLE:
109 case OBJECT_FIELD_HASH_TABLE:
110 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0)
111 return -EBADMSG;
112
86adf873
LP
113 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
114 return -EBADMSG;
115
0284adc6
LP
116 break;
117
118 case OBJECT_ENTRY_ARRAY:
119 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0)
120 return -EBADMSG;
121
86adf873
LP
122 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
123 return -EBADMSG;
124
0284adc6
LP
125 break;
126
127 case OBJECT_TAG:
128 if (le64toh(o->object.size) != sizeof(TagObject))
129 return -EBADMSG;
130 break;
131 }
132
133 return 0;
134}
135
136static void draw_progress(uint64_t p, usec_t *last_usec) {
137 unsigned n, i, j, k;
138 usec_t z, x;
139
140 if (!isatty(STDOUT_FILENO))
141 return;
142
143 z = now(CLOCK_MONOTONIC);
144 x = *last_usec;
145
146 if (x != 0 && x + 40 * USEC_PER_MSEC > z)
147 return;
148
149 *last_usec = z;
150
151 n = (3 * columns()) / 4;
152 j = (n * (unsigned) p) / 65535ULL;
153 k = n - j;
154
f9fffc31 155 fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
0284adc6
LP
156
157 for (i = 0; i < j; i++)
158 fputs("\xe2\x96\x88", stdout);
159
f9fffc31
LP
160 fputs(ANSI_HIGHLIGHT_OFF, stdout);
161
0284adc6
LP
162 for (i = 0; i < k; i++)
163 fputs("\xe2\x96\x91", stdout);
164
165 printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU);
166
167 fputs("\r\x1B[?25h", stdout);
168 fflush(stdout);
169}
170
171static void flush_progress(void) {
172 unsigned n, i;
173
174 if (!isatty(STDOUT_FILENO))
175 return;
176
177 n = (3 * columns()) / 4;
178
179 putchar('\r');
180
181 for (i = 0; i < n + 5; i++)
182 putchar(' ');
183
184 putchar('\r');
185 fflush(stdout);
186}
187
188static int write_uint64(int fd, uint64_t p) {
189 ssize_t k;
190
191 k = write(fd, &p, sizeof(p));
192 if (k < 0)
193 return -errno;
194 if (k != sizeof(p))
195 return -EIO;
196
197 return 0;
198}
199
200static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
201 uint64_t a, b;
202 int r;
203
204 assert(m);
205 assert(fd >= 0);
206
207 /* Bisection ... */
208
209 a = 0; b = n;
210 while (a < b) {
211 uint64_t c, *z;
212
213 c = (a + b) / 2;
214
86adf873 215 r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z);
0284adc6
LP
216 if (r < 0)
217 return r;
218
219 if (*z == p)
220 return 1;
221
222 if (p < *z)
223 b = c;
224 else
225 a = c;
226 }
227
228 return 0;
229}
230
86adf873
LP
231static int entry_points_to_data(
232 JournalFile *f,
233 int entry_fd,
234 uint64_t n_entries,
235 uint64_t entry_p,
236 uint64_t data_p) {
237
238 int r;
239 uint64_t i, n, a;
240 Object *o;
241 bool found = false;
242
243 assert(f);
244 assert(entry_fd >= 0);
245
246 if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
247 log_error("Data object references invalid entry at %llu", (unsigned long long) data_p);
248 return -EBADMSG;
249 }
250
251 r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
252 if (r < 0)
253 return r;
254
255 n = journal_file_entry_n_items(o);
256 for (i = 0; i < n; i++)
257 if (le64toh(o->entry.items[i].object_offset) == data_p) {
258 found = true;
259 break;
260 }
261
262 if (!found) {
263 log_error("Data object not referenced by linked entry at %llu", (unsigned long long) data_p);
264 return -EBADMSG;
265 }
266
267 /* Check if this entry is also in main entry array. Since the
268 * main entry array has already been verified we can rely on
269 * its consistency.*/
270
271 n = le64toh(f->header->n_entries);
272 a = le64toh(f->header->entry_array_offset);
273 i = 0;
274
275 while (i < n) {
276 uint64_t m, j;
277
278 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
279 if (r < 0)
280 return r;
281
282 m = journal_file_entry_array_n_items(o);
283 for (j = 0; i < n && j < m; i++, j++)
284 if (le64toh(o->entry_array.items[j]) == entry_p)
285 return 0;
286
287 a = le64toh(o->entry_array.next_entry_array_offset);;
288 }
289
290 return 0;
291}
292
293static int verify_data(
294 JournalFile *f,
295 Object *o, uint64_t p,
296 int entry_fd, uint64_t n_entries,
297 int entry_array_fd, uint64_t n_entry_arrays) {
298
299 uint64_t i, n, a, last, q;
300 int r;
301
302 assert(f);
303 assert(o);
304 assert(entry_fd >= 0);
305 assert(entry_array_fd >= 0);
306
307 n = le64toh(o->data.n_entries);
308 a = le64toh(o->data.entry_array_offset);
309
310 /* We already checked this earlier */
311 assert(n > 0);
312
313 last = q = le64toh(o->data.entry_offset);
314 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
315 if (r < 0)
316 return r;
317
2a7273ef 318 i = 1;
86adf873
LP
319 while (i < n) {
320 uint64_t next, m, j;
321
322 if (a == 0) {
323 log_error("Array chain too short at %llu.", (unsigned long long) p);
324 return -EBADMSG;
325 }
326
327 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
328 log_error("Invalid array at %llu.", (unsigned long long) p);
329 return -EBADMSG;
330 }
331
332 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
333 if (r < 0)
334 return r;
335
336 next = le64toh(o->entry_array.next_entry_array_offset);
337 if (next != 0 && next <= a) {
338 log_error("Array chain has cycle at %llu.", (unsigned long long) p);
339 return -EBADMSG;
340 }
341
342 m = journal_file_entry_array_n_items(o);
343 for (j = 0; i < n && j < m; i++, j++) {
344
345 q = le64toh(o->entry_array.items[j]);
346 if (q <= last) {
347 log_error("Data object's entry array not sorted at %llu.", (unsigned long long) p);
348 return -EBADMSG;
349 }
350 last = q;
351
352 r = entry_points_to_data(f, entry_fd, n_entries, q, p);
353 if (r < 0)
354 return r;
355
356 /* Pointer might have moved, reposition */
357 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
358 if (r < 0)
359 return r;
360 }
361
362 a = next;
363 }
364
365 return 0;
366}
367
368static int verify_hash_table(
369 JournalFile *f,
370 int data_fd, uint64_t n_data,
371 int entry_fd, uint64_t n_entries,
372 int entry_array_fd, uint64_t n_entry_arrays,
373 usec_t *last_usec) {
374
375 uint64_t i, n;
376 int r;
377
378 assert(f);
379 assert(data_fd >= 0);
380 assert(entry_fd >= 0);
381 assert(entry_array_fd >= 0);
382 assert(last_usec);
383
384 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
385 for (i = 0; i < n; i++) {
386 uint64_t last = 0, p;
387
388 draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
389
390 p = le64toh(f->data_hash_table[i].head_hash_offset);
391 while (p != 0) {
392 Object *o;
393 uint64_t next;
394
395 if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
396 log_error("Invalid data object at hash entry %llu of %llu.",
397 (unsigned long long) i, (unsigned long long) n);
398 return -EBADMSG;
399 }
400
401 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
402 if (r < 0)
403 return r;
404
405 next = le64toh(o->data.next_hash_offset);
406 if (next != 0 && next <= p) {
407 log_error("Hash chain has a cycle in hash entry %llu of %llu.",
408 (unsigned long long) i, (unsigned long long) n);
409 return -EBADMSG;
410 }
411
412 if (le64toh(o->data.hash) % n != i) {
413 log_error("Hash value mismatch in hash entry %llu of %llu.",
414 (unsigned long long) i, (unsigned long long) n);
415 return -EBADMSG;
416 }
417
418 r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
419 if (r < 0)
420 return r;
421
422 last = p;
423 p = next;
424 }
425
426 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
427 log_error("Tail hash pointer mismatch in hash table.");
428 return -EBADMSG;
429 }
430 }
431
432 return 0;
433}
434
435static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
436 uint64_t n, h, q;
437 int r;
438 assert(f);
439
440 n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
441 h = hash % n;
442
443 q = le64toh(f->data_hash_table[h].head_hash_offset);
444 while (q != 0) {
445 Object *o;
446
447 if (p == q)
448 return 1;
449
450 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
451 if (r < 0)
452 return r;
453
454 q = le64toh(o->data.next_hash_offset);
455 }
456
457 return 0;
458}
459
460static int verify_entry(
461 JournalFile *f,
462 Object *o, uint64_t p,
463 int data_fd, uint64_t n_data) {
464
465 uint64_t i, n;
466 int r;
467
468 assert(f);
469 assert(o);
470 assert(data_fd >= 0);
471
472 n = journal_file_entry_n_items(o);
473 for (i = 0; i < n; i++) {
474 uint64_t q, h;
475 Object *u;
476
477 q = le64toh(o->entry.items[i].object_offset);
478 h = le64toh(o->entry.items[i].hash);
479
480 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
481 log_error("Invalid data object at entry %llu.",
482 (unsigned long long) o);
483 return -EBADMSG;
484 }
485
486 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
487 if (r < 0)
488 return r;
489
490 if (le64toh(u->data.hash) != h) {
491 log_error("Hash mismatch for data object at entry %llu.",
492 (unsigned long long) p);
493 return -EBADMSG;
494 }
495
496 r = data_object_in_hash_table(f, h, q);
497 if (r < 0)
498 return r;
499 if (r == 0) {
500 log_error("Data object missing from hash at entry %llu.",
501 (unsigned long long) p);
502 return -EBADMSG;
503 }
504 }
505
506 return 0;
507}
508
509static int verify_entry_array(
510 JournalFile *f,
511 int data_fd, uint64_t n_data,
512 int entry_fd, uint64_t n_entries,
513 int entry_array_fd, uint64_t n_entry_arrays,
514 usec_t *last_usec) {
515
516 uint64_t i = 0, a, n, last = 0;
517 int r;
518
519 assert(f);
520 assert(data_fd >= 0);
521 assert(entry_fd >= 0);
522 assert(entry_array_fd >= 0);
523 assert(last_usec);
524
525 n = le64toh(f->header->n_entries);
526 a = le64toh(f->header->entry_array_offset);
527 while (i < n) {
528 uint64_t next, m, j;
529 Object *o;
530
531 draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
532
533 if (a == 0) {
534 log_error("Array chain too short at %llu of %llu.",
535 (unsigned long long) i, (unsigned long long) n);
536 return -EBADMSG;
537 }
538
539 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
540 log_error("Invalid array at %llu of %llu.",
541 (unsigned long long) i, (unsigned long long) n);
542 return -EBADMSG;
543 }
544
545 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
546 if (r < 0)
547 return r;
548
549 next = le64toh(o->entry_array.next_entry_array_offset);
550 if (next != 0 && next <= a) {
551 log_error("Array chain has cycle at %llu of %llu.",
552 (unsigned long long) i, (unsigned long long) n);
553 return -EBADMSG;
554 }
555
556 m = journal_file_entry_array_n_items(o);
557 for (j = 0; i < n && j < m; i++, j++) {
558 uint64_t p;
559
560 p = le64toh(o->entry_array.items[j]);
561 if (p <= last) {
562 log_error("Entry array not sorted at %llu of %llu.",
563 (unsigned long long) i, (unsigned long long) n);
564 return -EBADMSG;
565 }
566 last = p;
567
568 if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
569 log_error("Invalid array entry at %llu of %llu.",
570 (unsigned long long) i, (unsigned long long) n);
571 return -EBADMSG;
572 }
573
574 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
575 if (r < 0)
576 return r;
577
578 r = verify_entry(f, o, p, data_fd, n_data);
579 if (r < 0)
580 return r;
581
582 /* Pointer might have moved, reposition */
583 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
584 if (r < 0)
585 return r;
586 }
587
588 a = next;
589 }
590
591 return 0;
592}
593
0284adc6
LP
594int journal_file_verify(JournalFile *f, const char *key) {
595 int r;
596 Object *o;
597 uint64_t p = 0;
598 uint64_t tag_seqnum = 0, entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
599 sd_id128_t entry_boot_id;
600 bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
601 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;
602 usec_t last_usec = 0;
603 int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
604 char data_path[] = "/var/tmp/journal-data-XXXXXX",
605 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
606 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
607
608 assert(f);
609
610 data_fd = mkostemp(data_path, O_CLOEXEC);
611 if (data_fd < 0) {
612 log_error("Failed to create data file: %m");
1137e6c7 613 r = -errno;
0284adc6
LP
614 goto fail;
615 }
616 unlink(data_path);
617
618 entry_fd = mkostemp(entry_path, O_CLOEXEC);
619 if (entry_fd < 0) {
620 log_error("Failed to create entry file: %m");
1137e6c7 621 r = -errno;
0284adc6
LP
622 goto fail;
623 }
624 unlink(entry_path);
625
626 entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
627 if (entry_array_fd < 0) {
628 log_error("Failed to create entry array file: %m");
1137e6c7 629 r = -errno;
0284adc6
LP
630 goto fail;
631 }
632 unlink(entry_array_path);
633
634 /* First iteration: we go through all objects, verify the
635 * superficial structure, headers, hashes. */
636
637 r = journal_file_hmac_put_header(f);
638 if (r < 0) {
639 log_error("Failed to calculate HMAC of header.");
640 goto fail;
641 }
642
643 p = le64toh(f->header->header_size);
644 while (p != 0) {
86adf873 645 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
0284adc6
LP
646
647 r = journal_file_move_to_object(f, -1, p, &o);
648 if (r < 0) {
649 log_error("Invalid object at %llu", (unsigned long long) p);
650 goto fail;
651 }
652
653 if (le64toh(f->header->tail_object_offset) < p) {
654 log_error("Invalid tail object pointer.");
655 r = -EBADMSG;
656 goto fail;
657 }
658
659 n_objects ++;
660
661 r = journal_file_object_verify(f, o);
662 if (r < 0) {
663 log_error("Invalid object contents at %llu", (unsigned long long) p);
664 goto fail;
665 }
666
0284adc6
LP
667 if (o->object.flags & OBJECT_COMPRESSED &&
668 !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
669 log_error("Compressed object without compression at %llu", (unsigned long long) p);
670 r = -EBADMSG;
671 goto fail;
672 }
673
f59a5f6b
LP
674 r = journal_file_hmac_put_object(f, -1, p);
675 if (r < 0) {
676 log_error("Failed to calculate HMAC at %llu", (unsigned long long) p);
0284adc6
LP
677 goto fail;
678 }
679
680 if (o->object.type == OBJECT_TAG) {
681
682 if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) {
683 log_error("Tag object without authentication at %llu", (unsigned long long) p);
684 r = -EBADMSG;
685 goto fail;
686 }
687
688 if (le64toh(o->tag.seqnum) != tag_seqnum) {
689 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
690 r = -EBADMSG;
691 goto fail;
692 }
693
694 } else if (o->object.type == OBJECT_ENTRY) {
695
696 r = write_uint64(entry_fd, p);
697 if (r < 0)
698 goto fail;
699
700 if (!entry_seqnum_set &&
701 le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
702 log_error("Head entry sequence number incorrect");
703 r = -EBADMSG;
704 goto fail;
705 }
706
707 if (entry_seqnum_set &&
708 entry_seqnum >= le64toh(o->entry.seqnum)) {
709 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
710 r = -EBADMSG;
711 goto fail;
712 }
713
714 entry_seqnum = le64toh(o->entry.seqnum);
715 entry_seqnum_set = true;
716
717 if (entry_monotonic_set &&
718 sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
719 entry_monotonic > le64toh(o->entry.monotonic)) {
720 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
721 r = -EBADMSG;
722 goto fail;
723 }
724
725 entry_monotonic = le64toh(o->entry.monotonic);
726 entry_boot_id = o->entry.boot_id;
727 entry_monotonic_set = true;
728
729 if (!entry_realtime_set &&
730 le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
731 log_error("Head entry realtime timestamp incorrect");
732 r = -EBADMSG;
733 goto fail;
734 }
735
736 entry_realtime = le64toh(o->entry.realtime);
737 entry_realtime_set = true;
738
739 n_entries ++;
740 } else if (o->object.type == OBJECT_ENTRY_ARRAY) {
741
742 r = write_uint64(entry_array_fd, p);
743 if (r < 0)
744 goto fail;
745
746 if (p == le64toh(f->header->entry_array_offset)) {
747 if (found_main_entry_array) {
748 log_error("More than one main entry array at %llu", (unsigned long long) p);
749 r = -EBADMSG;
750 goto fail;
751 }
752
753 found_main_entry_array = true;
754 }
755
756 n_entry_arrays++;
757
758 } else if (o->object.type == OBJECT_DATA) {
759
760 r = write_uint64(data_fd, p);
761 if (r < 0)
762 goto fail;
763
764 n_data++;
765
766 } else if (o->object.type == OBJECT_FIELD)
767 n_fields++;
768 else if (o->object.type == OBJECT_DATA_HASH_TABLE) {
769 n_data_hash_tables++;
770
771 if (n_data_hash_tables > 1) {
772 log_error("More than one data hash table at %llu", (unsigned long long) p);
773 r = -EBADMSG;
774 goto fail;
775 }
776
777 if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
778 le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
779 log_error("Header fields for data hash table invalid.");
780 r = -EBADMSG;
781 goto fail;
782 }
783 } else if (o->object.type == OBJECT_FIELD_HASH_TABLE) {
784 n_field_hash_tables++;
785
786 if (n_field_hash_tables > 1) {
787 log_error("More than one field hash table at %llu", (unsigned long long) p);
788 r = -EBADMSG;
789 goto fail;
790 }
791
792 if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
793 le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
794 log_error("Header fields for field hash table invalid.");
795 r = -EBADMSG;
796 goto fail;
797 }
798 } else if (o->object.type >= _OBJECT_TYPE_MAX)
799 n_weird ++;
800
801 if (p == le64toh(f->header->tail_object_offset))
802 p = 0;
803 else
804 p = p + ALIGN64(le64toh(o->object.size));
805 }
806
807 if (n_objects != le64toh(f->header->n_objects)) {
808 log_error("Object number mismatch");
809 r = -EBADMSG;
810 goto fail;
811 }
812
813 if (n_entries != le64toh(f->header->n_entries)) {
814 log_error("Entry number mismatch");
815 r = -EBADMSG;
816 goto fail;
817 }
818
819 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
820 n_data != le64toh(f->header->n_data)) {
821 log_error("Data number mismatch");
822 r = -EBADMSG;
823 goto fail;
824 }
825
826 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
827 n_fields != le64toh(f->header->n_fields)) {
828 log_error("Field number mismatch");
829 r = -EBADMSG;
830 goto fail;
831 }
832
833 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
834 tag_seqnum != le64toh(f->header->n_tags)) {
835 log_error("Tag number mismatch");
836 r = -EBADMSG;
837 goto fail;
838 }
839
840 if (n_data_hash_tables != 1) {
841 log_error("Missing data hash table");
842 r = -EBADMSG;
843 goto fail;
844 }
845
846 if (n_field_hash_tables != 1) {
847 log_error("Missing field hash table");
848 r = -EBADMSG;
849 goto fail;
850 }
851
852 if (!found_main_entry_array) {
853 log_error("Missing entry array");
854 r = -EBADMSG;
855 goto fail;
856 }
857
858 if (entry_seqnum_set &&
859 entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
860 log_error("Invalid tail seqnum");
861 r = -EBADMSG;
862 goto fail;
863 }
864
865 if (entry_monotonic_set &&
866 (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
867 entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
868 log_error("Invalid tail monotonic timestamp");
869 r = -EBADMSG;
870 goto fail;
871 }
872
873 if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
874 log_error("Invalid tail realtime timestamp");
875 r = -EBADMSG;
876 goto fail;
877 }
878
86adf873
LP
879 /* Second iteration: we follow all objects referenced from the
880 * two entry points: the object hash table and the entry
881 * array. We also check that everything referenced (directly
882 * or indirectly) in the data hash table also exists in the
883 * entry array, and vice versa. Note that we do not care for
884 * unreferenced objects. We only care that everything that is
885 * referenced is consistent. */
886
887 r = verify_entry_array(f,
888 data_fd, n_data,
889 entry_fd, n_entries,
890 entry_array_fd, n_entry_arrays,
891 &last_usec);
892 if (r < 0)
893 goto fail;
0284adc6 894
86adf873
LP
895 r = verify_hash_table(f,
896 data_fd, n_data,
897 entry_fd, n_entries,
898 entry_array_fd, n_entry_arrays,
899 &last_usec);
900 if (r < 0)
901 goto fail;
0284adc6
LP
902
903 flush_progress();
904
905 mmap_cache_close_fd(f->mmap, data_fd);
906 mmap_cache_close_fd(f->mmap, entry_fd);
907 mmap_cache_close_fd(f->mmap, entry_array_fd);
908
909 close_nointr_nofail(data_fd);
910 close_nointr_nofail(entry_fd);
911 close_nointr_nofail(entry_array_fd);
912
913 return 0;
914
915fail:
916 flush_progress();
917
918 log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
919 f->path,
920 (unsigned long long) p,
921 (unsigned long long) f->last_stat.st_size,
922 (unsigned long long) (100 * p / f->last_stat.st_size));
923
924 if (data_fd >= 0) {
925 mmap_cache_close_fd(f->mmap, data_fd);
926 close_nointr_nofail(data_fd);
927 }
928
929 if (entry_fd >= 0) {
930 mmap_cache_close_fd(f->mmap, entry_fd);
931 close_nointr_nofail(entry_fd);
932 }
933
934 if (entry_array_fd >= 0) {
935 mmap_cache_close_fd(f->mmap, entry_array_fd);
936 close_nointr_nofail(entry_array_fd);
937 }
938
939 return r;
940}