chiark / gitweb /
journal: rework terminology
[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  * - change terms
40  * - write bit mucking test
41  * - tag timestamps should be between entry timestamps
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                         if (le64toh(o->tag.seqnum) != n_tags + 1) {
860                                 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
861                                 r = -EBADMSG;
862                                 goto fail;
863                         }
864
865                         if (le64toh(o->tag.epoch) < last_epoch) {
866                                 log_error("Epoch sequence out of synchronization at %llu", (unsigned long long) p);
867                                 r = -EBADMSG;
868                                 goto fail;
869                         }
870
871                         /* OK, now we know the epoch. So let's now set
872                          * it, and calculate the HMAC for everything
873                          * since the last tag. */
874                         r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
875                         if (r < 0)
876                                 goto fail;
877
878                         r = journal_file_hmac_start(f);
879                         if (r < 0)
880                                 goto fail;
881
882                         if (last_tag == 0) {
883                                 r = journal_file_hmac_put_header(f);
884                                 if (r < 0)
885                                         goto fail;
886
887                                 q = le64toh(f->header->header_size);
888                         } else
889                                 q = last_tag;
890
891                         while (q <= p) {
892                                 r = journal_file_move_to_object(f, -1, q, &o);
893                                 if (r < 0)
894                                         goto fail;
895
896                                 r = journal_file_hmac_put_object(f, -1, q);
897                                 if (r < 0)
898                                         goto fail;
899
900                                 q = q + ALIGN64(le64toh(o->object.size));
901                         }
902
903                         /* Position might have changed, let's reposition things */
904                         r = journal_file_move_to_object(f, -1, p, &o);
905                         if (r < 0)
906                                 goto fail;
907
908                         if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
909                                 log_error("Tag failed verification at %llu", (unsigned long long) p);
910                                 r = -EBADMSG;
911                                 goto fail;
912                         }
913
914                         f->hmac_running = false;
915
916                         last_tag = p + ALIGN64(le64toh(o->object.size));
917                         n_tags ++;
918                         break;
919                 }
920
921                 default:
922                         n_weird ++;
923                 }
924
925                 if (p == le64toh(f->header->tail_object_offset))
926                         p = 0;
927                 else
928                         p = p + ALIGN64(le64toh(o->object.size));
929         }
930
931         if (n_objects != le64toh(f->header->n_objects)) {
932                 log_error("Object number mismatch");
933                 r = -EBADMSG;
934                 goto fail;
935         }
936
937         if (n_entries != le64toh(f->header->n_entries)) {
938                 log_error("Entry number mismatch");
939                 r = -EBADMSG;
940                 goto fail;
941         }
942
943         if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
944             n_data != le64toh(f->header->n_data)) {
945                 log_error("Data number mismatch");
946                 r = -EBADMSG;
947                 goto fail;
948         }
949
950         if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
951             n_fields != le64toh(f->header->n_fields)) {
952                 log_error("Field number mismatch");
953                 r = -EBADMSG;
954                 goto fail;
955         }
956
957         if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
958             n_tags != le64toh(f->header->n_tags)) {
959                 log_error("Tag number mismatch");
960                 r = -EBADMSG;
961                 goto fail;
962         }
963
964         if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
965             n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
966                 log_error("Entry array number mismatch");
967                 r = -EBADMSG;
968                 goto fail;
969         }
970
971         if (n_data_hash_tables != 1) {
972                 log_error("Missing data hash table");
973                 r = -EBADMSG;
974                 goto fail;
975         }
976
977         if (n_field_hash_tables != 1) {
978                 log_error("Missing field hash table");
979                 r = -EBADMSG;
980                 goto fail;
981         }
982
983         if (!found_main_entry_array) {
984                 log_error("Missing entry array");
985                 r = -EBADMSG;
986                 goto fail;
987         }
988
989         if (entry_seqnum_set &&
990             entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
991                 log_error("Invalid tail seqnum");
992                 r = -EBADMSG;
993                 goto fail;
994         }
995
996         if (entry_monotonic_set &&
997             (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
998              entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
999                 log_error("Invalid tail monotonic timestamp");
1000                 r = -EBADMSG;
1001                 goto fail;
1002         }
1003
1004         if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1005                 log_error("Invalid tail realtime timestamp");
1006                 r = -EBADMSG;
1007                 goto fail;
1008         }
1009
1010         /* Second iteration: we follow all objects referenced from the
1011          * two entry points: the object hash table and the entry
1012          * array. We also check that everything referenced (directly
1013          * or indirectly) in the data hash table also exists in the
1014          * entry array, and vice versa. Note that we do not care for
1015          * unreferenced objects. We only care that everything that is
1016          * referenced is consistent. */
1017
1018         r = verify_entry_array(f,
1019                                data_fd, n_data,
1020                                entry_fd, n_entries,
1021                                entry_array_fd, n_entry_arrays,
1022                                &last_usec);
1023         if (r < 0)
1024                 goto fail;
1025
1026         r = verify_hash_table(f,
1027                               data_fd, n_data,
1028                               entry_fd, n_entries,
1029                               entry_array_fd, n_entry_arrays,
1030                               &last_usec);
1031         if (r < 0)
1032                 goto fail;
1033
1034         flush_progress();
1035
1036         mmap_cache_close_fd(f->mmap, data_fd);
1037         mmap_cache_close_fd(f->mmap, entry_fd);
1038         mmap_cache_close_fd(f->mmap, entry_array_fd);
1039
1040         close_nointr_nofail(data_fd);
1041         close_nointr_nofail(entry_fd);
1042         close_nointr_nofail(entry_array_fd);
1043
1044         return 0;
1045
1046 fail:
1047         flush_progress();
1048
1049         log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
1050                   f->path,
1051                   (unsigned long long) p,
1052                   (unsigned long long) f->last_stat.st_size,
1053                   (unsigned long long) (100 * p / f->last_stat.st_size));
1054
1055         if (data_fd >= 0) {
1056                 mmap_cache_close_fd(f->mmap, data_fd);
1057                 close_nointr_nofail(data_fd);
1058         }
1059
1060         if (entry_fd >= 0) {
1061                 mmap_cache_close_fd(f->mmap, entry_fd);
1062                 close_nointr_nofail(entry_fd);
1063         }
1064
1065         if (entry_array_fd >= 0) {
1066                 mmap_cache_close_fd(f->mmap, entry_array_fd);
1067                 close_nointr_nofail(entry_array_fd);
1068         }
1069
1070         return r;
1071 }