chiark / gitweb /
journal: verify structural consistency
[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  * - verification should use MAP_PRIVATE
42  *
43  * */
44
45 static int journal_file_object_verify(JournalFile *f, Object *o) {
46         assert(f);
47         assert(o);
48
49         /* This does various superficial tests about the length an
50          * possible field values. It does not follow any references to
51          * other objects. */
52
53         if ((o->object.flags & OBJECT_COMPRESSED) &&
54             o->object.type != OBJECT_DATA)
55                 return -EBADMSG;
56
57         switch (o->object.type) {
58
59         case OBJECT_DATA: {
60                 uint64_t h1, h2;
61
62                 if (le64toh(o->data.entry_offset) <= 0 ||
63                     le64toh(o->data.n_entries) <= 0)
64                         return -EBADMSG;
65
66                 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
67                         return -EBADMSG;
68
69                 h1 = le64toh(o->data.hash);
70
71                 if (o->object.flags & OBJECT_COMPRESSED) {
72                         void *b = NULL;
73                         uint64_t alloc = 0, b_size;
74
75                         if (!uncompress_blob(o->data.payload,
76                                              le64toh(o->object.size) - offsetof(Object, data.payload),
77                                              &b, &alloc, &b_size))
78                                 return -EBADMSG;
79
80                         h2 = hash64(b, b_size);
81                         free(b);
82                 } else
83                         h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
84
85                 if (h1 != h2)
86                         return -EBADMSG;
87
88                 break;
89         }
90
91         case OBJECT_FIELD:
92                 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
93                         return -EBADMSG;
94                 break;
95
96         case OBJECT_ENTRY:
97                 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
98                         return -EBADMSG;
99
100                 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
101                         return -EBADMSG;
102
103                 if (le64toh(o->entry.seqnum) <= 0 ||
104                     le64toh(o->entry.realtime) <= 0)
105                         return -EBADMSG;
106
107                 break;
108
109         case OBJECT_DATA_HASH_TABLE:
110         case OBJECT_FIELD_HASH_TABLE:
111                 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0)
112                         return -EBADMSG;
113
114                 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
115                         return -EBADMSG;
116
117                 break;
118
119         case OBJECT_ENTRY_ARRAY:
120                 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0)
121                         return -EBADMSG;
122
123                 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
124                         return -EBADMSG;
125
126                 break;
127
128         case OBJECT_TAG:
129                 if (le64toh(o->object.size) != sizeof(TagObject))
130                         return -EBADMSG;
131                 break;
132         }
133
134         return 0;
135 }
136
137 static void draw_progress(uint64_t p, usec_t *last_usec) {
138         unsigned n, i, j, k;
139         usec_t z, x;
140
141         if (!isatty(STDOUT_FILENO))
142                 return;
143
144         z = now(CLOCK_MONOTONIC);
145         x = *last_usec;
146
147         if (x != 0 && x + 40 * USEC_PER_MSEC > z)
148                 return;
149
150         *last_usec = z;
151
152         n = (3 * columns()) / 4;
153         j = (n * (unsigned) p) / 65535ULL;
154         k = n - j;
155
156         fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
157
158         for (i = 0; i < j; i++)
159                 fputs("\xe2\x96\x88", stdout);
160
161         fputs(ANSI_HIGHLIGHT_OFF, stdout);
162
163         for (i = 0; i < k; i++)
164                 fputs("\xe2\x96\x91", stdout);
165
166         printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU);
167
168         fputs("\r\x1B[?25h", stdout);
169         fflush(stdout);
170 }
171
172 static void flush_progress(void) {
173         unsigned n, i;
174
175         if (!isatty(STDOUT_FILENO))
176                 return;
177
178         n = (3 * columns()) / 4;
179
180         putchar('\r');
181
182         for (i = 0; i < n + 5; i++)
183                 putchar(' ');
184
185         putchar('\r');
186         fflush(stdout);
187 }
188
189 static int write_uint64(int fd, uint64_t p) {
190         ssize_t k;
191
192         k = write(fd, &p, sizeof(p));
193         if (k < 0)
194                 return -errno;
195         if (k != sizeof(p))
196                 return -EIO;
197
198         return 0;
199 }
200
201 static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
202         uint64_t a, b;
203         int r;
204
205         assert(m);
206         assert(fd >= 0);
207
208         /* Bisection ... */
209
210         a = 0; b = n;
211         while (a < b) {
212                 uint64_t c, *z;
213
214                 c = (a + b) / 2;
215
216                 r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z);
217                 if (r < 0)
218                         return r;
219
220                 if (*z == p)
221                         return 1;
222
223                 if (p < *z)
224                         b = c;
225                 else
226                         a = c;
227         }
228
229         return 0;
230 }
231
232 static int entry_points_to_data(
233                 JournalFile *f,
234                 int entry_fd,
235                 uint64_t n_entries,
236                 uint64_t entry_p,
237                 uint64_t data_p) {
238
239         int r;
240         uint64_t i, n, a;
241         Object *o;
242         bool found = false;
243
244         assert(f);
245         assert(entry_fd >= 0);
246
247         if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
248                 log_error("Data object references invalid entry at %llu", (unsigned long long) data_p);
249                 return -EBADMSG;
250         }
251
252         r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
253         if (r < 0)
254                 return r;
255
256         n = journal_file_entry_n_items(o);
257         for (i = 0; i < n; i++)
258                 if (le64toh(o->entry.items[i].object_offset) == data_p) {
259                         found = true;
260                         break;
261                 }
262
263         if (!found) {
264                 log_error("Data object not referenced by linked entry at %llu", (unsigned long long) data_p);
265                 return -EBADMSG;
266         }
267
268         /* Check if this entry is also in main entry array. Since the
269          * main entry array has already been verified we can rely on
270          * its consistency.*/
271
272         n = le64toh(f->header->n_entries);
273         a = le64toh(f->header->entry_array_offset);
274         i = 0;
275
276         while (i < n) {
277                 uint64_t m, j;
278
279                 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
280                 if (r < 0)
281                         return r;
282
283                 m = journal_file_entry_array_n_items(o);
284                 for (j = 0; i < n && j < m; i++, j++)
285                         if (le64toh(o->entry_array.items[j]) == entry_p)
286                                 return 0;
287
288                 a = le64toh(o->entry_array.next_entry_array_offset);;
289         }
290
291         return 0;
292 }
293
294 static int verify_data(
295                 JournalFile *f,
296                 Object *o, uint64_t p,
297                 int entry_fd, uint64_t n_entries,
298                 int entry_array_fd, uint64_t n_entry_arrays) {
299
300         uint64_t i, n, a, last, q;
301         int r;
302
303         assert(f);
304         assert(o);
305         assert(entry_fd >= 0);
306         assert(entry_array_fd >= 0);
307
308         n = le64toh(o->data.n_entries);
309         a = le64toh(o->data.entry_array_offset);
310
311         /* We already checked this earlier */
312         assert(n > 0);
313
314         last = q = le64toh(o->data.entry_offset);
315         r = entry_points_to_data(f, entry_fd, n_entries, q, p);
316         if (r < 0)
317                 return r;
318
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 }