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