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