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