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