chiark / gitweb /
8b71d03c34dad412880b8569995d8862d5ec05f8
[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 * interval;
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, last_tag_realtime = 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 (last_tag_realtime > le64toh(o->entry.realtime)) {
757                                 log_error("Older entry after newer tag at %llu", (unsigned long long) p);
758                                 r = -EBADMSG;
759                                 goto fail;
760                         }
761
762                         if (!entry_seqnum_set &&
763                             le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
764                                 log_error("Head entry sequence number incorrect");
765                                 r = -EBADMSG;
766                                 goto fail;
767                         }
768
769                         if (entry_seqnum_set &&
770                             entry_seqnum >= le64toh(o->entry.seqnum)) {
771                                 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
772                                 r = -EBADMSG;
773                                 goto fail;
774                         }
775
776                         entry_seqnum = le64toh(o->entry.seqnum);
777                         entry_seqnum_set = true;
778
779                         if (entry_monotonic_set &&
780                             sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
781                             entry_monotonic > le64toh(o->entry.monotonic)) {
782                                 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
783                                 r = -EBADMSG;
784                                 goto fail;
785                         }
786
787                         entry_monotonic = le64toh(o->entry.monotonic);
788                         entry_boot_id = o->entry.boot_id;
789                         entry_monotonic_set = true;
790
791                         if (!entry_realtime_set &&
792                             le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
793                                 log_error("Head entry realtime timestamp incorrect");
794                                 r = -EBADMSG;
795                                 goto fail;
796                         }
797
798                         entry_realtime = le64toh(o->entry.realtime);
799                         entry_realtime_set = true;
800
801                         n_entries ++;
802                         break;
803
804                 case OBJECT_DATA_HASH_TABLE:
805                         if (n_data_hash_tables > 1) {
806                                 log_error("More than one data hash table at %llu", (unsigned long long) p);
807                                 r = -EBADMSG;
808                                 goto fail;
809                         }
810
811                         if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
812                             le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
813                                 log_error("Header fields for data hash table invalid.");
814                                 r = -EBADMSG;
815                                 goto fail;
816                         }
817
818                         n_data_hash_tables++;
819                         break;
820
821                 case OBJECT_FIELD_HASH_TABLE:
822                         if (n_field_hash_tables > 1) {
823                                 log_error("More than one field hash table at %llu", (unsigned long long) p);
824                                 r = -EBADMSG;
825                                 goto fail;
826                         }
827
828                         if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
829                             le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
830                                 log_error("Header fields for field hash table invalid.");
831                                 r = -EBADMSG;
832                                 goto fail;
833                         }
834
835                         n_field_hash_tables++;
836                         break;
837
838                 case OBJECT_ENTRY_ARRAY:
839                         r = write_uint64(entry_array_fd, p);
840                         if (r < 0)
841                                 goto fail;
842
843                         if (p == le64toh(f->header->entry_array_offset)) {
844                                 if (found_main_entry_array) {
845                                         log_error("More than one main entry array at %llu", (unsigned long long) p);
846                                         r = -EBADMSG;
847                                         goto fail;
848                                 }
849
850                                 found_main_entry_array = true;
851                         }
852
853                         n_entry_arrays++;
854                         break;
855
856                 case OBJECT_TAG: {
857                         uint64_t q;
858
859                         if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED)) {
860                                 log_error("Tag object without sealing at %llu", (unsigned long long) p);
861                                 r = -EBADMSG;
862                                 goto fail;
863                         }
864
865                         log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum));
866
867                         if (le64toh(o->tag.seqnum) != n_tags + 1) {
868                                 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
869                                 r = -EBADMSG;
870                                 goto fail;
871                         }
872
873                         if (le64toh(o->tag.epoch) < last_epoch) {
874                                 log_error("Epoch sequence out of synchronization at %llu", (unsigned long long) p);
875                                 r = -EBADMSG;
876                                 goto fail;
877                         }
878
879                         last_tag_realtime = (o->tag.epoch + 1) * f->fss_interval_usec + f->fss_start_usec;
880                         if (entry_realtime_set && entry_realtime >= last_tag_realtime) {
881                                 log_error("Tag/entry realtime timestamp out of synchronization at %llu", (unsigned long long) p);
882                                 r = -EBADMSG;
883                                 goto fail;
884                         }
885
886                         /* OK, now we know the epoch. So let's now set
887                          * it, and calculate the HMAC for everything
888                          * since the last tag. */
889                         r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
890                         if (r < 0)
891                                 goto fail;
892
893                         r = journal_file_hmac_start(f);
894                         if (r < 0)
895                                 goto fail;
896
897                         if (last_tag == 0) {
898                                 r = journal_file_hmac_put_header(f);
899                                 if (r < 0)
900                                         goto fail;
901
902                                 q = le64toh(f->header->header_size);
903                         } else
904                                 q = last_tag;
905
906                         while (q <= p) {
907                                 r = journal_file_move_to_object(f, -1, q, &o);
908                                 if (r < 0)
909                                         goto fail;
910
911                                 r = journal_file_hmac_put_object(f, -1, q);
912                                 if (r < 0)
913                                         goto fail;
914
915                                 q = q + ALIGN64(le64toh(o->object.size));
916                         }
917
918                         /* Position might have changed, let's reposition things */
919                         r = journal_file_move_to_object(f, -1, p, &o);
920                         if (r < 0)
921                                 goto fail;
922
923                         if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
924                                 log_error("Tag failed verification at %llu", (unsigned long long) p);
925                                 r = -EBADMSG;
926                                 goto fail;
927                         }
928
929                         f->hmac_running = false;
930
931                         last_tag = p + ALIGN64(le64toh(o->object.size));
932                         n_tags ++;
933                         break;
934                 }
935
936                 default:
937                         n_weird ++;
938                 }
939
940                 if (p == le64toh(f->header->tail_object_offset))
941                         p = 0;
942                 else
943                         p = p + ALIGN64(le64toh(o->object.size));
944         }
945
946         if (n_objects != le64toh(f->header->n_objects)) {
947                 log_error("Object number mismatch");
948                 r = -EBADMSG;
949                 goto fail;
950         }
951
952         if (n_entries != le64toh(f->header->n_entries)) {
953                 log_error("Entry number mismatch");
954                 r = -EBADMSG;
955                 goto fail;
956         }
957
958         if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
959             n_data != le64toh(f->header->n_data)) {
960                 log_error("Data number mismatch");
961                 r = -EBADMSG;
962                 goto fail;
963         }
964
965         if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
966             n_fields != le64toh(f->header->n_fields)) {
967                 log_error("Field number mismatch");
968                 r = -EBADMSG;
969                 goto fail;
970         }
971
972         if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
973             n_tags != le64toh(f->header->n_tags)) {
974                 log_error("Tag number mismatch");
975                 r = -EBADMSG;
976                 goto fail;
977         }
978
979         if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
980             n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
981                 log_error("Entry array number mismatch");
982                 r = -EBADMSG;
983                 goto fail;
984         }
985
986         if (n_data_hash_tables != 1) {
987                 log_error("Missing data hash table");
988                 r = -EBADMSG;
989                 goto fail;
990         }
991
992         if (n_field_hash_tables != 1) {
993                 log_error("Missing field hash table");
994                 r = -EBADMSG;
995                 goto fail;
996         }
997
998         if (!found_main_entry_array) {
999                 log_error("Missing entry array");
1000                 r = -EBADMSG;
1001                 goto fail;
1002         }
1003
1004         if (entry_seqnum_set &&
1005             entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
1006                 log_error("Invalid tail seqnum");
1007                 r = -EBADMSG;
1008                 goto fail;
1009         }
1010
1011         if (entry_monotonic_set &&
1012             (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
1013              entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
1014                 log_error("Invalid tail monotonic timestamp");
1015                 r = -EBADMSG;
1016                 goto fail;
1017         }
1018
1019         if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1020                 log_error("Invalid tail realtime timestamp");
1021                 r = -EBADMSG;
1022                 goto fail;
1023         }
1024
1025         /* Second iteration: we follow all objects referenced from the
1026          * two entry points: the object hash table and the entry
1027          * array. We also check that everything referenced (directly
1028          * or indirectly) in the data hash table also exists in the
1029          * entry array, and vice versa. Note that we do not care for
1030          * unreferenced objects. We only care that everything that is
1031          * referenced is consistent. */
1032
1033         r = verify_entry_array(f,
1034                                data_fd, n_data,
1035                                entry_fd, n_entries,
1036                                entry_array_fd, n_entry_arrays,
1037                                &last_usec);
1038         if (r < 0)
1039                 goto fail;
1040
1041         r = verify_hash_table(f,
1042                               data_fd, n_data,
1043                               entry_fd, n_entries,
1044                               entry_array_fd, n_entry_arrays,
1045                               &last_usec);
1046         if (r < 0)
1047                 goto fail;
1048
1049         flush_progress();
1050
1051         mmap_cache_close_fd(f->mmap, data_fd);
1052         mmap_cache_close_fd(f->mmap, entry_fd);
1053         mmap_cache_close_fd(f->mmap, entry_array_fd);
1054
1055         close_nointr_nofail(data_fd);
1056         close_nointr_nofail(entry_fd);
1057         close_nointr_nofail(entry_array_fd);
1058
1059         return 0;
1060
1061 fail:
1062         flush_progress();
1063
1064         log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
1065                   f->path,
1066                   (unsigned long long) p,
1067                   (unsigned long long) f->last_stat.st_size,
1068                   (unsigned long long) (100 * p / f->last_stat.st_size));
1069
1070         if (data_fd >= 0) {
1071                 mmap_cache_close_fd(f->mmap, data_fd);
1072                 close_nointr_nofail(data_fd);
1073         }
1074
1075         if (entry_fd >= 0) {
1076                 mmap_cache_close_fd(f->mmap, entry_fd);
1077                 close_nointr_nofail(entry_fd);
1078         }
1079
1080         if (entry_array_fd >= 0) {
1081                 mmap_cache_close_fd(f->mmap, entry_array_fd);
1082                 close_nointr_nofail(entry_array_fd);
1083         }
1084
1085         return r;
1086 }