chiark / gitweb /
journal: journal-send.h doesn't actually exist
[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         while (i < n) {
319                 uint64_t next, m, j;
320
321                 if (a == 0) {
322                         log_error("Array chain too short at %llu.", (unsigned long long) p);
323                         return -EBADMSG;
324                 }
325
326                 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
327                         log_error("Invalid array at %llu.", (unsigned long long) p);
328                         return -EBADMSG;
329                 }
330
331                 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
332                 if (r < 0)
333                         return r;
334
335                 next = le64toh(o->entry_array.next_entry_array_offset);
336                 if (next != 0 && next <= a) {
337                         log_error("Array chain has cycle at %llu.", (unsigned long long) p);
338                         return -EBADMSG;
339                 }
340
341                 m = journal_file_entry_array_n_items(o);
342                 for (j = 0; i < n && j < m; i++, j++) {
343
344                         q = le64toh(o->entry_array.items[j]);
345                         if (q <= last) {
346                                 log_error("Data object's entry array not sorted at %llu.", (unsigned long long) p);
347                                 return -EBADMSG;
348                         }
349                         last = q;
350
351                         r = entry_points_to_data(f, entry_fd, n_entries, q, p);
352                         if (r < 0)
353                                 return r;
354
355                         /* Pointer might have moved, reposition */
356                         r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
357                         if (r < 0)
358                                 return r;
359                 }
360
361                 a = next;
362         }
363
364         return 0;
365 }
366
367 static int verify_hash_table(
368                 JournalFile *f,
369                 int data_fd, uint64_t n_data,
370                 int entry_fd, uint64_t n_entries,
371                 int entry_array_fd, uint64_t n_entry_arrays,
372                 usec_t *last_usec) {
373
374         uint64_t i, n;
375         int r;
376
377         assert(f);
378         assert(data_fd >= 0);
379         assert(entry_fd >= 0);
380         assert(entry_array_fd >= 0);
381         assert(last_usec);
382
383         n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
384         for (i = 0; i < n; i++) {
385                 uint64_t last = 0, p;
386
387                 draw_progress(0xC000 + (0x3FFF * i / n), last_usec);
388
389                 p = le64toh(f->data_hash_table[i].head_hash_offset);
390                 while (p != 0) {
391                         Object *o;
392                         uint64_t next;
393
394                         if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
395                                 log_error("Invalid data object at hash entry %llu of %llu.",
396                                           (unsigned long long) i, (unsigned long long) n);
397                                 return -EBADMSG;
398                         }
399
400                         r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
401                         if (r < 0)
402                                 return r;
403
404                         next = le64toh(o->data.next_hash_offset);
405                         if (next != 0 && next <= p) {
406                                 log_error("Hash chain has a cycle in hash entry %llu of %llu.",
407                                           (unsigned long long) i, (unsigned long long) n);
408                                 return -EBADMSG;
409                         }
410
411                         if (le64toh(o->data.hash) % n != i) {
412                                 log_error("Hash value mismatch in hash entry %llu of %llu.",
413                                           (unsigned long long) i, (unsigned long long) n);
414                                 return -EBADMSG;
415                         }
416
417                         r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
418                         if (r < 0)
419                                 return r;
420
421                         last = p;
422                         p = next;
423                 }
424
425                 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
426                         log_error("Tail hash pointer mismatch in hash table.");
427                         return -EBADMSG;
428                 }
429         }
430
431         return 0;
432 }
433
434 static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
435         uint64_t n, h, q;
436         int r;
437         assert(f);
438
439         n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
440         h = hash % n;
441
442         q = le64toh(f->data_hash_table[h].head_hash_offset);
443         while (q != 0) {
444                 Object *o;
445
446                 if (p == q)
447                         return 1;
448
449                 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
450                 if (r < 0)
451                         return r;
452
453                 q = le64toh(o->data.next_hash_offset);
454         }
455
456         return 0;
457 }
458
459 static int verify_entry(
460                 JournalFile *f,
461                 Object *o, uint64_t p,
462                 int data_fd, uint64_t n_data) {
463
464         uint64_t i, n;
465         int r;
466
467         assert(f);
468         assert(o);
469         assert(data_fd >= 0);
470
471         n = journal_file_entry_n_items(o);
472         for (i = 0; i < n; i++) {
473                 uint64_t q, h;
474                 Object *u;
475
476                 q = le64toh(o->entry.items[i].object_offset);
477                 h = le64toh(o->entry.items[i].hash);
478
479                 if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
480                         log_error("Invalid data object at entry %llu.",
481                                   (unsigned long long) o);
482                                 return -EBADMSG;
483                         }
484
485                 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
486                 if (r < 0)
487                         return r;
488
489                 if (le64toh(u->data.hash) != h) {
490                         log_error("Hash mismatch for data object at entry %llu.",
491                                   (unsigned long long) p);
492                         return -EBADMSG;
493                 }
494
495                 r = data_object_in_hash_table(f, h, q);
496                 if (r < 0)
497                         return r;
498                 if (r == 0) {
499                         log_error("Data object missing from hash at entry %llu.",
500                                   (unsigned long long) p);
501                         return -EBADMSG;
502                 }
503         }
504
505         return 0;
506 }
507
508 static int verify_entry_array(
509                 JournalFile *f,
510                 int data_fd, uint64_t n_data,
511                 int entry_fd, uint64_t n_entries,
512                 int entry_array_fd, uint64_t n_entry_arrays,
513                 usec_t *last_usec) {
514
515         uint64_t i = 0, a, n, last = 0;
516         int r;
517
518         assert(f);
519         assert(data_fd >= 0);
520         assert(entry_fd >= 0);
521         assert(entry_array_fd >= 0);
522         assert(last_usec);
523
524         n = le64toh(f->header->n_entries);
525         a = le64toh(f->header->entry_array_offset);
526         while (i < n) {
527                 uint64_t next, m, j;
528                 Object *o;
529
530                 draw_progress(0x8000 + (0x3FFF * i / n), last_usec);
531
532                 if (a == 0) {
533                         log_error("Array chain too short at %llu of %llu.",
534                                   (unsigned long long) i, (unsigned long long) n);
535                         return -EBADMSG;
536                 }
537
538                 if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
539                         log_error("Invalid array at %llu of %llu.",
540                                   (unsigned long long) i, (unsigned long long) n);
541                         return -EBADMSG;
542                 }
543
544                 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
545                 if (r < 0)
546                         return r;
547
548                 next = le64toh(o->entry_array.next_entry_array_offset);
549                 if (next != 0 && next <= a) {
550                         log_error("Array chain has cycle at %llu of %llu.",
551                                   (unsigned long long) i, (unsigned long long) n);
552                         return -EBADMSG;
553                 }
554
555                 m = journal_file_entry_array_n_items(o);
556                 for (j = 0; i < n && j < m; i++, j++) {
557                         uint64_t p;
558
559                         p = le64toh(o->entry_array.items[j]);
560                         if (p <= last) {
561                                 log_error("Entry array not sorted at %llu of %llu.",
562                                           (unsigned long long) i, (unsigned long long) n);
563                                 return -EBADMSG;
564                         }
565                         last = p;
566
567                         if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
568                                 log_error("Invalid array entry at %llu of %llu.",
569                                           (unsigned long long) i, (unsigned long long) n);
570                                 return -EBADMSG;
571                         }
572
573                         r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
574                         if (r < 0)
575                                 return r;
576
577                         r = verify_entry(f, o, p, data_fd, n_data);
578                         if (r < 0)
579                                 return r;
580
581                         /* Pointer might have moved, reposition */
582                         r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
583                         if (r < 0)
584                                 return r;
585                 }
586
587                 a = next;
588         }
589
590         return 0;
591 }
592
593 int journal_file_verify(JournalFile *f, const char *key) {
594         int r;
595         Object *o;
596         uint64_t p = 0;
597         uint64_t tag_seqnum = 0, entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
598         sd_id128_t entry_boot_id;
599         bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
600         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;
601         usec_t last_usec = 0;
602         int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
603         char data_path[] = "/var/tmp/journal-data-XXXXXX",
604                 entry_path[] = "/var/tmp/journal-entry-XXXXXX",
605                 entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX";
606
607         assert(f);
608
609         data_fd = mkostemp(data_path, O_CLOEXEC);
610         if (data_fd < 0) {
611                 log_error("Failed to create data file: %m");
612                 goto fail;
613         }
614         unlink(data_path);
615
616         entry_fd = mkostemp(entry_path, O_CLOEXEC);
617         if (entry_fd < 0) {
618                 log_error("Failed to create entry file: %m");
619                 goto fail;
620         }
621         unlink(entry_path);
622
623         entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
624         if (entry_array_fd < 0) {
625                 log_error("Failed to create entry array file: %m");
626                 goto fail;
627         }
628         unlink(entry_array_path);
629
630         /* First iteration: we go through all objects, verify the
631          * superficial structure, headers, hashes. */
632
633         r = journal_file_hmac_put_header(f);
634         if (r < 0) {
635                 log_error("Failed to calculate HMAC of header.");
636                 goto fail;
637         }
638
639         p = le64toh(f->header->header_size);
640         while (p != 0) {
641                 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
642
643                 r = journal_file_move_to_object(f, -1, p, &o);
644                 if (r < 0) {
645                         log_error("Invalid object at %llu", (unsigned long long) p);
646                         goto fail;
647                 }
648
649                 if (le64toh(f->header->tail_object_offset) < p) {
650                         log_error("Invalid tail object pointer.");
651                         r = -EBADMSG;
652                         goto fail;
653                 }
654
655                 n_objects ++;
656
657                 r = journal_file_object_verify(f, o);
658                 if (r < 0) {
659                         log_error("Invalid object contents at %llu", (unsigned long long) p);
660                         goto fail;
661                 }
662
663                 if (o->object.flags & OBJECT_COMPRESSED &&
664                     !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
665                         log_error("Compressed object without compression at %llu", (unsigned long long) p);
666                         r = -EBADMSG;
667                         goto fail;
668                 }
669
670                 r = journal_file_hmac_put_object(f, -1, p);
671                 if (r < 0) {
672                         log_error("Failed to calculate HMAC at %llu", (unsigned long long) p);
673                         goto fail;
674                 }
675
676                 if (o->object.type == OBJECT_TAG) {
677
678                         if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) {
679                                 log_error("Tag object without authentication at %llu", (unsigned long long) p);
680                                 r = -EBADMSG;
681                                 goto fail;
682                         }
683
684                         if (le64toh(o->tag.seqnum) != tag_seqnum) {
685                                 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
686                                 r = -EBADMSG;
687                                 goto fail;
688                         }
689
690                 } else if (o->object.type == OBJECT_ENTRY) {
691
692                         r = write_uint64(entry_fd, p);
693                         if (r < 0)
694                                 goto fail;
695
696                         if (!entry_seqnum_set &&
697                             le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
698                                 log_error("Head entry sequence number incorrect");
699                                 r = -EBADMSG;
700                                 goto fail;
701                         }
702
703                         if (entry_seqnum_set &&
704                             entry_seqnum >= le64toh(o->entry.seqnum)) {
705                                 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
706                                 r = -EBADMSG;
707                                 goto fail;
708                         }
709
710                         entry_seqnum = le64toh(o->entry.seqnum);
711                         entry_seqnum_set = true;
712
713                         if (entry_monotonic_set &&
714                             sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
715                             entry_monotonic > le64toh(o->entry.monotonic)) {
716                                 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
717                                 r = -EBADMSG;
718                                 goto fail;
719                         }
720
721                         entry_monotonic = le64toh(o->entry.monotonic);
722                         entry_boot_id = o->entry.boot_id;
723                         entry_monotonic_set = true;
724
725                         if (!entry_realtime_set &&
726                             le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
727                                 log_error("Head entry realtime timestamp incorrect");
728                                 r = -EBADMSG;
729                                 goto fail;
730                         }
731
732                         entry_realtime = le64toh(o->entry.realtime);
733                         entry_realtime_set = true;
734
735                         n_entries ++;
736                 } else if (o->object.type == OBJECT_ENTRY_ARRAY) {
737
738                         r = write_uint64(entry_array_fd, p);
739                         if (r < 0)
740                                 goto fail;
741
742                         if (p == le64toh(f->header->entry_array_offset)) {
743                                 if (found_main_entry_array) {
744                                         log_error("More than one main entry array at %llu", (unsigned long long) p);
745                                         r = -EBADMSG;
746                                         goto fail;
747                                 }
748
749                                 found_main_entry_array = true;
750                         }
751
752                         n_entry_arrays++;
753
754                 } else if (o->object.type == OBJECT_DATA) {
755
756                         r = write_uint64(data_fd, p);
757                         if (r < 0)
758                                 goto fail;
759
760                         n_data++;
761
762                 } else if (o->object.type == OBJECT_FIELD)
763                         n_fields++;
764                 else if (o->object.type == OBJECT_DATA_HASH_TABLE) {
765                         n_data_hash_tables++;
766
767                         if (n_data_hash_tables > 1) {
768                                 log_error("More than one data hash table at %llu", (unsigned long long) p);
769                                 r = -EBADMSG;
770                                 goto fail;
771                         }
772
773                         if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
774                             le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
775                                 log_error("Header fields for data hash table invalid.");
776                                 r = -EBADMSG;
777                                 goto fail;
778                         }
779                 } else if (o->object.type == OBJECT_FIELD_HASH_TABLE) {
780                         n_field_hash_tables++;
781
782                         if (n_field_hash_tables > 1) {
783                                 log_error("More than one field hash table at %llu", (unsigned long long) p);
784                                 r = -EBADMSG;
785                                 goto fail;
786                         }
787
788                         if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
789                             le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
790                                 log_error("Header fields for field hash table invalid.");
791                                 r = -EBADMSG;
792                                 goto fail;
793                         }
794                 } else if (o->object.type >= _OBJECT_TYPE_MAX)
795                         n_weird ++;
796
797                 if (p == le64toh(f->header->tail_object_offset))
798                         p = 0;
799                 else
800                         p = p + ALIGN64(le64toh(o->object.size));
801         }
802
803         if (n_objects != le64toh(f->header->n_objects)) {
804                 log_error("Object number mismatch");
805                 r = -EBADMSG;
806                 goto fail;
807         }
808
809         if (n_entries != le64toh(f->header->n_entries)) {
810                 log_error("Entry number mismatch");
811                 r = -EBADMSG;
812                 goto fail;
813         }
814
815         if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
816             n_data != le64toh(f->header->n_data)) {
817                 log_error("Data number mismatch");
818                 r = -EBADMSG;
819                 goto fail;
820         }
821
822         if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
823             n_fields != le64toh(f->header->n_fields)) {
824                 log_error("Field number mismatch");
825                 r = -EBADMSG;
826                 goto fail;
827         }
828
829         if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
830             tag_seqnum != le64toh(f->header->n_tags)) {
831                 log_error("Tag number mismatch");
832                 r = -EBADMSG;
833                 goto fail;
834         }
835
836         if (n_data_hash_tables != 1) {
837                 log_error("Missing data hash table");
838                 r = -EBADMSG;
839                 goto fail;
840         }
841
842         if (n_field_hash_tables != 1) {
843                 log_error("Missing field hash table");
844                 r = -EBADMSG;
845                 goto fail;
846         }
847
848         if (!found_main_entry_array) {
849                 log_error("Missing entry array");
850                 r = -EBADMSG;
851                 goto fail;
852         }
853
854         if (entry_seqnum_set &&
855             entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
856                 log_error("Invalid tail seqnum");
857                 r = -EBADMSG;
858                 goto fail;
859         }
860
861         if (entry_monotonic_set &&
862             (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
863              entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
864                 log_error("Invalid tail monotonic timestamp");
865                 r = -EBADMSG;
866                 goto fail;
867         }
868
869         if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
870                 log_error("Invalid tail realtime timestamp");
871                 r = -EBADMSG;
872                 goto fail;
873         }
874
875         /* Second iteration: we follow all objects referenced from the
876          * two entry points: the object hash table and the entry
877          * array. We also check that everything referenced (directly
878          * or indirectly) in the data hash table also exists in the
879          * entry array, and vice versa. Note that we do not care for
880          * unreferenced objects. We only care that everything that is
881          * referenced is consistent. */
882
883         r = verify_entry_array(f,
884                                data_fd, n_data,
885                                entry_fd, n_entries,
886                                entry_array_fd, n_entry_arrays,
887                                &last_usec);
888         if (r < 0)
889                 goto fail;
890
891         r = verify_hash_table(f,
892                               data_fd, n_data,
893                               entry_fd, n_entries,
894                               entry_array_fd, n_entry_arrays,
895                               &last_usec);
896         if (r < 0)
897                 goto fail;
898
899         flush_progress();
900
901         mmap_cache_close_fd(f->mmap, data_fd);
902         mmap_cache_close_fd(f->mmap, entry_fd);
903         mmap_cache_close_fd(f->mmap, entry_array_fd);
904
905         close_nointr_nofail(data_fd);
906         close_nointr_nofail(entry_fd);
907         close_nointr_nofail(entry_array_fd);
908
909         return 0;
910
911 fail:
912         flush_progress();
913
914         log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
915                   f->path,
916                   (unsigned long long) p,
917                   (unsigned long long) f->last_stat.st_size,
918                   (unsigned long long) (100 * p / f->last_stat.st_size));
919
920         if (data_fd >= 0) {
921                 mmap_cache_close_fd(f->mmap, data_fd);
922                 close_nointr_nofail(data_fd);
923         }
924
925         if (entry_fd >= 0) {
926                 mmap_cache_close_fd(f->mmap, entry_fd);
927                 close_nointr_nofail(entry_fd);
928         }
929
930         if (entry_array_fd >= 0) {
931                 mmap_cache_close_fd(f->mmap, entry_array_fd);
932                 close_nointr_nofail(entry_array_fd);
933         }
934
935         return r;
936 }