chiark / gitweb /
90739a66c2cde0c2283540764b78fe3a3915c090
[elogind.git] / src / journal / journal-verify.c
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 #include <stddef.h>
26
27 #include "util.h"
28 #include "macro.h"
29 #include "journal-def.h"
30 #include "journal-file.h"
31 #include "journal-authenticate.h"
32 #include "journal-verify.h"
33 #include "lookup3.h"
34 #include "compress.h"
35 #include "fsprg.h"
36
37 static int journal_file_object_verify(JournalFile *f, Object *o) {
38         uint64_t i;
39
40         assert(f);
41         assert(o);
42
43         /* This does various superficial tests about the length an
44          * possible field values. It does not follow any references to
45          * other objects. */
46
47         if ((o->object.flags & OBJECT_COMPRESSED) &&
48             o->object.type != OBJECT_DATA)
49                 return -EBADMSG;
50
51         switch (o->object.type) {
52
53         case OBJECT_DATA: {
54                 uint64_t h1, h2;
55
56                 if (le64toh(o->data.entry_offset) <= 0 ||
57                     le64toh(o->data.n_entries) <= 0)
58                         return -EBADMSG;
59
60                 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
61                         return -EBADMSG;
62
63                 h1 = le64toh(o->data.hash);
64
65                 if (o->object.flags & OBJECT_COMPRESSED) {
66                         void *b = NULL;
67                         uint64_t alloc = 0, b_size;
68
69                         if (!uncompress_blob(o->data.payload,
70                                              le64toh(o->object.size) - offsetof(Object, data.payload),
71                                              &b, &alloc, &b_size))
72                                 return -EBADMSG;
73
74                         h2 = hash64(b, b_size);
75                         free(b);
76                 } else
77                         h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
78
79                 if (h1 != h2)
80                         return -EBADMSG;
81
82                 if (!VALID64(o->data.next_hash_offset) ||
83                     !VALID64(o->data.next_field_offset) ||
84                     !VALID64(o->data.entry_offset) ||
85                     !VALID64(o->data.entry_array_offset))
86                         return -EBADMSG;
87
88                 break;
89         }
90
91         case OBJECT_FIELD:
92                 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
93                         return -EBADMSG;
94
95                 if (!VALID64(o->field.next_hash_offset) ||
96                     !VALID64(o->field.head_data_offset))
97                         return -EBADMSG;
98                 break;
99
100         case OBJECT_ENTRY:
101                 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
102                         return -EBADMSG;
103
104                 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
105                         return -EBADMSG;
106
107                 if (le64toh(o->entry.seqnum) <= 0 ||
108                     !VALID_REALTIME(le64toh(o->entry.realtime)) ||
109                     !VALID_MONOTONIC(le64toh(o->entry.monotonic)))
110                         return -EBADMSG;
111
112                 for (i = 0; i < journal_file_entry_n_items(o); i++) {
113                         if (o->entry.items[i].object_offset == 0 ||
114                             !VALID64(o->entry.items[i].object_offset))
115                                 return -EBADMSG;
116                 }
117
118                 break;
119
120         case OBJECT_DATA_HASH_TABLE:
121         case OBJECT_FIELD_HASH_TABLE:
122                 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0)
123                         return -EBADMSG;
124
125                 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
126                         return -EBADMSG;
127
128                 for (i = 0; i < journal_file_hash_table_n_items(o); i++) {
129                         if (o->hash_table.items[i].head_hash_offset != 0 &&
130                             !VALID64(le64toh(o->hash_table.items[i].head_hash_offset)))
131                                 return -EBADMSG;
132                         if (o->hash_table.items[i].tail_hash_offset != 0 &&
133                             !VALID64(le64toh(o->hash_table.items[i].tail_hash_offset)))
134                                 return -EBADMSG;
135
136                         if ((o->hash_table.items[i].head_hash_offset != 0) !=
137                             (o->hash_table.items[i].tail_hash_offset != 0))
138                                 return -EBADMSG;
139                 }
140
141                 break;
142
143         case OBJECT_ENTRY_ARRAY:
144                 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0)
145                         return -EBADMSG;
146
147                 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
148                         return -EBADMSG;
149
150                 if (!VALID64(o->entry_array.next_entry_array_offset))
151                         return -EBADMSG;
152
153                 for (i = 0; i < journal_file_entry_array_n_items(o); i++)
154                         if (o->entry_array.items[i] != 0 &&
155                             !VALID64(o->entry_array.items[i]))
156                                 return -EBADMSG;
157
158                 break;
159
160         case OBJECT_TAG:
161                 if (le64toh(o->object.size) != sizeof(TagObject))
162                         return -EBADMSG;
163
164                 if (!VALID_EPOCH(o->tag.epoch))
165                         return -EBADMSG;
166
167                 break;
168         }
169
170         return 0;
171 }
172
173 static void draw_progress(uint64_t p, usec_t *last_usec) {
174         unsigned n, i, j, k;
175         usec_t z, x;
176
177         if (!isatty(STDOUT_FILENO))
178                 return;
179
180         z = now(CLOCK_MONOTONIC);
181         x = *last_usec;
182
183         if (x != 0 && x + 40 * USEC_PER_MSEC > z)
184                 return;
185
186         *last_usec = z;
187
188         n = (3 * columns()) / 4;
189         j = (n * (unsigned) p) / 65535ULL;
190         k = n - j;
191
192         fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
193
194         for (i = 0; i < j; i++)
195                 fputs("\xe2\x96\x88", stdout);
196
197         fputs(ANSI_HIGHLIGHT_OFF, stdout);
198
199         for (i = 0; i < k; i++)
200                 fputs("\xe2\x96\x91", stdout);
201
202         printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU);
203
204         fputs("\r\x1B[?25h", stdout);
205         fflush(stdout);
206 }
207
208 static void flush_progress(void) {
209         unsigned n, i;
210
211         if (!isatty(STDOUT_FILENO))
212                 return;
213
214         n = (3 * columns()) / 4;
215
216         putchar('\r');
217
218         for (i = 0; i < n + 5; i++)
219                 putchar(' ');
220
221         putchar('\r');
222         fflush(stdout);
223 }
224
225 static int write_uint64(int fd, uint64_t p) {
226         ssize_t k;
227
228         k = write(fd, &p, sizeof(p));
229         if (k < 0)
230                 return -errno;
231         if (k != sizeof(p))
232                 return -EIO;
233
234         return 0;
235 }
236
237 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
238         uint64_t a, b;
239         int r;
240
241         assert(m);
242         assert(fd >= 0);
243
244         /* Bisection ... */
245
246         a = 0; b = n;
247         while (a < b) {
248                 uint64_t c, *z;
249
250                 c = (a + b) / 2;
251
252                 r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z);
253                 if (r < 0)
254                         return r;
255
256                 if (*z == p)
257                         return 1;
258
259                 if (a + 1 >= b)
260                         return 0;
261
262                 if (p < *z)
263                         b = c;
264                 else {
265                         a = c;
266                 }
267         }
268
269         return 0;
270 }
271
272 static int entry_points_to_data(
273                 JournalFile *f,
274                 int entry_fd,
275                 uint64_t n_entries,
276                 uint64_t entry_p,
277                 uint64_t data_p) {
278
279         int r;
280         uint64_t i, n, a;
281         Object *o;
282         bool found = false;
283
284         assert(f);
285         assert(entry_fd >= 0);
286
287         if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
288                 log_error("Data object references invalid entry at %llu", (unsigned long long) data_p);
289                 return -EBADMSG;
290         }
291
292         r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
293         if (r < 0)
294                 return r;
295
296         n = journal_file_entry_n_items(o);
297         for (i = 0; i < n; i++)
298                 if (le64toh(o->entry.items[i].object_offset) == data_p) {
299                         found = true;
300                         break;
301                 }
302
303         if (!found) {
304                 log_error("Data object not referenced by linked entry at %llu", (unsigned long long) data_p);
305                 return -EBADMSG;
306         }
307
308         /* Check if this entry is also in main entry array. Since the
309          * main entry array has already been verified we can rely on
310          * its consistency.*/
311
312         i = 0;
313         n = le64toh(f->header->n_entries);
314         a = le64toh(f->header->entry_array_offset);
315
316         while (i < n) {
317                 uint64_t m, u;
318
319                 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
320                 if (r < 0)
321                         return r;
322
323                 m = journal_file_entry_array_n_items(o);
324                 u = MIN(n - i, m);
325
326                 if (entry_p <= le64toh(o->entry_array.items[u-1])) {
327                         uint64_t x, y, z;
328
329                         x = 0;
330                         y = u;
331
332                         while (x < y) {
333                                 z = (x + y) / 2;
334
335                                 if (le64toh(o->entry_array.items[z]) == entry_p)
336                                         return 0;
337
338                                 if (x + 1 >= y)
339                                         break;
340
341                                 if (entry_p < le64toh(o->entry_array.items[z]))
342                                         y = z;
343                                 else
344                                         x = z;
345                         }
346
347                         log_error("Entry object doesn't exist in main entry array at %llu", (unsigned long long) entry_p);
348                         return -EBADMSG;
349                 }
350
351                 i += u;
352                 a = le64toh(o->entry_array.next_entry_array_offset);
353         }
354
355         return 0;
356 }
357
358 static int verify_data(
359                 JournalFile *f,
360                 Object *o, uint64_t p,
361                 int entry_fd, uint64_t n_entries,
362                 int entry_array_fd, uint64_t n_entry_arrays) {
363
364         uint64_t i, n, a, last, q;
365         int r;
366
367         assert(f);
368         assert(o);
369         assert(entry_fd >= 0);
370         assert(entry_array_fd >= 0);
371
372         n = le64toh(o->data.n_entries);
373         a = le64toh(o->data.entry_array_offset);
374
375         /* We already checked this earlier */
376         assert(n > 0);
377
378         last = q = le64toh(o->data.entry_offset);
379         r = entry_points_to_data(f, entry_fd, n_entries, q, p);
380         if (r < 0)
381                 return r;
382
383         i = 1;
384         while (i < n) {
385                 uint64_t next, m, j;
386
387                 if (a == 0) {
388                         log_error("Array chain too short at %llu", (unsigned long long) p);
389                         return -EBADMSG;
390                 }
391
392                 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
393                         log_error("Invalid array at %llu", (unsigned long long) p);
394                         return -EBADMSG;
395                 }
396
397                 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
398                 if (r < 0)
399                         return r;
400
401                 next = le64toh(o->entry_array.next_entry_array_offset);
402                 if (next != 0 && next <= a) {
403                         log_error("Array chain has cycle at %llu", (unsigned long long) p);
404                         return -EBADMSG;
405                 }
406
407                 m = journal_file_entry_array_n_items(o);
408                 for (j = 0; i < n && j < m; i++, j++) {
409
410                         q = le64toh(o->entry_array.items[j]);
411                         if (q <= last) {
412                                 log_error("Data object's entry array not sorted at %llu", (unsigned long long) p);
413                                 return -EBADMSG;
414                         }
415                         last = q;
416
417                         r = entry_points_to_data(f, entry_fd, n_entries, q, p);
418                         if (r < 0)
419                                 return r;
420
421                         /* Pointer might have moved, reposition */
422                         r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
423                         if (r < 0)
424                                 return r;
425                 }
426
427                 a = next;
428         }
429
430         return 0;
431 }
432
433 static int verify_hash_table(
434                 JournalFile *f,
435                 int data_fd, uint64_t n_data,
436                 int entry_fd, uint64_t n_entries,
437                 int entry_array_fd, uint64_t n_entry_arrays,
438                 usec_t *last_usec,
439                 bool show_progress) {
440
441         uint64_t i, n;
442         int r;
443
444         assert(f);
445         assert(data_fd >= 0);
446         assert(entry_fd >= 0);
447         assert(entry_array_fd >= 0);
448         assert(last_usec);
449
450         n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
451         for (i = 0; i < n; i++) {
452                 uint64_t last = 0, p;
453
454                 if (show_progress)
455                         draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
456
457                 p = le64toh(f->data_hash_table[i].head_hash_offset);
458                 while (p != 0) {
459                         Object *o;
460                         uint64_t next;
461
462                         if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
463                                 log_error("Invalid data object at hash entry %llu of %llu",
464                                           (unsigned long long) i, (unsigned long long) n);
465                                 return -EBADMSG;
466                         }
467
468                         r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
469                         if (r < 0)
470                                 return r;
471
472                         next = le64toh(o->data.next_hash_offset);
473                         if (next != 0 && next <= p) {
474                                 log_error("Hash chain has a cycle in hash entry %llu of %llu",
475                                           (unsigned long long) i, (unsigned long long) n);
476                                 return -EBADMSG;
477                         }
478
479                         if (le64toh(o->data.hash) % n != i) {
480                                 log_error("Hash value mismatch in hash entry %llu of %llu",
481                                           (unsigned long long) i, (unsigned long long) n);
482                                 return -EBADMSG;
483                         }
484
485                         r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
486                         if (r < 0)
487                                 return r;
488
489                         last = p;
490                         p = next;
491                 }
492
493                 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
494                         log_error("Tail hash pointer mismatch in hash table");
495                         return -EBADMSG;
496                 }
497         }
498
499         return 0;
500 }
501
502 static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
503         uint64_t n, h, q;
504         int r;
505         assert(f);
506
507         n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
508         h = hash % n;
509
510         q = le64toh(f->data_hash_table[h].head_hash_offset);
511         while (q != 0) {
512                 Object *o;
513
514                 if (p == q)
515                         return 1;
516
517                 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
518                 if (r < 0)
519                         return r;
520
521                 q = le64toh(o->data.next_hash_offset);
522         }
523
524         return 0;
525 }
526
527 static int verify_entry(
528                 JournalFile *f,
529                 Object *o, uint64_t p,
530                 int data_fd, uint64_t n_data) {
531
532         uint64_t i, n;
533         int r;
534
535         assert(f);
536         assert(o);
537         assert(data_fd >= 0);
538
539         n = journal_file_entry_n_items(o);
540         for (i = 0; i < n; i++) {
541                 uint64_t q, h;
542                 Object *u;
543
544                 q = le64toh(o->entry.items[i].object_offset);
545                 h = le64toh(o->entry.items[i].hash);
546
547                 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
548                         log_error("Invalid data object at entry %llu",
549                                   (unsigned long long) p);
550                                 return -EBADMSG;
551                         }
552
553                 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
554                 if (r < 0)
555                         return r;
556
557                 if (le64toh(u->data.hash) != h) {
558                         log_error("Hash mismatch for data object at entry %llu",
559                                   (unsigned long long) p);
560                         return -EBADMSG;
561                 }
562
563                 r = data_object_in_hash_table(f, h, q);
564                 if (r < 0)
565                         return r;
566                 if (r == 0) {
567                         log_error("Data object missing from hash at entry %llu",
568                                   (unsigned long long) p);
569                         return -EBADMSG;
570                 }
571         }
572
573         return 0;
574 }
575
576 static int verify_entry_array(
577                 JournalFile *f,
578                 int data_fd, uint64_t n_data,
579                 int entry_fd, uint64_t n_entries,
580                 int entry_array_fd, uint64_t n_entry_arrays,
581                 usec_t *last_usec,
582                 bool show_progress) {
583
584         uint64_t i = 0, a, n, last = 0;
585         int r;
586
587         assert(f);
588         assert(data_fd >= 0);
589         assert(entry_fd >= 0);
590         assert(entry_array_fd >= 0);
591         assert(last_usec);
592
593         n = le64toh(f->header->n_entries);
594         a = le64toh(f->header->entry_array_offset);
595         while (i < n) {
596                 uint64_t next, m, j;
597                 Object *o;
598
599                 if (show_progress)
600                         draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
601
602                 if (a == 0) {
603                         log_error("Array chain too short at %llu of %llu",
604                                   (unsigned long long) i, (unsigned long long) n);
605                         return -EBADMSG;
606                 }
607
608                 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
609                         log_error("Invalid array at %llu of %llu",
610                                   (unsigned long long) i, (unsigned long long) n);
611                         return -EBADMSG;
612                 }
613
614                 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
615                 if (r < 0)
616                         return r;
617
618                 next = le64toh(o->entry_array.next_entry_array_offset);
619                 if (next != 0 && next <= a) {
620                         log_error("Array chain has cycle at %llu of %llu",
621                                   (unsigned long long) i, (unsigned long long) n);
622                         return -EBADMSG;
623                 }
624
625                 m = journal_file_entry_array_n_items(o);
626                 for (j = 0; i < n && j < m; i++, j++) {
627                         uint64_t p;
628
629                         p = le64toh(o->entry_array.items[j]);
630                         if (p <= last) {
631                                 log_error("Entry array not sorted at %llu of %llu",
632                                           (unsigned long long) i, (unsigned long long) n);
633                                 return -EBADMSG;
634                         }
635                         last = p;
636
637                         if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
638                                 log_error("Invalid array entry at %llu of %llu",
639                                           (unsigned long long) i, (unsigned long long) n);
640                                 return -EBADMSG;
641                         }
642
643                         r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
644                         if (r < 0)
645                                 return r;
646
647                         r = verify_entry(f, o, p, data_fd, n_data);
648                         if (r < 0)
649                                 return r;
650
651                         /* Pointer might have moved, reposition */
652                         r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
653                         if (r < 0)
654                                 return r;
655                 }
656
657                 a = next;
658         }
659
660         return 0;
661 }
662
663 int journal_file_verify(
664                 JournalFile *f,
665                 const char *key,
666                 usec_t *first_validated, usec_t *last_validated, usec_t *last_contained,
667                 bool show_progress) {
668         int r;
669         Object *o;
670         uint64_t p = 0, last_tag = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0;
671         uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
672         sd_id128_t entry_boot_id;
673         bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
674         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;
675         usec_t last_usec = 0;
676         int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
677         char data_path[] = "/var/tmp/journal-data-XXXXXX",
678                 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
679                 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
680         unsigned i;
681         bool found_last;
682
683         assert(f);
684
685         if (key) {
686 #ifdef HAVE_GCRYPT
687                 r = journal_file_parse_verification_key(f, key);
688                 if (r < 0) {
689                         log_error("Failed to parse seed.");
690                         return r;
691                 }
692 #else
693                 return -ENOTSUP;
694 #endif
695         } else if (f->seal)
696                 return -ENOKEY;
697
698         data_fd = mkostemp(data_path, O_CLOEXEC);
699         if (data_fd < 0) {
700                 log_error("Failed to create data file: %m");
701                 r = -errno;
702                 goto fail;
703         }
704         unlink(data_path);
705
706         entry_fd = mkostemp(entry_path, O_CLOEXEC);
707         if (entry_fd < 0) {
708                 log_error("Failed to create entry file: %m");
709                 r = -errno;
710                 goto fail;
711         }
712         unlink(entry_path);
713
714         entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
715         if (entry_array_fd < 0) {
716                 log_error("Failed to create entry array file: %m");
717                 r = -errno;
718                 goto fail;
719         }
720         unlink(entry_array_path);
721
722 #ifdef HAVE_GCRYPT
723         if ((le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) != 0)
724 #else
725         if (f->header->compatible_flags != 0)
726 #endif
727         {
728                 log_error("Cannot verify file with unknown extensions.");
729                 r = -ENOTSUP;
730                 goto fail;
731         }
732
733         for (i = 0; i < sizeof(f->header->reserved); i++)
734                 if (f->header->reserved[i] != 0) {
735                         log_error("Reserved field in non-zero.");
736                         r = -EBADMSG;
737                         goto fail;
738                 }
739
740         /* First iteration: we go through all objects, verify the
741          * superficial structure, headers, hashes. */
742
743         p = le64toh(f->header->header_size);
744         while (p != 0) {
745                 if (show_progress)
746                         draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
747
748                 r = journal_file_move_to_object(f, -1, p, &o);
749                 if (r < 0) {
750                         log_error("Invalid object at %llu", (unsigned long long) p);
751                         goto fail;
752                 }
753
754                 if (p > le64toh(f->header->tail_object_offset)) {
755                         log_error("Invalid tail object pointer");
756                         r = -EBADMSG;
757                         goto fail;
758                 }
759
760                 if (p == le64toh(f->header->tail_object_offset))
761                         found_last = true;
762
763                 n_objects ++;
764
765                 r = journal_file_object_verify(f, o);
766                 if (r < 0) {
767                         log_error("Invalid object contents at %llu", (unsigned long long) p);
768                         goto fail;
769                 }
770
771                 if ((o->object.flags & OBJECT_COMPRESSED) && !JOURNAL_HEADER_COMPRESSED(f->header)) {
772                         log_error("Compressed object in file without compression at %llu", (unsigned long long) p);
773                         r = -EBADMSG;
774                         goto fail;
775                 }
776
777                 switch (o->object.type) {
778
779                 case OBJECT_DATA:
780                         r = write_uint64(data_fd, p);
781                         if (r < 0)
782                                 goto fail;
783
784                         n_data++;
785                         break;
786
787                 case OBJECT_FIELD:
788                         n_fields++;
789                         break;
790
791                 case OBJECT_ENTRY:
792                         if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) {
793                                 log_error("First entry before first tag at %llu", (unsigned long long) p);
794                                 r = -EBADMSG;
795                                 goto fail;
796                         }
797
798                         r = write_uint64(entry_fd, p);
799                         if (r < 0)
800                                 goto fail;
801
802                         if (le64toh(o->entry.realtime) < last_tag_realtime) {
803                                 log_error("Older entry after newer tag at %llu", (unsigned long long) p);
804                                 r = -EBADMSG;
805                                 goto fail;
806                         }
807
808                         if (!entry_seqnum_set &&
809                             le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
810                                 log_error("Head entry sequence number incorrect at %llu", (unsigned long long) p);
811                                 r = -EBADMSG;
812                                 goto fail;
813                         }
814
815                         if (entry_seqnum_set &&
816                             entry_seqnum >= le64toh(o->entry.seqnum)) {
817                                 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
818                                 r = -EBADMSG;
819                                 goto fail;
820                         }
821
822                         entry_seqnum = le64toh(o->entry.seqnum);
823                         entry_seqnum_set = true;
824
825                         if (entry_monotonic_set &&
826                             sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
827                             entry_monotonic > le64toh(o->entry.monotonic)) {
828                                 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
829                                 r = -EBADMSG;
830                                 goto fail;
831                         }
832
833                         entry_monotonic = le64toh(o->entry.monotonic);
834                         entry_boot_id = o->entry.boot_id;
835                         entry_monotonic_set = true;
836
837                         if (!entry_realtime_set &&
838                             le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
839                                 log_error("Head entry realtime timestamp incorrect");
840                                 r = -EBADMSG;
841                                 goto fail;
842                         }
843
844                         entry_realtime = le64toh(o->entry.realtime);
845                         entry_realtime_set = true;
846
847                         n_entries ++;
848                         break;
849
850                 case OBJECT_DATA_HASH_TABLE:
851                         if (n_data_hash_tables > 1) {
852                                 log_error("More than one data hash table at %llu", (unsigned long long) p);
853                                 r = -EBADMSG;
854                                 goto fail;
855                         }
856
857                         if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
858                             le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
859                                 log_error("Header fields for data hash table invalid");
860                                 r = -EBADMSG;
861                                 goto fail;
862                         }
863
864                         n_data_hash_tables++;
865                         break;
866
867                 case OBJECT_FIELD_HASH_TABLE:
868                         if (n_field_hash_tables > 1) {
869                                 log_error("More than one field hash table at %llu", (unsigned long long) p);
870                                 r = -EBADMSG;
871                                 goto fail;
872                         }
873
874                         if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
875                             le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
876                                 log_error("Header fields for field hash table invalid");
877                                 r = -EBADMSG;
878                                 goto fail;
879                         }
880
881                         n_field_hash_tables++;
882                         break;
883
884                 case OBJECT_ENTRY_ARRAY:
885                         r = write_uint64(entry_array_fd, p);
886                         if (r < 0)
887                                 goto fail;
888
889                         if (p == le64toh(f->header->entry_array_offset)) {
890                                 if (found_main_entry_array) {
891                                         log_error("More than one main entry array at %llu", (unsigned long long) p);
892                                         r = -EBADMSG;
893                                         goto fail;
894                                 }
895
896                                 found_main_entry_array = true;
897                         }
898
899                         n_entry_arrays++;
900                         break;
901
902                 case OBJECT_TAG:
903                         if (!JOURNAL_HEADER_SEALED(f->header)) {
904                                 log_error("Tag object in file without sealing at %llu", (unsigned long long) p);
905                                 r = -EBADMSG;
906                                 goto fail;
907                         }
908
909                         if (le64toh(o->tag.seqnum) != n_tags + 1) {
910                                 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
911                                 r = -EBADMSG;
912                                 goto fail;
913                         }
914
915                         if (le64toh(o->tag.epoch) < last_epoch) {
916                                 log_error("Epoch sequence out of synchronization at %llu", (unsigned long long) p);
917                                 r = -EBADMSG;
918                                 goto fail;
919                         }
920
921 #ifdef HAVE_GCRYPT
922                         if (f->seal) {
923                                 uint64_t q, rt;
924
925                                 log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum));
926
927                                 rt = f->fss_start_usec + o->tag.epoch * f->fss_interval_usec;
928                                 if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) {
929                                         log_error("Tag/entry realtime timestamp out of synchronization at %llu", (unsigned long long) p);
930                                         r = -EBADMSG;
931                                         goto fail;
932                                 }
933
934                                 /* OK, now we know the epoch. So let's now set
935                                  * it, and calculate the HMAC for everything
936                                  * since the last tag. */
937                                 r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
938                                 if (r < 0)
939                                         goto fail;
940
941                                 r = journal_file_hmac_start(f);
942                                 if (r < 0)
943                                         goto fail;
944
945                                 if (last_tag == 0) {
946                                         r = journal_file_hmac_put_header(f);
947                                         if (r < 0)
948                                                 goto fail;
949
950                                         q = le64toh(f->header->header_size);
951                                 } else
952                                         q = last_tag;
953
954                                 while (q <= p) {
955                                         r = journal_file_move_to_object(f, -1, q, &o);
956                                         if (r < 0)
957                                                 goto fail;
958
959                                         r = journal_file_hmac_put_object(f, -1, q);
960                                         if (r < 0)
961                                                 goto fail;
962
963                                         q = q + ALIGN64(le64toh(o->object.size));
964                                 }
965
966                                 /* Position might have changed, let's reposition things */
967                                 r = journal_file_move_to_object(f, -1, p, &o);
968                                 if (r < 0)
969                                         goto fail;
970
971                                 if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
972                                         log_error("Tag failed verification at %llu", (unsigned long long) p);
973                                         r = -EBADMSG;
974                                         goto fail;
975                                 }
976
977                                 f->hmac_running = false;
978                                 last_tag_realtime = rt;
979                                 last_sealed_realtime = entry_realtime;
980                         }
981 #endif
982
983                         last_tag = p + ALIGN64(le64toh(o->object.size));
984                         last_epoch = le64toh(o->tag.epoch);
985
986                         n_tags ++;
987                         break;
988
989                 default:
990                         n_weird ++;
991                 }
992
993                 if (p == le64toh(f->header->tail_object_offset))
994                         p = 0;
995                 else
996                         p = p + ALIGN64(le64toh(o->object.size));
997         }
998
999         if (!found_last) {
1000                 log_error("Tail object pointer dead");
1001                 r = -EBADMSG;
1002                 goto fail;
1003         }
1004
1005         if (n_objects != le64toh(f->header->n_objects)) {
1006                 log_error("Object number mismatch");
1007                 r = -EBADMSG;
1008                 goto fail;
1009         }
1010
1011         if (n_entries != le64toh(f->header->n_entries)) {
1012                 log_error("Entry number mismatch");
1013                 r = -EBADMSG;
1014                 goto fail;
1015         }
1016
1017         if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
1018             n_data != le64toh(f->header->n_data)) {
1019                 log_error("Data number mismatch");
1020                 r = -EBADMSG;
1021                 goto fail;
1022         }
1023
1024         if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
1025             n_fields != le64toh(f->header->n_fields)) {
1026                 log_error("Field number mismatch");
1027                 r = -EBADMSG;
1028                 goto fail;
1029         }
1030
1031         if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
1032             n_tags != le64toh(f->header->n_tags)) {
1033                 log_error("Tag number mismatch");
1034                 r = -EBADMSG;
1035                 goto fail;
1036         }
1037
1038         if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
1039             n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
1040                 log_error("Entry array number mismatch");
1041                 r = -EBADMSG;
1042                 goto fail;
1043         }
1044
1045         if (n_data_hash_tables != 1) {
1046                 log_error("Missing data hash table");
1047                 r = -EBADMSG;
1048                 goto fail;
1049         }
1050
1051         if (n_field_hash_tables != 1) {
1052                 log_error("Missing field hash table");
1053                 r = -EBADMSG;
1054                 goto fail;
1055         }
1056
1057         if (!found_main_entry_array) {
1058                 log_error("Missing entry array");
1059                 r = -EBADMSG;
1060                 goto fail;
1061         }
1062
1063         if (entry_seqnum_set &&
1064             entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
1065                 log_error("Invalid tail seqnum");
1066                 r = -EBADMSG;
1067                 goto fail;
1068         }
1069
1070         if (entry_monotonic_set &&
1071             (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
1072              entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
1073                 log_error("Invalid tail monotonic timestamp");
1074                 r = -EBADMSG;
1075                 goto fail;
1076         }
1077
1078         if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1079                 log_error("Invalid tail realtime timestamp");
1080                 r = -EBADMSG;
1081                 goto fail;
1082         }
1083
1084         /* Second iteration: we follow all objects referenced from the
1085          * two entry points: the object hash table and the entry
1086          * array. We also check that everything referenced (directly
1087          * or indirectly) in the data hash table also exists in the
1088          * entry array, and vice versa. Note that we do not care for
1089          * unreferenced objects. We only care that everything that is
1090          * referenced is consistent. */
1091
1092         r = verify_entry_array(f,
1093                                data_fd, n_data,
1094                                entry_fd, n_entries,
1095                                entry_array_fd, n_entry_arrays,
1096                                &last_usec,
1097                                show_progress);
1098         if (r < 0)
1099                 goto fail;
1100
1101         r = verify_hash_table(f,
1102                               data_fd, n_data,
1103                               entry_fd, n_entries,
1104                               entry_array_fd, n_entry_arrays,
1105                               &last_usec,
1106                               show_progress);
1107         if (r < 0)
1108                 goto fail;
1109
1110         if (show_progress)
1111                 flush_progress();
1112
1113         mmap_cache_close_fd(f->mmap, data_fd);
1114         mmap_cache_close_fd(f->mmap, entry_fd);
1115         mmap_cache_close_fd(f->mmap, entry_array_fd);
1116
1117         close_nointr_nofail(data_fd);
1118         close_nointr_nofail(entry_fd);
1119         close_nointr_nofail(entry_array_fd);
1120
1121         if (first_validated)
1122                 *first_validated = last_tag_realtime ? le64toh(f->header->head_entry_realtime) : 0;
1123         if (last_validated)
1124                 *last_validated = last_sealed_realtime;
1125         if (last_contained)
1126                 *last_contained = le64toh(f->header->tail_entry_realtime);
1127
1128         return 0;
1129
1130 fail:
1131         if (show_progress)
1132                 flush_progress();
1133
1134         log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
1135                   f->path,
1136                   (unsigned long long) p,
1137                   (unsigned long long) f->last_stat.st_size,
1138                   (unsigned long long) (100 * p / f->last_stat.st_size));
1139
1140         if (data_fd >= 0) {
1141                 mmap_cache_close_fd(f->mmap, data_fd);
1142                 close_nointr_nofail(data_fd);
1143         }
1144
1145         if (entry_fd >= 0) {
1146                 mmap_cache_close_fd(f->mmap, entry_fd);
1147                 close_nointr_nofail(entry_fd);
1148         }
1149
1150         if (entry_array_fd >= 0) {
1151                 mmap_cache_close_fd(f->mmap, entry_array_fd);
1152                 close_nointr_nofail(entry_array_fd);
1153         }
1154
1155         return r;
1156 }