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