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