chiark / gitweb /
journal: fix unitialized var
[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                 goto fail;
614         }
615         unlink(data_path);
616
617         entry_fd = mkostemp(entry_path, O_CLOEXEC);
618         if (entry_fd < 0) {
619                 log_error("Failed to create entry file: %m");
620                 goto fail;
621         }
622         unlink(entry_path);
623
624         entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC);
625         if (entry_array_fd < 0) {
626                 log_error("Failed to create entry array file: %m");
627                 goto fail;
628         }
629         unlink(entry_array_path);
630
631         /* First iteration: we go through all objects, verify the
632          * superficial structure, headers, hashes. */
633
634         r = journal_file_hmac_put_header(f);
635         if (r < 0) {
636                 log_error("Failed to calculate HMAC of header.");
637                 goto fail;
638         }
639
640         p = le64toh(f->header->header_size);
641         while (p != 0) {
642                 draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
643
644                 r = journal_file_move_to_object(f, -1, p, &o);
645                 if (r < 0) {
646                         log_error("Invalid object at %llu", (unsigned long long) p);
647                         goto fail;
648                 }
649
650                 if (le64toh(f->header->tail_object_offset) < p) {
651                         log_error("Invalid tail object pointer.");
652                         r = -EBADMSG;
653                         goto fail;
654                 }
655
656                 n_objects ++;
657
658                 r = journal_file_object_verify(f, o);
659                 if (r < 0) {
660                         log_error("Invalid object contents at %llu", (unsigned long long) p);
661                         goto fail;
662                 }
663
664                 if (o->object.flags & OBJECT_COMPRESSED &&
665                     !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) {
666                         log_error("Compressed object without compression at %llu", (unsigned long long) p);
667                         r = -EBADMSG;
668                         goto fail;
669                 }
670
671                 r = journal_file_hmac_put_object(f, -1, p);
672                 if (r < 0) {
673                         log_error("Failed to calculate HMAC at %llu", (unsigned long long) p);
674                         goto fail;
675                 }
676
677                 if (o->object.type == OBJECT_TAG) {
678
679                         if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) {
680                                 log_error("Tag object without authentication at %llu", (unsigned long long) p);
681                                 r = -EBADMSG;
682                                 goto fail;
683                         }
684
685                         if (le64toh(o->tag.seqnum) != tag_seqnum) {
686                                 log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p);
687                                 r = -EBADMSG;
688                                 goto fail;
689                         }
690
691                 } else if (o->object.type == OBJECT_ENTRY) {
692
693                         r = write_uint64(entry_fd, p);
694                         if (r < 0)
695                                 goto fail;
696
697                         if (!entry_seqnum_set &&
698                             le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
699                                 log_error("Head entry sequence number incorrect");
700                                 r = -EBADMSG;
701                                 goto fail;
702                         }
703
704                         if (entry_seqnum_set &&
705                             entry_seqnum >= le64toh(o->entry.seqnum)) {
706                                 log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p);
707                                 r = -EBADMSG;
708                                 goto fail;
709                         }
710
711                         entry_seqnum = le64toh(o->entry.seqnum);
712                         entry_seqnum_set = true;
713
714                         if (entry_monotonic_set &&
715                             sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
716                             entry_monotonic > le64toh(o->entry.monotonic)) {
717                                 log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p);
718                                 r = -EBADMSG;
719                                 goto fail;
720                         }
721
722                         entry_monotonic = le64toh(o->entry.monotonic);
723                         entry_boot_id = o->entry.boot_id;
724                         entry_monotonic_set = true;
725
726                         if (!entry_realtime_set &&
727                             le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
728                                 log_error("Head entry realtime timestamp incorrect");
729                                 r = -EBADMSG;
730                                 goto fail;
731                         }
732
733                         entry_realtime = le64toh(o->entry.realtime);
734                         entry_realtime_set = true;
735
736                         n_entries ++;
737                 } else if (o->object.type == OBJECT_ENTRY_ARRAY) {
738
739                         r = write_uint64(entry_array_fd, p);
740                         if (r < 0)
741                                 goto fail;
742
743                         if (p == le64toh(f->header->entry_array_offset)) {
744                                 if (found_main_entry_array) {
745                                         log_error("More than one main entry array at %llu", (unsigned long long) p);
746                                         r = -EBADMSG;
747                                         goto fail;
748                                 }
749
750                                 found_main_entry_array = true;
751                         }
752
753                         n_entry_arrays++;
754
755                 } else if (o->object.type == OBJECT_DATA) {
756
757                         r = write_uint64(data_fd, p);
758                         if (r < 0)
759                                 goto fail;
760
761                         n_data++;
762
763                 } else if (o->object.type == OBJECT_FIELD)
764                         n_fields++;
765                 else if (o->object.type == OBJECT_DATA_HASH_TABLE) {
766                         n_data_hash_tables++;
767
768                         if (n_data_hash_tables > 1) {
769                                 log_error("More than one data hash table at %llu", (unsigned long long) p);
770                                 r = -EBADMSG;
771                                 goto fail;
772                         }
773
774                         if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
775                             le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
776                                 log_error("Header fields for data hash table invalid.");
777                                 r = -EBADMSG;
778                                 goto fail;
779                         }
780                 } else if (o->object.type == OBJECT_FIELD_HASH_TABLE) {
781                         n_field_hash_tables++;
782
783                         if (n_field_hash_tables > 1) {
784                                 log_error("More than one field hash table at %llu", (unsigned long long) p);
785                                 r = -EBADMSG;
786                                 goto fail;
787                         }
788
789                         if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
790                             le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
791                                 log_error("Header fields for field hash table invalid.");
792                                 r = -EBADMSG;
793                                 goto fail;
794                         }
795                 } else if (o->object.type >= _OBJECT_TYPE_MAX)
796                         n_weird ++;
797
798                 if (p == le64toh(f->header->tail_object_offset))
799                         p = 0;
800                 else
801                         p = p + ALIGN64(le64toh(o->object.size));
802         }
803
804         if (n_objects != le64toh(f->header->n_objects)) {
805                 log_error("Object number mismatch");
806                 r = -EBADMSG;
807                 goto fail;
808         }
809
810         if (n_entries != le64toh(f->header->n_entries)) {
811                 log_error("Entry number mismatch");
812                 r = -EBADMSG;
813                 goto fail;
814         }
815
816         if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
817             n_data != le64toh(f->header->n_data)) {
818                 log_error("Data number mismatch");
819                 r = -EBADMSG;
820                 goto fail;
821         }
822
823         if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
824             n_fields != le64toh(f->header->n_fields)) {
825                 log_error("Field number mismatch");
826                 r = -EBADMSG;
827                 goto fail;
828         }
829
830         if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
831             tag_seqnum != le64toh(f->header->n_tags)) {
832                 log_error("Tag number mismatch");
833                 r = -EBADMSG;
834                 goto fail;
835         }
836
837         if (n_data_hash_tables != 1) {
838                 log_error("Missing data hash table");
839                 r = -EBADMSG;
840                 goto fail;
841         }
842
843         if (n_field_hash_tables != 1) {
844                 log_error("Missing field hash table");
845                 r = -EBADMSG;
846                 goto fail;
847         }
848
849         if (!found_main_entry_array) {
850                 log_error("Missing entry array");
851                 r = -EBADMSG;
852                 goto fail;
853         }
854
855         if (entry_seqnum_set &&
856             entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
857                 log_error("Invalid tail seqnum");
858                 r = -EBADMSG;
859                 goto fail;
860         }
861
862         if (entry_monotonic_set &&
863             (!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
864              entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
865                 log_error("Invalid tail monotonic timestamp");
866                 r = -EBADMSG;
867                 goto fail;
868         }
869
870         if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
871                 log_error("Invalid tail realtime timestamp");
872                 r = -EBADMSG;
873                 goto fail;
874         }
875
876         /* Second iteration: we follow all objects referenced from the
877          * two entry points: the object hash table and the entry
878          * array. We also check that everything referenced (directly
879          * or indirectly) in the data hash table also exists in the
880          * entry array, and vice versa. Note that we do not care for
881          * unreferenced objects. We only care that everything that is
882          * referenced is consistent. */
883
884         r = verify_entry_array(f,
885                                data_fd, n_data,
886                                entry_fd, n_entries,
887                                entry_array_fd, n_entry_arrays,
888                                &last_usec);
889         if (r < 0)
890                 goto fail;
891
892         r = verify_hash_table(f,
893                               data_fd, n_data,
894                               entry_fd, n_entries,
895                               entry_array_fd, n_entry_arrays,
896                               &last_usec);
897         if (r < 0)
898                 goto fail;
899
900         flush_progress();
901
902         mmap_cache_close_fd(f->mmap, data_fd);
903         mmap_cache_close_fd(f->mmap, entry_fd);
904         mmap_cache_close_fd(f->mmap, entry_array_fd);
905
906         close_nointr_nofail(data_fd);
907         close_nointr_nofail(entry_fd);
908         close_nointr_nofail(entry_array_fd);
909
910         return 0;
911
912 fail:
913         flush_progress();
914
915         log_error("File corruption detected at %s:%llu (of %llu, %llu%%).",
916                   f->path,
917                   (unsigned long long) p,
918                   (unsigned long long) f->last_stat.st_size,
919                   (unsigned long long) (100 * p / f->last_stat.st_size));
920
921         if (data_fd >= 0) {
922                 mmap_cache_close_fd(f->mmap, data_fd);
923                 close_nointr_nofail(data_fd);
924         }
925
926         if (entry_fd >= 0) {
927                 mmap_cache_close_fd(f->mmap, entry_fd);
928                 close_nointr_nofail(entry_fd);
929         }
930
931         if (entry_array_fd >= 0) {
932                 mmap_cache_close_fd(f->mmap, entry_array_fd);
933                 close_nointr_nofail(entry_array_fd);
934         }
935
936         return r;
937 }