chiark / gitweb /
util: simplify column caching logic
[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_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0;
674
675         uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
676         sd_id128_t entry_boot_id;
677         bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
678         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;
679         usec_t last_usec = 0;
680         int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
681         char data_path[] = "/var/tmp/journal-data-XXXXXX",
682                 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
683                 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
684         unsigned i;
685         bool found_last;
686 #ifdef HAVE_GCRYPT
687         uint64_t last_tag = 0;
688 #endif
689         assert(f);
690
691         if (key) {
692 #ifdef HAVE_GCRYPT
693                 r = journal_file_parse_verification_key(f, key);
694                 if (r < 0) {
695                         log_error("Failed to parse seed.");
696                         return r;
697                 }
698 #else
699                 return -ENOTSUP;
700 #endif
701         } else if (f->seal)
702                 return -ENOKEY;
703
704         data_fd = mkostemp(data_path, O_CLOEXEC);
705         if (data_fd < 0) {
706                 log_error("Failed to create data file: %m");
707                 r = -errno;
708                 goto fail;
709         }
710         unlink(data_path);
711
712         entry_fd = mkostemp(entry_path, O_CLOEXEC);
713         if (entry_fd < 0) {
714                 log_error("Failed to create entry file: %m");
715                 r = -errno;
716                 goto fail;
717         }
718         unlink(entry_path);
719
720         entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
721         if (entry_array_fd < 0) {
722                 log_error("Failed to create entry array file: %m");
723                 r = -errno;
724                 goto fail;
725         }
726         unlink(entry_array_path);
727
728 #ifdef HAVE_GCRYPT
729         if ((le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) != 0)
730 #else
731         if (f->header->compatible_flags != 0)
732 #endif
733         {
734                 log_error("Cannot verify file with unknown extensions.");
735                 r = -ENOTSUP;
736                 goto fail;
737         }
738
739         for (i = 0; i < sizeof(f->header->reserved); i++)
740                 if (f->header->reserved[i] != 0) {
741                         log_error("Reserved field in non-zero.");
742                         r = -EBADMSG;
743                         goto fail;
744                 }
745
746         /* First iteration: we go through all objects, verify the
747          * superficial structure, headers, hashes. */
748
749         p = le64toh(f->header->header_size);
750         while (p != 0) {
751                 if (show_progress)
752                         draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
753
754                 r = journal_file_move_to_object(f, -1, p, &o);
755                 if (r < 0) {
756                         log_error("Invalid object at %llu", (unsigned long long) p);
757                         goto fail;
758                 }
759
760                 if (p > le64toh(f->header->tail_object_offset)) {
761                         log_error("Invalid tail object pointer");
762                         r = -EBADMSG;
763                         goto fail;
764                 }
765
766                 if (p == le64toh(f->header->tail_object_offset))
767                         found_last = true;
768
769                 n_objects ++;
770
771                 r = journal_file_object_verify(f, o);
772                 if (r < 0) {
773                         log_error("Invalid object contents at %llu", (unsigned long long) p);
774                         goto fail;
775                 }
776
777                 if ((o->object.flags & OBJECT_COMPRESSED) && !JOURNAL_HEADER_COMPRESSED(f->header)) {
778                         log_error("Compressed object in file without compression at %llu", (unsigned long long) p);
779                         r = -EBADMSG;
780                         goto fail;
781                 }
782
783                 switch (o->object.type) {
784
785                 case OBJECT_DATA:
786                         r = write_uint64(data_fd, p);
787                         if (r < 0)
788                                 goto fail;
789
790                         n_data++;
791                         break;
792
793                 case OBJECT_FIELD:
794                         n_fields++;
795                         break;
796
797                 case OBJECT_ENTRY:
798                         if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) {
799                                 log_error("First entry before first tag at %llu", (unsigned long long) p);
800                                 r = -EBADMSG;
801                                 goto fail;
802                         }
803
804                         r = write_uint64(entry_fd, p);
805                         if (r < 0)
806                                 goto fail;
807
808                         if (le64toh(o->entry.realtime) < last_tag_realtime) {
809                                 log_error("Older entry after newer tag at %llu", (unsigned long long) p);
810                                 r = -EBADMSG;
811                                 goto fail;
812                         }
813
814                         if (!entry_seqnum_set &&
815                             le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
816                                 log_error("Head entry sequence number incorrect at %llu", (unsigned long long) p);
817                                 r = -EBADMSG;
818                                 goto fail;
819                         }
820
821                         if (entry_seqnum_set &&
822                             entry_seqnum >= le64toh(o->entry.seqnum)) {
823                                 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
824                                 r = -EBADMSG;
825                                 goto fail;
826                         }
827
828                         entry_seqnum = le64toh(o->entry.seqnum);
829                         entry_seqnum_set = true;
830
831                         if (entry_monotonic_set &&
832                             sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
833                             entry_monotonic > le64toh(o->entry.monotonic)) {
834                                 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
835                                 r = -EBADMSG;
836                                 goto fail;
837                         }
838
839                         entry_monotonic = le64toh(o->entry.monotonic);
840                         entry_boot_id = o->entry.boot_id;
841                         entry_monotonic_set = true;
842
843                         if (!entry_realtime_set &&
844                             le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
845                                 log_error("Head entry realtime timestamp incorrect");
846                                 r = -EBADMSG;
847                                 goto fail;
848                         }
849
850                         entry_realtime = le64toh(o->entry.realtime);
851                         entry_realtime_set = true;
852
853                         n_entries ++;
854                         break;
855
856                 case OBJECT_DATA_HASH_TABLE:
857                         if (n_data_hash_tables > 1) {
858                                 log_error("More than one data hash table at %llu", (unsigned long long) p);
859                                 r = -EBADMSG;
860                                 goto fail;
861                         }
862
863                         if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
864                             le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
865                                 log_error("Header fields for data hash table invalid");
866                                 r = -EBADMSG;
867                                 goto fail;
868                         }
869
870                         n_data_hash_tables++;
871                         break;
872
873                 case OBJECT_FIELD_HASH_TABLE:
874                         if (n_field_hash_tables > 1) {
875                                 log_error("More than one field hash table at %llu", (unsigned long long) p);
876                                 r = -EBADMSG;
877                                 goto fail;
878                         }
879
880                         if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
881                             le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
882                                 log_error("Header fields for field hash table invalid");
883                                 r = -EBADMSG;
884                                 goto fail;
885                         }
886
887                         n_field_hash_tables++;
888                         break;
889
890                 case OBJECT_ENTRY_ARRAY:
891                         r = write_uint64(entry_array_fd, p);
892                         if (r < 0)
893                                 goto fail;
894
895                         if (p == le64toh(f->header->entry_array_offset)) {
896                                 if (found_main_entry_array) {
897                                         log_error("More than one main entry array at %llu", (unsigned long long) p);
898                                         r = -EBADMSG;
899                                         goto fail;
900                                 }
901
902                                 found_main_entry_array = true;
903                         }
904
905                         n_entry_arrays++;
906                         break;
907
908                 case OBJECT_TAG:
909                         if (!JOURNAL_HEADER_SEALED(f->header)) {
910                                 log_error("Tag object in file without sealing at %llu", (unsigned long long) p);
911                                 r = -EBADMSG;
912                                 goto fail;
913                         }
914
915                         if (le64toh(o->tag.seqnum) != n_tags + 1) {
916                                 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
917                                 r = -EBADMSG;
918                                 goto fail;
919                         }
920
921                         if (le64toh(o->tag.epoch) < last_epoch) {
922                                 log_error("Epoch sequence out of synchronization at %llu", (unsigned long long) p);
923                                 r = -EBADMSG;
924                                 goto fail;
925                         }
926
927 #ifdef HAVE_GCRYPT
928                         if (f->seal) {
929                                 uint64_t q, rt;
930
931                                 log_debug("Checking tag %llu..", (unsigned long long) le64toh(o->tag.seqnum));
932
933                                 rt = f->fss_start_usec + o->tag.epoch * f->fss_interval_usec;
934                                 if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) {
935                                         log_error("Tag/entry realtime timestamp out of synchronization at %llu", (unsigned long long) p);
936                                         r = -EBADMSG;
937                                         goto fail;
938                                 }
939
940                                 /* OK, now we know the epoch. So let's now set
941                                  * it, and calculate the HMAC for everything
942                                  * since the last tag. */
943                                 r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
944                                 if (r < 0)
945                                         goto fail;
946
947                                 r = journal_file_hmac_start(f);
948                                 if (r < 0)
949                                         goto fail;
950
951                                 if (last_tag == 0) {
952                                         r = journal_file_hmac_put_header(f);
953                                         if (r < 0)
954                                                 goto fail;
955
956                                         q = le64toh(f->header->header_size);
957                                 } else
958                                         q = last_tag;
959
960                                 while (q <= p) {
961                                         r = journal_file_move_to_object(f, -1, q, &o);
962                                         if (r < 0)
963                                                 goto fail;
964
965                                         r = journal_file_hmac_put_object(f, -1, o, q);
966                                         if (r < 0)
967                                                 goto fail;
968
969                                         q = q + ALIGN64(le64toh(o->object.size));
970                                 }
971
972                                 /* Position might have changed, let's reposition things */
973                                 r = journal_file_move_to_object(f, -1, p, &o);
974                                 if (r < 0)
975                                         goto fail;
976
977                                 if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
978                                         log_error("Tag failed verification at %llu", (unsigned long long) p);
979                                         r = -EBADMSG;
980                                         goto fail;
981                                 }
982
983                                 f->hmac_running = false;
984                                 last_tag_realtime = rt;
985                                 last_sealed_realtime = entry_realtime;
986                         }
987
988                         last_tag = p + ALIGN64(le64toh(o->object.size));
989 #endif
990
991                         last_epoch = le64toh(o->tag.epoch);
992
993                         n_tags ++;
994                         break;
995
996                 default:
997                         n_weird ++;
998                 }
999
1000                 if (p == le64toh(f->header->tail_object_offset))
1001                         p = 0;
1002                 else
1003                         p = p + ALIGN64(le64toh(o->object.size));
1004         }
1005
1006         if (!found_last) {
1007                 log_error("Tail object pointer dead");
1008                 r = -EBADMSG;
1009                 goto fail;
1010         }
1011
1012         if (n_objects != le64toh(f->header->n_objects)) {
1013                 log_error("Object number mismatch");
1014                 r = -EBADMSG;
1015                 goto fail;
1016         }
1017
1018         if (n_entries != le64toh(f->header->n_entries)) {
1019                 log_error("Entry number mismatch");
1020                 r = -EBADMSG;
1021                 goto fail;
1022         }
1023
1024         if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
1025             n_data != le64toh(f->header->n_data)) {
1026                 log_error("Data number mismatch");
1027                 r = -EBADMSG;
1028                 goto fail;
1029         }
1030
1031         if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
1032             n_fields != le64toh(f->header->n_fields)) {
1033                 log_error("Field number mismatch");
1034                 r = -EBADMSG;
1035                 goto fail;
1036         }
1037
1038         if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
1039             n_tags != le64toh(f->header->n_tags)) {
1040                 log_error("Tag number mismatch");
1041                 r = -EBADMSG;
1042                 goto fail;
1043         }
1044
1045         if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
1046             n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
1047                 log_error("Entry array number mismatch");
1048                 r = -EBADMSG;
1049                 goto fail;
1050         }
1051
1052         if (n_data_hash_tables != 1) {
1053                 log_error("Missing data hash table");
1054                 r = -EBADMSG;
1055                 goto fail;
1056         }
1057
1058         if (n_field_hash_tables != 1) {
1059                 log_error("Missing field hash table");
1060                 r = -EBADMSG;
1061                 goto fail;
1062         }
1063
1064         if (!found_main_entry_array) {
1065                 log_error("Missing entry array");
1066                 r = -EBADMSG;
1067                 goto fail;
1068         }
1069
1070         if (entry_seqnum_set &&
1071             entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
1072                 log_error("Invalid tail seqnum");
1073                 r = -EBADMSG;
1074                 goto fail;
1075         }
1076
1077         if (entry_monotonic_set &&
1078             (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
1079              entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
1080                 log_error("Invalid tail monotonic timestamp");
1081                 r = -EBADMSG;
1082                 goto fail;
1083         }
1084
1085         if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
1086                 log_error("Invalid tail realtime timestamp");
1087                 r = -EBADMSG;
1088                 goto fail;
1089         }
1090
1091         /* Second iteration: we follow all objects referenced from the
1092          * two entry points: the object hash table and the entry
1093          * array. We also check that everything referenced (directly
1094          * or indirectly) in the data hash table also exists in the
1095          * entry array, and vice versa. Note that we do not care for
1096          * unreferenced objects. We only care that everything that is
1097          * referenced is consistent. */
1098
1099         r = verify_entry_array(f,
1100                                data_fd, n_data,
1101                                entry_fd, n_entries,
1102                                entry_array_fd, n_entry_arrays,
1103                                &last_usec,
1104                                show_progress);
1105         if (r < 0)
1106                 goto fail;
1107
1108         r = verify_hash_table(f,
1109                               data_fd, n_data,
1110                               entry_fd, n_entries,
1111                               entry_array_fd, n_entry_arrays,
1112                               &last_usec,
1113                               show_progress);
1114         if (r < 0)
1115                 goto fail;
1116
1117         if (show_progress)
1118                 flush_progress();
1119
1120         mmap_cache_close_fd(f->mmap, data_fd);
1121         mmap_cache_close_fd(f->mmap, entry_fd);
1122         mmap_cache_close_fd(f->mmap, entry_array_fd);
1123
1124         close_nointr_nofail(data_fd);
1125         close_nointr_nofail(entry_fd);
1126         close_nointr_nofail(entry_array_fd);
1127
1128         if (first_contained)
1129                 *first_contained = le64toh(f->header->head_entry_realtime);
1130         if (last_validated)
1131                 *last_validated = last_sealed_realtime;
1132         if (last_contained)
1133                 *last_contained = le64toh(f->header->tail_entry_realtime);
1134
1135         return 0;
1136
1137 fail:
1138         if (show_progress)
1139                 flush_progress();
1140
1141         log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
1142                   f->path,
1143                   (unsigned long long) p,
1144                   (unsigned long long) f->last_stat.st_size,
1145                   (unsigned long long) (100 * p / f->last_stat.st_size));
1146
1147         if (data_fd >= 0) {
1148                 mmap_cache_close_fd(f->mmap, data_fd);
1149                 close_nointr_nofail(data_fd);
1150         }
1151
1152         if (entry_fd >= 0) {
1153                 mmap_cache_close_fd(f->mmap, entry_fd);
1154                 close_nointr_nofail(entry_fd);
1155         }
1156
1157         if (entry_array_fd >= 0) {
1158                 mmap_cache_close_fd(f->mmap, entry_array_fd);
1159                 close_nointr_nofail(entry_array_fd);
1160         }
1161
1162         return r;
1163 }