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